Browse Source

Build simple folding support (independent of parsers in SharpDevelop) into AvalonEdit.

Add TextEditorOptions.AllowScrollBelowDocument (enabled in SharpDevelop) to avoid the view from jumping when collapsing code near the end of document.

git-svn-id: svn://svn.sharpdevelop.net/sharpdevelop/trunk@4910 1ccf3a8d-04fe-1044-b7c0-cef0b8235c61
shortcuts
Daniel Grunwald 17 years ago
parent
commit
3d8c70850c
  1. 4
      src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/CodeEditor.cs
  2. 60
      src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/ParserFoldingStrategy.cs
  3. 67
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Folding/BraceFoldingStrategy.cs
  4. 209
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Folding/FoldingManager.cs
  5. 67
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Folding/NewFolding.cs
  6. 5
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/ICSharpCode.AvalonEdit.csproj
  7. 4
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Rendering/TextView.cs
  8. 45
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/TextEditor.cs
  9. 26
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/TextEditorAutomationPeer.cs
  10. 19
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/TextEditorOptions.cs

4
src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/CodeEditor.cs

@ -146,6 +146,9 @@ namespace ICSharpCode.AvalonEdit.AddIn
primaryTextEditorAdapter = (CodeEditorAdapter)primaryTextEditor.TextArea.GetService(typeof(ITextEditor)); primaryTextEditorAdapter = (CodeEditorAdapter)primaryTextEditor.TextArea.GetService(typeof(ITextEditor));
Debug.Assert(primaryTextEditorAdapter != null); Debug.Assert(primaryTextEditorAdapter != null);
// always support scrolling below the end of the document - it's better when folding is enabled
primaryTextEditor.Options.AllowScrollBelowDocument = true;
this.primaryBracketRenderer = new BracketHighlightRenderer(primaryTextEditor.TextArea.TextView); this.primaryBracketRenderer = new BracketHighlightRenderer(primaryTextEditor.TextArea.TextView);
this.Document = primaryTextEditor.Document; this.Document = primaryTextEditor.Document;
@ -239,6 +242,7 @@ namespace ICSharpCode.AvalonEdit.AddIn
secondaryTextEditor.SetBinding(TextEditor.DocumentProperty, secondaryTextEditor.SetBinding(TextEditor.DocumentProperty,
new Binding(TextEditor.DocumentProperty.Name) { Source = primaryTextEditor }); new Binding(TextEditor.DocumentProperty.Name) { Source = primaryTextEditor });
secondaryTextEditor.SyntaxHighlighting = primaryTextEditor.SyntaxHighlighting; secondaryTextEditor.SyntaxHighlighting = primaryTextEditor.SyntaxHighlighting;
secondaryTextEditor.Options = primaryTextEditor.Options;
SetRow(secondaryTextEditor, 2); SetRow(secondaryTextEditor, 2);
this.Children.Add(secondaryTextEditor); this.Children.Add(secondaryTextEditor);

60
src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/ParserFoldingStrategy.cs

