Browse Source

Make FoldingManager independent of TextView.

This allows using a single FoldingManager in multiple TextViews.
pull/15/head
Daniel Grunwald 15 years ago
parent
commit
7b011cf067
  1. 56
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Folding/FoldingElementGenerator.cs
  2. 75
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Folding/FoldingManager.cs
  3. 36
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Folding/FoldingSection.cs
  4. 6
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Rendering/CollapsedLineSection.cs
  5. 6
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Rendering/TextView.cs

56
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Folding/FoldingElementGenerator.cs

@ -2,11 +2,11 @@
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) // This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System; using System;
using System.Collections.Generic;
using System.Windows; using System.Windows;
using System.Windows.Input; using System.Windows.Input;
using System.Windows.Media; using System.Windows.Media;
using System.Windows.Media.TextFormatting; using System.Windows.Media.TextFormatting;
using ICSharpCode.AvalonEdit.Rendering; using ICSharpCode.AvalonEdit.Rendering;
using ICSharpCode.AvalonEdit.Utils; using ICSharpCode.AvalonEdit.Utils;
@ -15,21 +15,57 @@ namespace ICSharpCode.AvalonEdit.Folding
/// <summary> /// <summary>
/// A <see cref="VisualLineElementGenerator"/> that produces line elements for folded <see cref="FoldingSection"/>s. /// A <see cref="VisualLineElementGenerator"/> that produces line elements for folded <see cref="FoldingSection"/>s.
/// </summary> /// </summary>
public class FoldingElementGenerator : VisualLineElementGenerator public sealed class FoldingElementGenerator : VisualLineElementGenerator, ITextViewConnect
{ {
readonly List<TextView> textViews = new List<TextView>();
FoldingManager foldingManager;
#region FoldingManager property / connecting with TextView
/// <summary> /// <summary>
/// Gets/Sets the folding manager from which the foldings should be shown. /// Gets/Sets the folding manager from which the foldings should be shown.
/// </summary> /// </summary>
public FoldingManager FoldingManager { get; set; } public FoldingManager FoldingManager {
get {
return foldingManager;
}
set {
if (foldingManager != value) {
if (foldingManager != null) {
foreach (TextView v in textViews)
foldingManager.RemoveFromTextView(v);
}
foldingManager = value;
if (foldingManager != null) {
foreach (TextView v in textViews)
foldingManager.AddToTextView(v);
}
}
}
}
void ITextViewConnect.AddToTextView(TextView textView)
{
textViews.Add(textView);
if (foldingManager != null)
foldingManager.AddToTextView(textView);
}
void ITextViewConnect.RemoveFromTextView(TextView textView)
{
textViews.Remove(textView);
if (foldingManager != null)
foldingManager.RemoveFromTextView(textView);
}
#endregion
/// <inheritdoc/> /// <inheritdoc/>
public override void StartGeneration(ITextRunConstructionContext context) public override void StartGeneration(ITextRunConstructionContext context)
{ {
base.StartGeneration(context); base.StartGeneration(context);
if (FoldingManager != null) { if (foldingManager != null) {
if (context.TextView != FoldingManager.textView) if (!foldingManager.textViews.Contains(context.TextView))
throw new ArgumentException("Invalid TextView"); throw new ArgumentException("Invalid TextView");
if (context.Document != FoldingManager.document) if (context.Document != foldingManager.document)
throw new ArgumentException("Invalid document"); throw new ArgumentException("Invalid document");
} }
} }
@ -37,8 +73,8 @@ namespace ICSharpCode.AvalonEdit.Folding
/// <inheritdoc/> /// <inheritdoc/>
public override int GetFirstInterestedOffset(int startOffset) public override int GetFirstInterestedOffset(int startOffset)
{ {
if (FoldingManager != null) if (foldingManager != null)
return FoldingManager.GetNextFoldedFoldingStart(startOffset); return foldingManager.GetNextFoldedFoldingStart(startOffset);
else else
return -1; return -1;
} }
@ -46,11 +82,11 @@ namespace ICSharpCode.AvalonEdit.Folding
/// <inheritdoc/> /// <inheritdoc/>
public override VisualLineElement ConstructElement(int offset) public override VisualLineElement ConstructElement(int offset)
{ {
if (FoldingManager == null) if (foldingManager == null)
return null; return null;
int foldedUntil = -1; int foldedUntil = -1;
FoldingSection foldingSection = null; FoldingSection foldingSection = null;
foreach (FoldingSection fs in FoldingManager.GetFoldingsAt(offset)) { foreach (FoldingSection fs in foldingManager.GetFoldingsAt(offset)) {
if (fs.IsFolded) { if (fs.IsFolded) {
if (fs.EndOffset > foldedUntil) { if (fs.EndOffset > foldedUntil) {
foldedUntil = fs.EndOffset; foldedUntil = fs.EndOffset;

75
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Folding/FoldingManager.cs

@ -20,27 +20,33 @@ namespace ICSharpCode.AvalonEdit.Folding
/// </summary> /// </summary>
public class FoldingManager : IWeakEventListener public class FoldingManager : IWeakEventListener
{ {
internal readonly TextView textView;
internal readonly TextDocument document; internal readonly TextDocument document;
internal readonly List<TextView> textViews = new List<TextView>();
readonly TextSegmentCollection<FoldingSection> foldings; readonly TextSegmentCollection<FoldingSection> foldings;
#region Constructor #region Constructor
/// <summary> /// <summary>
/// Creates a new FoldingManager instance. /// Creates a new FoldingManager instance.
/// </summary> /// </summary>
public FoldingManager(TextView textView, TextDocument document) public FoldingManager(TextDocument document)
{ {
if (textView == null)
throw new ArgumentNullException("textView");
if (document == null) if (document == null)
throw new ArgumentNullException("document"); throw new ArgumentNullException("document");
this.textView = textView;
this.document = document; this.document = document;
this.foldings = new TextSegmentCollection<FoldingSection>(); this.foldings = new TextSegmentCollection<FoldingSection>();
document.VerifyAccess(); document.VerifyAccess();
TextDocumentWeakEventManager.Changed.AddListener(document, this); TextDocumentWeakEventManager.Changed.AddListener(document, this);
} }
/// <summary>
/// Creates a new FoldingManager instance.
/// </summary>
[Obsolete("Use the (TextDocument) constructor instead.")]
public FoldingManager(TextView textView, TextDocument document)
: this(document)
{
}
#endregion #endregion
#region ReceiveWeakEvent #region ReceiveWeakEvent
@ -73,6 +79,57 @@ namespace ICSharpCode.AvalonEdit.Folding
} }
#endregion #endregion
#region Manage TextViews
internal void AddToTextView(TextView textView)
{
if (textView == null || textViews.Contains(textView))
throw new ArgumentException();
textViews.Add(textView);
foreach (FoldingSection fs in foldings) {
if (fs.collapsedSections != null) {
Array.Resize(ref fs.collapsedSections, textViews.Count);
fs.collapsedSections[fs.collapsedSections.Length - 1] = fs.CollapseSection(textView);
}
}
}
internal void RemoveFromTextView(TextView textView)
{
int pos = textViews.IndexOf(textView);
if (pos < 0)
throw new ArgumentException();
foreach (FoldingSection fs in foldings) {
if (fs.collapsedSections != null) {
CollapsedLineSection[] c = new CollapsedLineSection[textViews.Count];
Array.Copy(fs.collapsedSections, 0, c, 0, pos);
Array.Copy(fs.collapsedSections, pos + 1, c, pos, c.Length - pos);
fs.collapsedSections = c;
}
}
}
internal CollapsedLineSection[] CollapseLines(DocumentLine start, DocumentLine end)
{
CollapsedLineSection[] c = new CollapsedLineSection[textViews.Count];
for (int i = 0; i < c.Length; i++) {
c[i] = textViews[i].CollapseLines(start, end);
}
return c;
}
internal void Redraw()
{
foreach (TextView textView in textViews)
textView.Redraw();
}
internal void Redraw(FoldingSection fs)
{
foreach (TextView textView in textViews)
textView.Redraw(fs);
}
#endregion
#region Create / Remove / Clear #region Create / Remove / Clear
/// <summary> /// <summary>
/// Creates a folding for the specified text section. /// Creates a folding for the specified text section.
@ -83,7 +140,7 @@ namespace ICSharpCode.AvalonEdit.Folding
throw new ArgumentException("startOffset must be less than endOffset"); throw new ArgumentException("startOffset must be less than endOffset");
FoldingSection fs = new FoldingSection(this, startOffset, endOffset); FoldingSection fs = new FoldingSection(this, startOffset, endOffset);
foldings.Add(fs); foldings.Add(fs);
textView.Redraw(fs, DispatcherPriority.Normal); Redraw(fs);
return fs; return fs;
} }
@ -96,7 +153,7 @@ namespace ICSharpCode.AvalonEdit.Folding
throw new ArgumentNullException("fs"); throw new ArgumentNullException("fs");
fs.IsFolded = false; fs.IsFolded = false;
foldings.Remove(fs); foldings.Remove(fs);
textView.Redraw(fs, DispatcherPriority.Normal); Redraw(fs);
} }
/// <summary> /// <summary>
@ -108,7 +165,7 @@ namespace ICSharpCode.AvalonEdit.Folding
foreach (FoldingSection s in foldings) foreach (FoldingSection s in foldings)
s.IsFolded = false; s.IsFolded = false;
foldings.Clear(); foldings.Clear();
textView.Redraw(); Redraw();
} }
#endregion #endregion
@ -264,7 +321,7 @@ namespace ICSharpCode.AvalonEdit.Folding
FoldingMargin margin; FoldingMargin margin;
FoldingElementGenerator generator; FoldingElementGenerator generator;
public FoldingManagerInstallation(TextArea textArea) : base(textArea.TextView, textArea.Document) public FoldingManagerInstallation(TextArea textArea) : base(textArea.Document)
{ {
this.textArea = textArea; this.textArea = textArea;
margin = new FoldingMargin() { FoldingManager = this }; margin = new FoldingMargin() { FoldingManager = this };

36
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Folding/FoldingSection.cs

@ -13,9 +13,9 @@ namespace ICSharpCode.AvalonEdit.Folding
/// </summary> /// </summary>
public sealed class FoldingSection : TextSegment public sealed class FoldingSection : TextSegment
{ {
FoldingManager manager; readonly FoldingManager manager;
bool isFolded; bool isFolded;
CollapsedLineSection collapsedSection; internal CollapsedLineSection[] collapsedSections;
string title; string title;
/// <summary> /// <summary>
@ -32,18 +32,29 @@ namespace ICSharpCode.AvalonEdit.Folding
DocumentLine endLine = manager.document.GetLineByOffset(EndOffset); DocumentLine endLine = manager.document.GetLineByOffset(EndOffset);
if (startLine != endLine) { if (startLine != endLine) {
DocumentLine startLinePlusOne = startLine.NextLine; DocumentLine startLinePlusOne = startLine.NextLine;
collapsedSection = manager.textView.CollapseLines(startLinePlusOne, endLine); collapsedSections = manager.CollapseLines(startLinePlusOne, endLine);
} }
} }
} else { } else {
RemoveCollapsedLineSection(); RemoveCollapsedLineSection();
} }
if (manager != null) if (manager != null)
manager.textView.Redraw(this, DispatcherPriority.Normal); manager.Redraw(this);
} }
} }
} }
internal CollapsedLineSection CollapseSection(TextView textView)
{
DocumentLine startLine = manager.document.GetLineByOffset(StartOffset);
DocumentLine endLine = manager.document.GetLineByOffset(EndOffset);
if (startLine != endLine) {
DocumentLine startLinePlusOne = startLine.NextLine;
return textView.CollapseLines(startLinePlusOne, endLine);
}
return null;
}
/// <summary> /// <summary>
/// Gets/Sets the text used to display the collapsed version of the folding section. /// Gets/Sets the text used to display the collapsed version of the folding section.
/// </summary> /// </summary>
@ -55,7 +66,7 @@ namespace ICSharpCode.AvalonEdit.Folding
if (title != value) { if (title != value) {
title = value; title = value;
if (this.IsFolded && manager != null) if (this.IsFolded && manager != null)
manager.textView.Redraw(this, DispatcherPriority.Normal); manager.Redraw(this);
} }
} }
} }
@ -74,16 +85,13 @@ namespace ICSharpCode.AvalonEdit.Folding
void RemoveCollapsedLineSection() void RemoveCollapsedLineSection()
{ {
if (collapsedSection != null) { if (collapsedSections != null) {
if (collapsedSection.Start != null) foreach (var collapsedSection in collapsedSections) {
collapsedSection.Uncollapse(); if (collapsedSection != null && collapsedSection.Start != null)
collapsedSection = null; collapsedSection.Uncollapse();
}
collapsedSections = null;
} }
} }
internal void Removed()
{
manager = null;
}
} }
} }

