From 3d8c70850c1733003acead9917aee056f4909ca0 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Fri, 11 Sep 2009 19:22:46 +0000 Subject: [PATCH] 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 --- .../AvalonEdit.AddIn/Src/CodeEditor.cs | 4 + .../Src/ParserFoldingStrategy.cs | 60 +---- .../Folding/BraceFoldingStrategy.cs | 67 ++++++ .../Folding/FoldingManager.cs | 209 +++++++++++++----- .../Folding/NewFolding.cs | 67 ++++++ .../ICSharpCode.AvalonEdit.csproj | 5 + .../Rendering/TextView.cs | 4 + .../ICSharpCode.AvalonEdit/TextEditor.cs | 45 ++-- .../TextEditorAutomationPeer.cs | 26 ++- .../TextEditorOptions.cs | 19 +- 10 files changed, 378 insertions(+), 128 deletions(-) create mode 100644 src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Folding/BraceFoldingStrategy.cs create mode 100644 src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Folding/NewFolding.cs diff --git a/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/CodeEditor.cs b/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/CodeEditor.cs index 9f6d8ace59..acc9510526 100644 --- a/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/CodeEditor.cs +++ b/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/CodeEditor.cs @@ -146,6 +146,9 @@ namespace ICSharpCode.AvalonEdit.AddIn primaryTextEditorAdapter = (CodeEditorAdapter)primaryTextEditor.TextArea.GetService(typeof(ITextEditor)); 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.Document = primaryTextEditor.Document; @@ -239,6 +242,7 @@ namespace ICSharpCode.AvalonEdit.AddIn secondaryTextEditor.SetBinding(TextEditor.DocumentProperty, new Binding(TextEditor.DocumentProperty.Name) { Source = primaryTextEditor }); secondaryTextEditor.SyntaxHighlighting = primaryTextEditor.SyntaxHighlighting; + secondaryTextEditor.Options = primaryTextEditor.Options; SetRow(secondaryTextEditor, 2); this.Children.Add(secondaryTextEditor); diff --git a/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/ParserFoldingStrategy.cs b/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/ParserFoldingStrategy.cs index 78f1b8a36e..d12a2a072b 100644 --- a/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/ParserFoldingStrategy.cs +++ b/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/ParserFoldingStrategy.cs @@ -23,60 +23,26 @@ namespace ICSharpCode.AvalonEdit.AddIn { readonly FoldingManager foldingManager; TextArea textArea; - FoldingMargin margin; - FoldingElementGenerator generator; bool isFirstUpdate = true; public ParserFoldingStrategy(TextArea textArea) { this.textArea = textArea; - foldingManager = new FoldingManager(textArea.TextView, textArea.Document); - foldingManager.ExpandFoldingsWhenCaretIsMovedIntoThem(textArea.Caret); - margin = new FoldingMargin() { FoldingManager = foldingManager }; - generator = new FoldingElementGenerator() { FoldingManager = foldingManager }; - textArea.LeftMargins.Add(margin); - textArea.TextView.ElementGenerators.Add(generator); + foldingManager = FoldingManager.Install(textArea); } public void Dispose() { if (textArea != null) { - textArea.LeftMargins.Remove(margin); - textArea.TextView.ElementGenerators.Remove(generator); - foldingManager.Clear(); + FoldingManager.Uninstall(foldingManager); textArea = null; } } public void UpdateFoldings(ParseInformation parseInfo) { - var oldFoldings = foldingManager.AllFoldings.ToArray(); IEnumerable newFoldings = GetNewFoldings(parseInfo); - int oldFoldingIndex = 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) { - // 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++]); - } + foldingManager.UpdateFoldings(newFoldings, -1); isFirstUpdate = false; } @@ -91,12 +57,12 @@ namespace ICSharpCode.AvalonEdit.AddIn foreach (FoldingRegion foldingRegion in parseInfo.CompilationUnit.FoldingRegions) { NewFolding f = new NewFolding(textArea.Document.GetOffset(foldingRegion.Region.BeginLine, foldingRegion.Region.BeginColumn), textArea.Document.GetOffset(foldingRegion.Region.EndLine, foldingRegion.Region.EndColumn)); - f.DefaultClosed = true; + f.DefaultClosed = isFirstUpdate; f.Name = foldingRegion.Name; 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 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; - } - } } } diff --git a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Folding/BraceFoldingStrategy.cs b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Folding/BraceFoldingStrategy.cs new file mode 100644 index 0000000000..d93c66eaf0 --- /dev/null +++ b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Folding/BraceFoldingStrategy.cs @@ -0,0 +1,67 @@ +// +// +// +// +// $Revision$ +// + +using System; +using ICSharpCode.AvalonEdit.Document; +using System.Collections.Generic; + +namespace ICSharpCode.AvalonEdit.Folding +{ + /// + /// Allows producing foldings from a document based on braces. + /// + public class BraceFoldingStrategy + { + /// + /// Gets/Sets the opening brace. The default value is '{'. + /// + public char OpeningBrace { get; set; } + + /// + /// Gets/Sets the closing brace. The default value is '}'. + /// + public char ClosingBrace { get; set; } + + /// + /// Creates a new BraceFoldingStrategy. + /// + public BraceFoldingStrategy() + { + this.OpeningBrace = '{'; + this.ClosingBrace = '}'; + } + + /// + /// Create s for the specified document. + /// + public IEnumerable CreateNewFoldings(ITextSource document) + { + List newFoldings = new List(); + + Stack startOffsets = new Stack(); + 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; + } + } +} diff --git a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Folding/FoldingManager.cs b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Folding/FoldingManager.cs index 67390a18f0..1bc2c718e7 100644 --- a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Folding/FoldingManager.cs +++ b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Folding/FoldingManager.cs @@ -8,6 +8,7 @@ using System; using System.Collections.Generic; using System.Collections.ObjectModel; +using System.Linq; using System.Windows; using System.Windows.Threading; @@ -28,14 +29,7 @@ namespace ICSharpCode.AvalonEdit.Folding readonly TextSegmentCollection foldings; - /// - /// Creates a new FoldingManager instance. - /// - public FoldingManager(TextView textView) - : this(textView, ThrowUtil.CheckNotNull(textView, "textView").Document) - { - } - + #region Constructor /// /// Creates a new FoldingManager instance. /// @@ -49,48 +43,9 @@ namespace ICSharpCode.AvalonEdit.Folding this.document = document; this.foldings = new TextSegmentCollection(document); } - - #region ExpandFoldingsWhenCaretIsMovedIntoThem - // keep a reference to the helper as long as the folding manager exists - ExpandFoldingsWhenCaretIsMovedIntoThemHelper helper; - - /// - /// Will listen to Caret.PositionChanged events and automatically expand folding sections - /// when the caret is moved into them. - /// - 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 + #region Create / Remove / Clear /// /// Creates a folding for the specified text section. /// @@ -100,7 +55,7 @@ namespace ICSharpCode.AvalonEdit.Folding throw new ArgumentException("startOffset must be less than endOffset"); FoldingSection fs = new FoldingSection(this, startOffset, endOffset); foldings.Add(fs); - textView.Redraw(); + textView.Redraw(fs, DispatcherPriority.Normal); return fs; } @@ -111,7 +66,6 @@ namespace ICSharpCode.AvalonEdit.Folding { if (fs == null) throw new ArgumentNullException("fs"); - document.VerifyAccess(); fs.IsFolded = false; foldings.Remove(fs); textView.Redraw(fs, DispatcherPriority.Normal); @@ -128,7 +82,9 @@ namespace ICSharpCode.AvalonEdit.Folding foldings.Clear(); textView.Redraw(); } + #endregion + #region Get...Folding /// /// Gets all foldings in this manager. /// The foldings are returned sorted by start offset; @@ -182,5 +138,158 @@ namespace ICSharpCode.AvalonEdit.Folding { return foldings.FindSegmentsContaining(offset); } + #endregion + + #region UpdateFoldings + /// + /// Updates the foldings in this using the given new foldings. + /// This method will try to detect which new foldings correspond to which existing foldings; and will keep the state + /// () for existing foldings. + /// + /// The new set of foldings. These must be sorted by starting offset. + /// The first position of a parse error. Existing foldings starting after + /// this offset will be kept even if they don't appear in . + /// Use -1 for this parameter if there were no parse errors. + public void UpdateFoldings(IEnumerable 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 + /// + /// 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. + /// + /// The that manages the list of foldings inside the text area. + public static FoldingManager Install(TextArea textArea) + { + if (textArea == null) + throw new ArgumentNullException("textArea"); + return new FoldingManagerInstallation(textArea); + } + + /// + /// Uninstalls the folding manager. + /// + /// The specified manager was not created using . + 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 } } diff --git a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Folding/NewFolding.cs b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Folding/NewFolding.cs new file mode 100644 index 0000000000..371866788f --- /dev/null +++ b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Folding/NewFolding.cs @@ -0,0 +1,67 @@ +// +// +// +// +// $Revision$ +// + +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 +{ + /// + /// Helper class used for . + /// + public sealed class NewFolding : ISegment + { + /// + /// Gets the start offset. + /// + public int StartOffset { get; private set; } + + /// + /// Gets the end offset. + /// + public int EndOffset { get; private set; } + + /// + /// Gets/Sets the name displayed for the folding. + /// + public string Name { get; set; } + + /// + /// Gets/Sets whether the folding is closed by default. + /// + public bool DefaultClosed { get; set; } + + /// + /// Creates a new NewFolding instance. + /// + 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; } + } + } +} diff --git a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/ICSharpCode.AvalonEdit.csproj b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/ICSharpCode.AvalonEdit.csproj index 276e6c66ba..937c218c46 100644 --- a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/ICSharpCode.AvalonEdit.csproj +++ b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/ICSharpCode.AvalonEdit.csproj @@ -68,6 +68,9 @@ 3.0 + + 3.0 + 3.0 @@ -135,6 +138,7 @@ + @@ -167,6 +171,7 @@ IReadOnlySectionProvider.cs + diff --git a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Rendering/TextView.cs b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Rendering/TextView.cs index d4f93e6087..95e2ccefcc 100644 --- a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Rendering/TextView.cs +++ b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Rendering/TextView.cs @@ -636,6 +636,10 @@ namespace ICSharpCode.AvalonEdit.Rendering maxWidth += AdditionalHorizontalScrollAmount; 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, new Size(maxWidth, heightTreeHeight), diff --git a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/TextEditor.cs b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/TextEditor.cs index 19fafd4978..0728803351 100644 --- a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/TextEditor.cs +++ b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/TextEditor.cs @@ -14,13 +14,13 @@ using System.Windows.Controls; using System.Windows.Data; using System.Windows.Input; using System.Windows.Markup; +using System.Windows.Threading; using ICSharpCode.AvalonEdit.Document; using ICSharpCode.AvalonEdit.Editing; using ICSharpCode.AvalonEdit.Highlighting; using ICSharpCode.AvalonEdit.Rendering; using ICSharpCode.AvalonEdit.Utils; -using System.Windows.Threading; namespace ICSharpCode.AvalonEdit { @@ -353,11 +353,18 @@ namespace ICSharpCode.AvalonEdit static void OnIsReadOnlyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { - TextEditor editor = (TextEditor)d; - if ((bool)e.NewValue) - editor.TextArea.ReadOnlySectionProvider = ReadOnlyDocument.Instance; - else - editor.TextArea.ReadOnlySectionProvider = NoReadOnlySections.Instance; + TextEditor editor = d as TextEditor; + if (editor != null) { + if ((bool)e.NewValue) + editor.TextArea.ReadOnlySectionProvider = ReadOnlyDocument.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 @@ -922,6 +929,15 @@ namespace ICSharpCode.AvalonEdit return textView.GetPosition(TranslatePoint(point, textView) + textView.ScrollOffset); } + /// + /// Scrolls to the specified line. + /// This method requires that the TextEditor was already assigned a size (WPF layout must have run prior). + /// + public void ScrollToLine(int line) + { + ScrollTo(line, -1); + } + /// /// Scrolls to the specified line/column. /// 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; + ScrollToLine(line); 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; if (Math.Abs(verticalPos - scrollViewer.VerticalOffset) > MinimumScrollPercentage * scrollViewer.ViewportHeight) { scrollViewer.ScrollToVerticalOffset(Math.Max(0, verticalPos)); } - if (p.X > scrollViewer.ViewportWidth - Caret.MinimumDistanceToViewBorder * 2) { - double horizontalPos = Math.Max(0, p.X - scrollViewer.ViewportWidth / 2); - if (Math.Abs(horizontalPos - scrollViewer.HorizontalOffset) > MinimumScrollPercentage * scrollViewer.ViewportWidth) { - scrollViewer.ScrollToHorizontalOffset(horizontalPos); + if (column > 0) { + if (p.X > scrollViewer.ViewportWidth - Caret.MinimumDistanceToViewBorder * 2) { + double horizontalPos = Math.Max(0, p.X - scrollViewer.ViewportWidth / 2); + if (Math.Abs(horizontalPos - scrollViewer.HorizontalOffset) > MinimumScrollPercentage * scrollViewer.ViewportWidth) { + scrollViewer.ScrollToHorizontalOffset(horizontalPos); + } + } else { + scrollViewer.ScrollToHorizontalOffset(0); } - } else { - scrollViewer.ScrollToHorizontalOffset(0); } } } diff --git a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/TextEditorAutomationPeer.cs b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/TextEditorAutomationPeer.cs index 3fe34f027e..b43d591ea4 100644 --- a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/TextEditorAutomationPeer.cs +++ b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/TextEditorAutomationPeer.cs @@ -1,17 +1,19 @@ -/* - * Created by SharpDevelop. - * User: Daniel - * Date: 04.06.2009 - * Time: 20:46 - * - * To change this template use Tools | Options | Coding | Edit Standard Headers. - */ +// +// +// +// +// $Revision$ +// using System; +using System.Diagnostics; +using System.Windows.Automation; using System.Windows.Automation.Peers; using System.Windows.Automation.Provider; using System.Windows.Controls; +using ICSharpCode.AvalonEdit.Utils; + namespace ICSharpCode.AvalonEdit { /// @@ -24,6 +26,7 @@ namespace ICSharpCode.AvalonEdit /// public TextEditorAutomationPeer(TextEditor owner) : base(owner) { + Debug.WriteLine("TextEditorAutomationPeer was created"); } private TextEditor TextEditor { @@ -40,7 +43,7 @@ namespace ICSharpCode.AvalonEdit } bool IValueProvider.IsReadOnly { - get { return false; } + get { return this.TextEditor.IsReadOnly; } } /// @@ -57,5 +60,10 @@ namespace ICSharpCode.AvalonEdit return base.GetPattern(patternInterface); } + + internal void RaiseIsReadOnlyChanged(bool oldValue, bool newValue) + { + RaisePropertyChangedEvent(ValuePatternIdentifiers.IsReadOnlyProperty, Boxes.Box(oldValue), Boxes.Box(newValue)); + } } } diff --git a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/TextEditorOptions.cs b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/TextEditorOptions.cs index 2e5e14a310..fe6a01bd02 100644 --- a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/TextEditorOptions.cs +++ b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/TextEditorOptions.cs @@ -145,7 +145,7 @@ namespace ICSharpCode.AvalonEdit } } } - + bool requireControlModifierForHyperlinkClick = true; /// @@ -247,5 +247,22 @@ namespace ICSharpCode.AvalonEdit } } } + + bool allowScrollBelowDocument; + + /// + /// 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. + /// + [DefaultValue(false)] + public virtual bool AllowScrollBelowDocument { + get { return allowScrollBelowDocument; } + set { + if (allowScrollBelowDocument != value) { + allowScrollBelowDocument = value; + OnPropertyChanged("AllowScrollBelowDocument"); + } + } + } } }