@ -23,60 +23,26 @@ namespace ICSharpCode.AvalonEdit.AddIn
{ {
readonly FoldingManager foldingManager; readonly FoldingManager foldingManager;
TextArea textArea; TextArea textArea;
FoldingMargin margin;
FoldingElementGenerator generator;
bool isFirstUpdate = true; bool isFirstUpdate = true;
public ParserFoldingStrategy(TextArea textArea) public ParserFoldingStrategy(TextArea textArea)
{ {
this.textArea = textArea; this.textArea = textArea;
foldingManager = new FoldingManager(textArea.TextView, textArea.Document); foldingManager = FoldingManager.Install(textArea);
foldingManager.ExpandFoldingsWhenCaretIsMovedIntoThem(textArea.Caret);
margin = new FoldingMargin() { FoldingManager = foldingManager };
generator = new FoldingElementGenerator() { FoldingManager = foldingManager };
textArea.LeftMargins.Add(margin);
textArea.TextView.ElementGenerators.Add(generator);
} }
public void Dispose() public void Dispose()
{ {
if (textArea != null) { if (textArea != null) {
textArea.LeftMargins.Remove(margin); FoldingManager.Uninstall(foldingManager);
textArea.TextView.ElementGenerators.Remove(generator);
foldingManager.Clear();
textArea = null; textArea = null;
} }
} }
public void UpdateFoldings(ParseInformation parseInfo) public void UpdateFoldings(ParseInformation parseInfo)
{ {
var oldFoldings = foldingManager.AllFoldings.ToArray();
IEnumerable<NewFolding> newFoldings = GetNewFoldings(parseInfo); IEnumerable<NewFolding> newFoldings = GetNewFoldings(parseInfo);
int oldFoldingIndex = 0; foldingManager.UpdateFoldings(newFoldings, -1);
// merge new foldings into old foldings so that sections keep being collapsed
// both oldFoldings and newFoldings are sorted by start offset
foreach (NewFolding newFolding in newFoldings) {
// remove old foldings that were skipped
while (oldFoldingIndex < oldFoldings.Length && newFolding.StartOffset > oldFoldings[oldFoldingIndex].StartOffset) {
foldingManager.RemoveFolding(oldFoldings[oldFoldingIndex++]);
}
FoldingSection section;
// reuse current folding if its matching:
if (oldFoldingIndex < oldFoldings.Length && newFolding.StartOffset == oldFoldings[oldFoldingIndex].StartOffset) {
section = oldFoldings[oldFoldingIndex++];
section.Length = newFolding.EndOffset - newFolding.StartOffset;
} else {
// no matching current folding; create a new one:
section = foldingManager.CreateFolding(newFolding.StartOffset, newFolding.EndOffset);
// auto-close #regions only when opening the document
section.IsFolded = isFirstUpdate && newFolding.DefaultClosed;
}
section.Title = newFolding.Name;
}
// remove all outstanding old foldings:
while (oldFoldingIndex < oldFoldings.Length) {
foldingManager.RemoveFolding(oldFoldings[oldFoldingIndex++]);
}
isFirstUpdate = false; isFirstUpdate = false;
} }
@ -91,12 +57,12 @@ namespace ICSharpCode.AvalonEdit.AddIn
foreach (FoldingRegion foldingRegion in parseInfo.CompilationUnit.FoldingRegions) { foreach (FoldingRegion foldingRegion in parseInfo.CompilationUnit.FoldingRegions) {
NewFolding f = new NewFolding(textArea.Document.GetOffset(foldingRegion.Region.BeginLine, foldingRegion.Region.BeginColumn), NewFolding f = new NewFolding(textArea.Document.GetOffset(foldingRegion.Region.BeginLine, foldingRegion.Region.BeginColumn),
textArea.Document.GetOffset(foldingRegion.Region.EndLine, foldingRegion.Region.EndColumn)); textArea.Document.GetOffset(foldingRegion.Region.EndLine, foldingRegion.Region.EndColumn));
f.DefaultClosed = true; f.DefaultClosed = isFirstUpdate;
f.Name = foldingRegion.Name; f.Name = foldingRegion.Name;
newFoldMarkers.Add(f); newFoldMarkers.Add(f);
} }
} }
return newFoldMarkers.Where(f => f.EndOffset > f.StartOffset).OrderBy(f=>f.StartOffset); return newFoldMarkers.OrderBy(f => f.StartOffset);
} }
void AddClassMembers(IClass c, List<NewFolding> newFoldMarkers) void AddClassMembers(IClass c, List<NewFolding> newFoldMarkers)
@ -122,21 +88,5 @@ namespace ICSharpCode.AvalonEdit.AddIn
} }
} }
} }
struct NewFolding
{
public readonly int StartOffset, EndOffset;
public string Name;
public bool DefaultClosed;
public NewFolding(int start, int end)
{
Debug.Assert(start < end);
this.StartOffset = start;
this.EndOffset = end;
this.Name = null;
this.DefaultClosed = false;
}
}
} }
} }

67
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Folding/BraceFoldingStrategy.cs