6
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Rendering/CollapsedLineSection.cs

@ -30,7 +30,9 @@ namespace ICSharpCode.AvalonEdit.Rendering
this.start = start; this.start = start;
this.end = end; this.end = end;
#if DEBUG #if DEBUG
this.ID = "#" + (nextId++); unchecked {
this.ID = " #" + (nextId++);
}
#endif #endif
} }
@ -110,7 +112,7 @@ namespace ICSharpCode.AvalonEdit.Rendering
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Globalization", "CA1305:SpecifyIFormatProvider", MessageId = "System.Int32.ToString")] [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Globalization", "CA1305:SpecifyIFormatProvider", MessageId = "System.Int32.ToString")]
public override string ToString() public override string ToString()
{ {
return "[CollapsedSection " + ID + " Start=" + (start != null ? start.LineNumber.ToString() : "null") return "[CollapsedSection" + ID + " Start=" + (start != null ? start.LineNumber.ToString() : "null")
+ " End=" + (end != null ? end.LineNumber.ToString() : "null") + " IsCollapsed=" + isCollapsed + "]"; + " End=" + (end != null ? end.LineNumber.ToString() : "null") + " IsCollapsed=" + isCollapsed + "]";
} }
} }

6
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Rendering/TextView.cs

@ -406,7 +406,7 @@ namespace ICSharpCode.AvalonEdit.Rendering
/// <summary> /// <summary>
/// Causes the text editor to regenerate the specified visual line. /// Causes the text editor to regenerate the specified visual line.
/// </summary> /// </summary>
public void Redraw(VisualLine visualLine, DispatcherPriority redrawPriority) public void Redraw(VisualLine visualLine, DispatcherPriority redrawPriority = DispatcherPriority.Normal)
{ {
VerifyAccess(); VerifyAccess();
if (allVisualLines.Remove(visualLine)) { if (allVisualLines.Remove(visualLine)) {
@ -419,7 +419,7 @@ namespace ICSharpCode.AvalonEdit.Rendering
/// <summary> /// <summary>
/// Causes the text editor to redraw all lines overlapping with the specified segment. /// Causes the text editor to redraw all lines overlapping with the specified segment.
/// </summary> /// </summary>
public void Redraw(int offset, int length, DispatcherPriority redrawPriority) public void Redraw(int offset, int length, DispatcherPriority redrawPriority = DispatcherPriority.Normal)
{ {
VerifyAccess(); VerifyAccess();
bool removedLine = false; bool removedLine = false;
@ -464,7 +464,7 @@ namespace ICSharpCode.AvalonEdit.Rendering
/// Causes the text editor to redraw all lines overlapping with the specified segment. /// Causes the text editor to redraw all lines overlapping with the specified segment.
/// Does nothing if segment is null. /// Does nothing if segment is null.
/// </summary> /// </summary>
public void Redraw(ISegment segment, DispatcherPriority redrawPriority) public void Redraw(ISegment segment, DispatcherPriority redrawPriority = DispatcherPriority.Normal)
{ {
if (segment != null) { if (segment != null) {
Redraw(segment.Offset, segment.Length, redrawPriority); Redraw(segment.Offset, segment.Length, redrawPriority);

Loading…
Cancel
Save