@ -0,0 +1,67 @@
// <file>
// <copyright see="prj:///doc/copyright.txt"/>
// <license see="prj:///doc/license.txt"/>
// <owner name="Daniel Grunwald"/>
// <version>$Revision$</version>
// </file>
using System;
using ICSharpCode.AvalonEdit.Document;
using System.Collections.Generic;
namespace ICSharpCode.AvalonEdit.Folding
{
/// <summary>
/// Allows producing foldings from a document based on braces.
/// </summary>
public class BraceFoldingStrategy
{
/// <summary>
/// Gets/Sets the opening brace. The default value is '{'.
/// </summary>
public char OpeningBrace { get; set; }
/// <summary>
/// Gets/Sets the closing brace. The default value is '}'.
/// </summary>
public char ClosingBrace { get; set; }
/// <summary>
/// Creates a new BraceFoldingStrategy.
/// </summary>
public BraceFoldingStrategy()
{
this.OpeningBrace = '{';
this.ClosingBrace = '}';
}
/// <summary>
/// Create <see cref="NewFolding"/>s for the specified document.
/// </summary>
public IEnumerable<NewFolding> CreateNewFoldings(ITextSource document)
{
List<NewFolding> newFoldings = new List<NewFolding>();
Stack<int> startOffsets = new Stack<int>();
int lastNewLineOffset = 0;
char openingBrace = this.OpeningBrace;
char closingBrace = this.ClosingBrace;
for (int i = 0; i < document.TextLength; i++) {
char c = document.GetCharAt(i);
if (c == openingBrace) {
startOffsets.Push(i);
} else if (c == closingBrace && startOffsets.Count > 0) {
int startOffset = startOffsets.Pop();
// don't fold if opening and closing brace are on the same line
if (startOffset < lastNewLineOffset) {
newFoldings.Add(new NewFolding(startOffset, i + 1));
}
} else if (c == '\n' || c == '\r') {
lastNewLineOffset = i + 1;
}
}
newFoldings.Sort((a,b) => a.StartOffset.CompareTo(b.StartOffset));
return newFoldings;
}
}
}

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

@ -8,6 +8,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using System.Linq;
using System.Windows; using System.Windows;
using System.Windows.Threading; using System.Windows.Threading;
@ -28,14 +29,7 @@ namespace ICSharpCode.AvalonEdit.Folding
readonly TextSegmentCollection<FoldingSection> foldings; readonly TextSegmentCollection<FoldingSection> foldings;
/// <summary> #region Constructor
/// Creates a new FoldingManager instance.
/// </summary>
public FoldingManager(TextView textView)
: this(textView, ThrowUtil.CheckNotNull(textView, "textView").Document)
{
}
/// <summary> /// <summary>
/// Creates a new FoldingManager instance. /// Creates a new FoldingManager instance.
/// </summary> /// </summary>
@ -49,48 +43,9 @@ namespace ICSharpCode.AvalonEdit.Folding
this.document = document; this.document = document;
this.foldings = new TextSegmentCollection<FoldingSection>(document); this.foldings = new TextSegmentCollection<FoldingSection>(document);
} }
#region ExpandFoldingsWhenCaretIsMovedIntoThem
// keep a reference to the helper as long as the folding manager exists
ExpandFoldingsWhenCaretIsMovedIntoThemHelper helper;
/// <summary>
/// Will listen to Caret.PositionChanged events and automatically expand folding sections
/// when the caret is moved into them.
/// </summary>
public void ExpandFoldingsWhenCaretIsMovedIntoThem(Caret caret)
{
if (helper == null)
helper = new ExpandFoldingsWhenCaretIsMovedIntoThemHelper(this);
CaretWeakEventManager.PositionChanged.AddListener(caret, helper);
}
sealed class ExpandFoldingsWhenCaretIsMovedIntoThemHelper : IWeakEventListener
{
FoldingManager manager;
public ExpandFoldingsWhenCaretIsMovedIntoThemHelper(FoldingManager manager)
{
this.manager = manager;
}
bool IWeakEventListener.ReceiveWeakEvent(Type managerType, object sender, EventArgs e)
{
if (managerType == typeof(CaretWeakEventManager.PositionChanged)) {
Caret caret = (Caret)sender;
int caretOffset = caret.Offset;
foreach (FoldingSection s in manager.GetFoldingsContaining(caretOffset)) {
if (s.IsFolded && s.StartOffset < caretOffset && caretOffset < s.EndOffset) {
s.IsFolded = false;
}
}
return true;
}
return false;
}
}
#endregion #endregion
#region Create / Remove / Clear
/// <summary> /// <summary>
/// Creates a folding for the specified text section. /// Creates a folding for the specified text section.
/// </summary> /// </summary>
@ -100,7 +55,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(); textView.Redraw(fs, DispatcherPriority.Normal);
return fs; return fs;
} }
@ -111,7 +66,6 @@ namespace ICSharpCode.AvalonEdit.Folding
{ {
if (fs == null) if (fs == null)
throw new ArgumentNullException("fs"); throw new ArgumentNullException("fs");
document.VerifyAccess();
fs.IsFolded = false; fs.IsFolded = false;
foldings.Remove(fs); foldings.Remove(fs);
textView.Redraw(fs, DispatcherPriority.Normal); textView.Redraw(fs, DispatcherPriority.Normal);
@ -128,7 +82,9 @@ namespace ICSharpCode.AvalonEdit.Folding
foldings.Clear(); foldings.Clear();
textView.Redraw(); textView.Redraw();
} }
#endregion
#region Get...Folding
/// <summary> /// <summary>
/// Gets all foldings in this manager. /// Gets all foldings in this manager.
/// The foldings are returned sorted by start offset; /// The foldings are returned sorted by start offset;
@ -182,5 +138,158 @@ namespace ICSharpCode.AvalonEdit.Folding
{ {
return foldings.FindSegmentsContaining(offset); return foldings.FindSegmentsContaining(offset);
} }
#endregion
#region UpdateFoldings
/// <summary>
/// Updates the foldings in this <see cref="FoldingManager"/> using the given new foldings.
/// This method will try to detect which new foldings correspond to which existing foldings; and will keep the state
/// (<see cref="FoldingSection.IsFolded"/>) for existing foldings.
/// </summary>
/// <param name="newFoldings">The new set of foldings. These must be sorted by starting offset.</param>
/// <param name="firstErrorOffset">The first position of a parse error. Existing foldings starting after
/// this offset will be kept even if they don't appear in <paramref name="newFoldings"/>.
/// Use -1 for this parameter if there were no parse errors.</param>
public void UpdateFoldings(IEnumerable<NewFolding> newFoldings, int firstErrorOffset)
{
if (newFoldings == null)
throw new ArgumentNullException("newFoldings");
if (firstErrorOffset < 0)
firstErrorOffset = int.MaxValue;
var oldFoldings = this.AllFoldings.ToArray();
int oldFoldingIndex = 0;
int previousStartOffset = 0;
// merge new foldings into old foldings so that sections keep being collapsed
// both oldFoldings and newFoldings are sorted by start offset
foreach (NewFolding newFolding in newFoldings) {
// ensure newFoldings are sorted correctly
if (newFolding.StartOffset < previousStartOffset)
throw new ArgumentException("newFoldings must be sorted by start offset");
previousStartOffset = newFolding.StartOffset;
// remove old foldings that were skipped
while (oldFoldingIndex < oldFoldings.Length && newFolding.StartOffset > oldFoldings[oldFoldingIndex].StartOffset) {
this.RemoveFolding(oldFoldings[oldFoldingIndex++]);
}
FoldingSection section;
// reuse current folding if its matching:
if (oldFoldingIndex < oldFoldings.Length && newFolding.StartOffset == oldFoldings[oldFoldingIndex].StartOffset) {
section = oldFoldings[oldFoldingIndex++];
section.Length = newFolding.EndOffset - newFolding.StartOffset;
} else {
// no matching current folding; create a new one:
section = this.CreateFolding(newFolding.StartOffset, newFolding.EndOffset);
// auto-close #regions only when opening the document
section.IsFolded = newFolding.DefaultClosed;
}
section.Title = newFolding.Name;
}
// remove all outstanding old foldings:
while (oldFoldingIndex < oldFoldings.Length) {
FoldingSection oldSection = oldFoldings[oldFoldingIndex];
if (oldSection.StartOffset >= firstErrorOffset)
break;
this.RemoveFolding(oldSection);
}
}
#endregion
#region Install
/// <summary>
/// Adds Folding support to the specified text area.
/// Warning: The folding manager is only valid for the text area's current document. The folding manager
/// must be uninstalled before the text area is bound to a different document.
/// </summary>
/// <returns>The <see cref="FoldingManager"/> that manages the list of foldings inside the text area.</returns>
public static FoldingManager Install(TextArea textArea)
{
if (textArea == null)
throw new ArgumentNullException("textArea");
return new FoldingManagerInstallation(textArea);
}
/// <summary>
/// Uninstalls the folding manager.
/// </summary>
/// <exception cref="ArgumentException">The specified manager was not created using <see cref="Install"/>.</exception>
public static void Uninstall(FoldingManager manager)
{
if (manager == null)
throw new ArgumentNullException("manager");
FoldingManagerInstallation installation = manager as FoldingManagerInstallation;
if (installation != null) {
installation.Uninstall();
} else {
throw new ArgumentException("FoldingManager was not created using FoldingManager.Install");
}
}
sealed class FoldingManagerInstallation : FoldingManager
{
TextArea textArea;
FoldingMargin margin;
FoldingElementGenerator generator;
public FoldingManagerInstallation(TextArea textArea) : base(textArea.TextView, textArea.Document)
{
this.textArea = textArea;
margin = new FoldingMargin() { FoldingManager = this };
generator = new FoldingElementGenerator() { FoldingManager = this };
textArea.LeftMargins.Add(margin);
textArea.TextView.ElementGenerators.Add(generator);
textArea.Caret.PositionChanged += textArea_Caret_PositionChanged;
}
/*
void DemoMode()
{
foldingGenerator = new FoldingElementGenerator() { FoldingManager = fm };
foldingMargin = new FoldingMargin { FoldingManager = fm };
foldingMarginBorder = new Border {
Child = foldingMargin,
Background = new LinearGradientBrush(Colors.White, Colors.Transparent, 0)
};
foldingMarginBorder.SizeChanged += UpdateTextViewClip;
textEditor.TextArea.TextView.ElementGenerators.Add(foldingGenerator);
textEditor.TextArea.LeftMargins.Add(foldingMarginBorder);
}
void UpdateTextViewClip(object sender, SizeChangedEventArgs e)
{
textEditor.TextArea.TextView.Clip = new RectangleGeometry(
new Rect(-foldingMarginBorder.ActualWidth,
0,
textEditor.TextArea.TextView.ActualWidth + foldingMarginBorder.ActualWidth,
textEditor.TextArea.TextView.ActualHeight));
}
*/
public void Uninstall()
{
Clear();
if (textArea != null) {
textArea.Caret.PositionChanged -= textArea_Caret_PositionChanged;
textArea.LeftMargins.Remove(margin);
textArea.TextView.ElementGenerators.Remove(generator);
margin = null;
generator = null;
textArea = null;
}
}
void textArea_Caret_PositionChanged(object sender, EventArgs e)
{
// Expand Foldings when Caret is moved into them.
int caretOffset = textArea.Caret.Offset;
foreach (FoldingSection s in GetFoldingsContaining(caretOffset)) {
if (s.IsFolded && s.StartOffset < caretOffset && caretOffset < s.EndOffset) {
s.IsFolded = false;
}
}
}
}
#endregion
} }
} }

67
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Folding/NewFolding.cs

@ -0,0 +1,67 @@
// <file>
// <copyright see="prj:///doc/copyright.txt"/>
// <license see="prj:///doc/license.txt"/>
// <author name="Daniel Grunwald"/>
// <version>$Revision$</version>
// </file>
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Windows;
using System.Windows.Threading;
using ICSharpCode.AvalonEdit.Document;
using ICSharpCode.AvalonEdit.Editing;
using ICSharpCode.AvalonEdit.Rendering;
using ICSharpCode.AvalonEdit.Utils;
namespace ICSharpCode.AvalonEdit.Folding
{
/// <summary>
/// Helper class used for <see cref="FoldingManager.UpdateFoldings"/>.
/// </summary>
public sealed class NewFolding : ISegment
{
/// <summary>
/// Gets the start offset.
/// </summary>
public int StartOffset { get; private set; }
/// <summary>
/// Gets the end offset.
/// </summary>
public int EndOffset { get; private set; }
/// <summary>
/// Gets/Sets the name displayed for the folding.
/// </summary>
public string Name { get; set; }
/// <summary>
/// Gets/Sets whether the folding is closed by default.
/// </summary>
public bool DefaultClosed { get; set; }
/// <summary>
/// Creates a new NewFolding instance.
/// </summary>
public NewFolding(int start, int end)
{
if (!(start < end))
throw new ArgumentException("'start' must be less than 'end'");
this.StartOffset = start;
this.EndOffset = end;
this.Name = null;
this.DefaultClosed = false;
}
int ISegment.Offset {
get { return this.StartOffset; }
}
int ISegment.Length {
get { return this.EndOffset - this.StartOffset; }
}
}
}

5
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/ICSharpCode.AvalonEdit.csproj

@ -68,6 +68,9 @@
<Reference Include="UIAutomationProvider"> <Reference Include="UIAutomationProvider">
<RequiredTargetFramework>3.0</RequiredTargetFramework> <RequiredTargetFramework>3.0</RequiredTargetFramework>
</Reference> </Reference>
<Reference Include="UIAutomationTypes">
<RequiredTargetFramework>3.0</RequiredTargetFramework>
</Reference>
<Reference Include="WindowsBase"> <Reference Include="WindowsBase">
<RequiredTargetFramework>3.0</RequiredTargetFramework> <RequiredTargetFramework>3.0</RequiredTargetFramework>
</Reference> </Reference>
@ -135,6 +138,7 @@
<Compile Include="Editing\DottedLineMargin.cs" /> <Compile Include="Editing\DottedLineMargin.cs" />
<Compile Include="Editing\DragDropException.cs" /> <Compile Include="Editing\DragDropException.cs" />
<Compile Include="Editing\EditingCommandHandler.cs" /> <Compile Include="Editing\EditingCommandHandler.cs" />
<Compile Include="Folding\BraceFoldingStrategy.cs" />
<Compile Include="Folding\FoldingElementGenerator.cs" /> <Compile Include="Folding\FoldingElementGenerator.cs" />
<Compile Include="Folding\FoldingManager.cs" /> <Compile Include="Folding\FoldingManager.cs" />
<Compile Include="Folding\FoldingMargin.cs" /> <Compile Include="Folding\FoldingMargin.cs" />
@ -167,6 +171,7 @@
<Compile Include="Editing\TextSegmentReadOnlySectionProvider.cs"> <Compile Include="Editing\TextSegmentReadOnlySectionProvider.cs">
<DependentUpon>IReadOnlySectionProvider.cs</DependentUpon> <DependentUpon>IReadOnlySectionProvider.cs</DependentUpon>
</Compile> </Compile>
<Compile Include="Folding\NewFolding.cs" />
<Compile Include="Highlighting\DocumentHighlighter.cs" /> <Compile Include="Highlighting\DocumentHighlighter.cs" />
<Compile Include="Highlighting\HighlightedInlineBuilder.cs" /> <Compile Include="Highlighting\HighlightedInlineBuilder.cs" />
<Compile Include="Highlighting\HighlightedLine.cs" /> <Compile Include="Highlighting\HighlightedLine.cs" />

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

@ -636,6 +636,10 @@ namespace ICSharpCode.AvalonEdit.Rendering
maxWidth += AdditionalHorizontalScrollAmount; maxWidth += AdditionalHorizontalScrollAmount;
double heightTreeHeight = this.DocumentHeight; double heightTreeHeight = this.DocumentHeight;
TextEditorOptions options = this.Options;
if (options.AllowScrollBelowDocument) {
heightTreeHeight = Math.Max(heightTreeHeight, Math.Min(heightTreeHeight - 50, scrollOffset.Y) + scrollViewport.Height);
}
SetScrollData(availableSize, SetScrollData(availableSize,
new Size(maxWidth, heightTreeHeight), new Size(maxWidth, heightTreeHeight),

45
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/TextEditor.cs

@ -14,13 +14,13 @@ using System.Windows.Controls;
using System.Windows.Data; using System.Windows.Data;
using System.Windows.Input; using System.Windows.Input;
using System.Windows.Markup; using System.Windows.Markup;
using System.Windows.Threading;
using ICSharpCode.AvalonEdit.Document; using ICSharpCode.AvalonEdit.Document;
using ICSharpCode.AvalonEdit.Editing; using ICSharpCode.AvalonEdit.Editing;
using ICSharpCode.AvalonEdit.Highlighting; using ICSharpCode.AvalonEdit.Highlighting;
using ICSharpCode.AvalonEdit.Rendering; using ICSharpCode.AvalonEdit.Rendering;
using ICSharpCode.AvalonEdit.Utils; using ICSharpCode.AvalonEdit.Utils;
using System.Windows.Threading;
namespace ICSharpCode.AvalonEdit namespace ICSharpCode.AvalonEdit
{ {
@ -353,11 +353,18 @@ namespace ICSharpCode.AvalonEdit
static void OnIsReadOnlyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) static void OnIsReadOnlyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{ {
TextEditor editor = (TextEditor)d; TextEditor editor = d as TextEditor;
if ((bool)e.NewValue) if (editor != null) {
editor.TextArea.ReadOnlySectionProvider = ReadOnlyDocument.Instance; if ((bool)e.NewValue)
else editor.TextArea.ReadOnlySectionProvider = ReadOnlyDocument.Instance;
editor.TextArea.ReadOnlySectionProvider = NoReadOnlySections.Instance; else
editor.TextArea.ReadOnlySectionProvider = NoReadOnlySections.Instance;
TextEditorAutomationPeer peer = TextEditorAutomationPeer.FromElement(editor) as TextEditorAutomationPeer;
if (peer != null) {
peer.RaiseIsReadOnlyChanged((bool)e.OldValue, (bool)e.NewValue);
}
}
} }
#endregion #endregion
@ -922,6 +929,15 @@ namespace ICSharpCode.AvalonEdit
return textView.GetPosition(TranslatePoint(point, textView) + textView.ScrollOffset); return textView.GetPosition(TranslatePoint(point, textView) + textView.ScrollOffset);
} }
/// <summary>
/// Scrolls to the specified line.
/// This method requires that the TextEditor was already assigned a size (WPF layout must have run prior).
/// </summary>
public void ScrollToLine(int line)
{
ScrollTo(line, -1);
}
/// <summary> /// <summary>
/// Scrolls to the specified line/column. /// Scrolls to the specified line/column.
/// This method requires that the TextEditor was already assigned a size (WPF layout must have run prior). /// This method requires that the TextEditor was already assigned a size (WPF layout must have run prior).
@ -930,19 +946,22 @@ namespace ICSharpCode.AvalonEdit
{ {
const double MinimumScrollPercentage = 0.3; const double MinimumScrollPercentage = 0.3;
ScrollToLine(line);
if (scrollViewer != null) { if (scrollViewer != null) {
Point p = textArea.TextView.GetVisualPosition(new TextViewPosition(line, column), VisualYPosition.LineMiddle); Point p = textArea.TextView.GetVisualPosition(new TextViewPosition(line, Math.Max(1, column)), VisualYPosition.LineMiddle);
double verticalPos = p.Y - scrollViewer.ViewportHeight / 2; double verticalPos = p.Y - scrollViewer.ViewportHeight / 2;
if (Math.Abs(verticalPos - scrollViewer.VerticalOffset) > MinimumScrollPercentage * scrollViewer.ViewportHeight) { if (Math.Abs(verticalPos - scrollViewer.VerticalOffset) > MinimumScrollPercentage * scrollViewer.ViewportHeight) {
scrollViewer.ScrollToVerticalOffset(Math.Max(0, verticalPos)); scrollViewer.ScrollToVerticalOffset(Math.Max(0, verticalPos));
} }
if (p.X > scrollViewer.ViewportWidth - Caret.MinimumDistanceToViewBorder * 2) { if (column > 0) {
double horizontalPos = Math.Max(0, p.X - scrollViewer.ViewportWidth / 2); if (p.X > scrollViewer.ViewportWidth - Caret.MinimumDistanceToViewBorder * 2) {
if (Math.Abs(horizontalPos - scrollViewer.HorizontalOffset) > MinimumScrollPercentage * scrollViewer.ViewportWidth) { double horizontalPos = Math.Max(0, p.X - scrollViewer.ViewportWidth / 2);
scrollViewer.ScrollToHorizontalOffset(horizontalPos); if (Math.Abs(horizontalPos - scrollViewer.HorizontalOffset) > MinimumScrollPercentage * scrollViewer.ViewportWidth) {
scrollViewer.ScrollToHorizontalOffset(horizontalPos);
}
} else {
scrollViewer.ScrollToHorizontalOffset(0);
} }
} else {
scrollViewer.ScrollToHorizontalOffset(0);
} }
} }
} }

26
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/TextEditorAutomationPeer.cs

@ -1,17 +1,19 @@
/* // <file>
* Created by SharpDevelop. // <copyright see="prj:///doc/copyright.txt"/>
* User: Daniel // <license see="prj:///doc/license.txt"/>
* Date: 04.06.2009 // <author name="Daniel Grunwald"/>
* Time: 20:46 // <version>$Revision$</version>
* // </file>
* To change this template use Tools | Options | Coding | Edit Standard Headers.
*/
using System; using System;
using System.Diagnostics;
using System.Windows.Automation;
using System.Windows.Automation.Peers; using System.Windows.Automation.Peers;
using System.Windows.Automation.Provider; using System.Windows.Automation.Provider;
using System.Windows.Controls; using System.Windows.Controls;
using ICSharpCode.AvalonEdit.Utils;
namespace ICSharpCode.AvalonEdit namespace ICSharpCode.AvalonEdit
{ {
/// <summary> /// <summary>
@ -24,6 +26,7 @@ namespace ICSharpCode.AvalonEdit
/// </summary> /// </summary>
public TextEditorAutomationPeer(TextEditor owner) : base(owner) public TextEditorAutomationPeer(TextEditor owner) : base(owner)
{ {
Debug.WriteLine("TextEditorAutomationPeer was created");
} }
private TextEditor TextEditor { private TextEditor TextEditor {
@ -40,7 +43,7 @@ namespace ICSharpCode.AvalonEdit
} }
bool IValueProvider.IsReadOnly { bool IValueProvider.IsReadOnly {
get { return false; } get { return this.TextEditor.IsReadOnly; }
} }
/// <inheritdoc/> /// <inheritdoc/>
@ -57,5 +60,10 @@ namespace ICSharpCode.AvalonEdit
return base.GetPattern(patternInterface); return base.GetPattern(patternInterface);
} }
internal void RaiseIsReadOnlyChanged(bool oldValue, bool newValue)
{
RaisePropertyChangedEvent(ValuePatternIdentifiers.IsReadOnlyProperty, Boxes.Box(oldValue), Boxes.Box(newValue));
}
} }
} }

19
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/TextEditorOptions.cs

@ -145,7 +145,7 @@ namespace ICSharpCode.AvalonEdit
} }
} }
} }
bool requireControlModifierForHyperlinkClick = true; bool requireControlModifierForHyperlinkClick = true;
/// <summary> /// <summary>
@ -247,5 +247,22 @@ namespace ICSharpCode.AvalonEdit
} }
} }
} }
bool allowScrollBelowDocument;
/// <summary>
/// Gets/Sets whether the user can scroll below the bottom of the document.
/// The default value is false; but it a good idea to set this property to true when using folding.
/// </summary>
[DefaultValue(false)]
public virtual bool AllowScrollBelowDocument {
get { return allowScrollBelowDocument; }
set {
if (allowScrollBelowDocument != value) {
allowScrollBelowDocument = value;
OnPropertyChanged("AllowScrollBelowDocument");
}
}
}
} }
} }

Loading…
Cancel
Save