diff --git a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/CodeCompletion/CompletionList.cs b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/CodeCompletion/CompletionList.cs new file mode 100644 index 0000000000..121cc6d4f7 --- /dev/null +++ b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/CodeCompletion/CompletionList.cs @@ -0,0 +1,20 @@ +// +// +// +// +// $Revision$ +// + +using System; +using System.Windows.Controls; + +namespace ICSharpCode.AvalonEdit.CodeCompletion +{ + /// + /// The listbox used inside the CompletionWindow. + /// + public class CompletionList : ListBox + { + + } +} diff --git a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/CodeCompletion/CompletionWindow.xaml b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/CodeCompletion/CompletionWindow.xaml new file mode 100644 index 0000000000..4ff6a769a2 --- /dev/null +++ b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/CodeCompletion/CompletionWindow.xaml @@ -0,0 +1,9 @@ + + + \ No newline at end of file diff --git a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/CodeCompletion/CompletionWindow.xaml.cs b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/CodeCompletion/CompletionWindow.xaml.cs new file mode 100644 index 0000000000..267f9c2b20 --- /dev/null +++ b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/CodeCompletion/CompletionWindow.xaml.cs @@ -0,0 +1,75 @@ +using ICSharpCode.AvalonEdit.Document; +using System; + +namespace ICSharpCode.AvalonEdit.CodeCompletion +{ + /// + /// The code completion window. + /// + public partial class CompletionWindow : CompletionWindowBase + { + TextDocument document; + int startOffset; + int endOffset; + + /// + /// Creates a new code completion window. + /// + public CompletionWindow(TextArea textArea) : base(textArea) + { + InitializeComponent(); + + document = textArea.TextView.Document; + startOffset = endOffset = textArea.Caret.Offset; + } + + /// + protected override void AttachEvents() + { + base.AttachEvents(); + document.Changing += textArea_Document_Changing; + this.TextArea.Caret.PositionChanged += CaretPositionChanged; + } + + /// + protected override void DetachEvents() + { + document.Changing -= textArea_Document_Changing; + this.TextArea.Caret.PositionChanged -= CaretPositionChanged; + base.DetachEvents(); + } + + void textArea_Document_Changing(object sender, DocumentChangeEventArgs e) + { + // => startOffset test required so that this startOffset/endOffset are not incremented again + // for BeforeStartKey characters + if (e.Offset >= startOffset && e.Offset <= endOffset) { + endOffset += e.InsertionLength - e.RemovalLength; + } else { + Close(); + } + } + + /// + /// When this flag is set, code completion closes if the caret moves to the + /// beginning of the allowed range. This is useful in Ctrl+Space and "complete when typing", + /// but not in dot-completion. + /// + public bool CloseWhenCaretAtBeginning { get; set; } + + void CaretPositionChanged(object sender, EventArgs e) + { + int offset = this.TextArea.Caret.Offset; + if (offset == startOffset) { + if (CloseWhenCaretAtBeginning) + Close(); + return; + } + if (offset < startOffset || offset > endOffset) { + Close(); + } else { + //codeCompletionListView.SelectItemWithStart(document.GetText(startOffset, offset - startOffset)); + } + } + } +} \ No newline at end of file diff --git a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/CodeCompletion/CompletionWindowBase.cs b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/CodeCompletion/CompletionWindowBase.cs new file mode 100644 index 0000000000..d1f27380de --- /dev/null +++ b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/CodeCompletion/CompletionWindowBase.cs @@ -0,0 +1,178 @@ +// +// +// +// +// $Revision$ +// + +using System; +using System.Diagnostics; +using System.Windows; +using System.Windows.Input; +using System.Windows.Threading; + +using ICSharpCode.AvalonEdit.Gui; +using ICSharpCode.AvalonEdit.Utils; + +namespace ICSharpCode.AvalonEdit.CodeCompletion +{ + /// + /// Base class for completion windows. Handles positioning the window at the caret. + /// + public class CompletionWindowBase : Window + { + static CompletionWindowBase() + { + WindowStyleProperty.OverrideMetadata(typeof(CompletionWindowBase), new FrameworkPropertyMetadata(WindowStyle.None)); + ShowActivatedProperty.OverrideMetadata(typeof(CompletionWindowBase), new FrameworkPropertyMetadata(Boxes.False)); + ShowInTaskbarProperty.OverrideMetadata(typeof(CompletionWindowBase), new FrameworkPropertyMetadata(Boxes.False)); + } + + /// + /// Gets the parent TextArea. + /// + public TextArea TextArea { get; private set; } + + Window parentWindow; + + /// + /// Creates a new CompletionWindowBase. + /// + public CompletionWindowBase(TextArea textArea) + { + if (textArea == null) + throw new ArgumentNullException("textArea"); + this.TextArea = textArea; + parentWindow = Window.GetWindow(textArea); + } + + #region Event Handlers + /// + /// Attaches events to the text area. + /// + protected virtual void AttachEvents() + { + this.TextArea.LostFocus += TextAreaLostFocus; + this.TextArea.TextView.ScrollOffsetChanged += TextViewScrollOffsetChanged; + this.TextArea.TextView.DocumentChanged += TextViewDocumentChanged; + if (parentWindow != null) { + parentWindow.LocationChanged += parentWindow_LocationChanged; + parentWindow.Deactivated += parentWindowDeactivated; + } + } + + /// + /// Detaches events from the text area. + /// + protected virtual void DetachEvents() + { + this.TextArea.LostFocus -= TextAreaLostFocus; + this.TextArea.TextView.ScrollOffsetChanged -= TextViewScrollOffsetChanged; + this.TextArea.TextView.DocumentChanged -= TextViewDocumentChanged; + if (parentWindow != null) { + parentWindow.LocationChanged -= parentWindow_LocationChanged; + parentWindow.Deactivated -= parentWindowDeactivated; + } + } + + void TextViewScrollOffsetChanged(object sender, EventArgs e) + { + UpdatePosition(); + } + + void TextViewDocumentChanged(object sender, EventArgs e) + { + Close(); + } + + void TextAreaLostFocus(object sender, RoutedEventArgs e) + { + Dispatcher.BeginInvoke(new Action(CloseIfFocusLost), DispatcherPriority.Background); + } + + void parentWindow_LocationChanged(object sender, EventArgs e) + { + UpdatePosition(); + } + + void parentWindowDeactivated(object sender, EventArgs e) + { + Dispatcher.BeginInvoke(new Action(CloseIfFocusLost), DispatcherPriority.Background); + } + + /// + protected override void OnDeactivated(EventArgs e) + { + base.OnDeactivated(e); + Dispatcher.BeginInvoke(new Action(CloseIfFocusLost), DispatcherPriority.Background); + } + #endregion + + void CloseIfFocusLost() + { + Debug.WriteLine("CloseIfFocusLost"); + if (!this.IsActive && parentWindow != null && !parentWindow.IsActive) { + // close if parent window looses focus + Close(); + } + if (!this.TextArea.IsFocused) { + Close(); + } + } + + /// + protected override void OnSourceInitialized(EventArgs e) + { + base.OnSourceInitialized(e); + SetPosition(); + AttachEvents(); + } + + /// + protected override void OnClosed(EventArgs e) + { + base.OnClosed(e); + DetachEvents(); + } + + Point visualLocation, visualLocationTop; + + /// + /// Positions the completion window at the caret position. + /// + void SetPosition() + { + TextView textView = this.TextArea.TextView; + + visualLocation = textView.GetVisualPosition(this.TextArea.Caret.Position, VisualYPosition.LineBottom); + visualLocationTop = textView.GetVisualPosition(this.TextArea.Caret.Position, VisualYPosition.LineTop); + UpdatePosition(); + } + + void UpdatePosition() + { + TextView textView = this.TextArea.TextView; + Point location = textView.PointToScreen(visualLocation - textView.ScrollOffset); + Point locationTop = textView.PointToScreen(visualLocationTop - textView.ScrollOffset); + + Size completionWindowSize = new Size(this.ActualWidth, this.ActualHeight); + Rect bounds = new Rect(location, completionWindowSize); + Rect workingScreen = System.Windows.Forms.Screen.GetWorkingArea(location.ToSystemDrawing()).ToWpf(); + if (!workingScreen.Contains(bounds)) { + if (bounds.Left < workingScreen.Left) { + bounds.X = workingScreen.Left; + } else if (bounds.Right > workingScreen.Right) { + bounds.X = workingScreen.Right - bounds.Width; + } + if (bounds.Bottom > workingScreen.Bottom) { + bounds.Y = locationTop.Y - bounds.Height; + } + if (bounds.Y < workingScreen.Top) { + bounds.Y = workingScreen.Top; + } + } + this.Left = bounds.X; + this.Top = bounds.Y; + } + } +} diff --git a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Gui/BackgroundGeometryBuilder.cs b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Gui/BackgroundGeometryBuilder.cs index 764ed60c5a..6e6cee95fa 100644 --- a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Gui/BackgroundGeometryBuilder.cs +++ b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Gui/BackgroundGeometryBuilder.cs @@ -76,7 +76,7 @@ namespace ICSharpCode.AvalonEdit.Gui TextLine lastTextLine = vl.TextLines.Last(); foreach (TextLine line in vl.TextLines) { - double y = vl.GetTextLineVisualTop(line); + double y = vl.GetTextLineVisualYPosition(line, VisualYPosition.LineTop); int visualStartCol = vl.GetTextLineVisualStartColumn(line); int visualEndCol = visualStartCol + line.Length; if (line != lastTextLine) diff --git a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Gui/Caret.cs b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Gui/Caret.cs index ddf0ce63f5..4644fd4807 100644 --- a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Gui/Caret.cs +++ b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Gui/Caret.cs @@ -213,14 +213,13 @@ namespace ICSharpCode.AvalonEdit.Gui TextLine textLine = visualLine.GetTextLine(position.VisualColumn); double xPos = textLine.GetDistanceFromCharacterHit(new CharacterHit(position.VisualColumn, 0)); - double lineTop = visualLine.GetTextLineVisualTop(textLine); - double lineBottom = lineTop + textLine.Height; - double fontSize = (double)textArea.GetValue(TextBlock.FontSizeProperty); + double lineTop = visualLine.GetTextLineVisualYPosition(textLine, VisualYPosition.TextTop); + double lineBottom = visualLine.GetTextLineVisualYPosition(textLine, VisualYPosition.LineBottom); return new Rect(xPos, - lineBottom - fontSize, + lineTop, SystemParameters.CaretWidth, - fontSize); + lineBottom - lineTop); } /// diff --git a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Gui/CaretNavigationCommandHandler.cs b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Gui/CaretNavigationCommandHandler.cs index d162f6aae9..1e4861d01a 100644 --- a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Gui/CaretNavigationCommandHandler.cs +++ b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Gui/CaretNavigationCommandHandler.cs @@ -280,14 +280,14 @@ namespace ICSharpCode.AvalonEdit.Gui case CaretMovementType.PageDown: { // Page up/down: find the target line using its visual position - double yPos = visualLine.GetTextLineVisualTop(textLine) + textLine.Height / 2; + double yPos = visualLine.GetTextLineVisualYPosition(textLine, VisualYPosition.LineMiddle); if (direction == CaretMovementType.PageUp) yPos -= textArea.TextView.RenderSize.Height; else yPos += textArea.TextView.RenderSize.Height; DocumentLine newLine = textArea.TextView.GetDocumentLineByVisualTop(yPos); targetVisualLine = textArea.TextView.GetOrConstructVisualLine(newLine); - targetLine = targetVisualLine.GetTextLineByVisualTop(yPos); + targetLine = targetVisualLine.GetTextLineByVisualYPosition(yPos); break; } default: diff --git a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Gui/FoldingMargin.cs b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Gui/FoldingMargin.cs index 98832cbcde..e2ecbdd665 100644 --- a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Gui/FoldingMargin.cs +++ b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Gui/FoldingMargin.cs @@ -45,7 +45,7 @@ namespace ICSharpCode.AvalonEdit.Gui foreach (FoldingMarginMarker m in markers) { int visualColumn = m.VisualLine.GetVisualColumn(m.FoldingSection.StartOffset - m.VisualLine.FirstDocumentLine.Offset); TextLine textLine = m.VisualLine.GetTextLine(visualColumn); - double yPos = m.VisualLine.GetTextLineVisualTop(textLine) - TextView.VerticalOffset; + double yPos = m.VisualLine.GetTextLineVisualYPosition(textLine, VisualYPosition.LineTop) - TextView.VerticalOffset; yPos += (textLine.Height - m.DesiredSize.Height) / 2; double xPos = (finalSize.Width - m.DesiredSize.Width) / 2; m.Arrange(new Rect(new Point(xPos, yPos), m.DesiredSize)); @@ -213,7 +213,7 @@ namespace ICSharpCode.AvalonEdit.Gui double GetVisualPos(VisualLine vl, TextLine tl) { - double pos = vl.GetTextLineVisualTop(tl) + tl.Height / 2 - TextView.VerticalOffset; + double pos = vl.GetTextLineVisualYPosition(tl, VisualYPosition.LineTop) + tl.Height / 2 - TextView.VerticalOffset; return Math.Round(pos) + 0.5; } diff --git a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Gui/ITextViewConnect.cs b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Gui/ITextViewConnect.cs index 0bd36ed0c6..d248488c1d 100644 --- a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Gui/ITextViewConnect.cs +++ b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Gui/ITextViewConnect.cs @@ -11,7 +11,7 @@ namespace ICSharpCode.AvalonEdit.Gui { /// /// Allows s, s and - /// to be notified when they are added or removed from a text view. + /// s to be notified when they are added or removed from a text view. /// public interface ITextViewConnect { diff --git a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Gui/LineNumberMargin.cs b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Gui/LineNumberMargin.cs index 100d50f72b..a8e95a1256 100644 --- a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Gui/LineNumberMargin.cs +++ b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Gui/LineNumberMargin.cs @@ -175,7 +175,7 @@ namespace ICSharpCode.AvalonEdit.Gui VisualLine vl = TextView.GetVisualLineFromVisualTop(pos.Y); if (vl == null) return SimpleSegment.Invalid; - TextLine tl = vl.GetTextLineByVisualTop(pos.Y); + TextLine tl = vl.GetTextLineByVisualYPosition(pos.Y); int visualStartColumn = vl.GetTextLineVisualStartColumn(tl); int visualEndColumn = visualStartColumn + tl.Length; int relStart = vl.FirstDocumentLine.Offset; diff --git a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Gui/TextEditor.cs b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Gui/TextEditor.cs index 1bad9f264d..ffcff93e7f 100644 --- a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Gui/TextEditor.cs +++ b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Gui/TextEditor.cs @@ -625,6 +625,7 @@ namespace ICSharpCode.AvalonEdit /// /// Gets/sets the encoding used when the file is saved. /// + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public Encoding Encoding { get; set; } /// diff --git a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Gui/TextView.cs b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Gui/TextView.cs index 0d8f6bdd20..5f0cdebf6f 100644 --- a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Gui/TextView.cs +++ b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Gui/TextView.cs @@ -82,7 +82,7 @@ namespace ICSharpCode.AvalonEdit.Gui ((TextView)dp).OnDocumentChanged((TextDocument)e.OldValue, (TextDocument)e.NewValue); } - double LineHeight { + internal double FontSize { get { return (double)GetValue(TextBlock.FontSizeProperty); } @@ -107,7 +107,7 @@ namespace ICSharpCode.AvalonEdit.Gui ClearVisualLines(); if (newValue != null) { TextDocumentWeakEventManager.Changing.AddListener(newValue, this); - heightTree = new HeightTree(newValue, LineHeight + 3); + heightTree = new HeightTree(newValue, FontSize + 3); formatter = TextFormatter.Create(); } InvalidateMeasure(DispatcherPriority.Normal); @@ -579,7 +579,7 @@ namespace ICSharpCode.AvalonEdit.Gui { return new GlobalTextRunProperties { typeface = this.CreateTypeface(), - fontRenderingEmSize = LineHeight, + fontRenderingEmSize = FontSize, foregroundBrush = (Brush)GetValue(Control.ForegroundProperty), cultureInfo = CultureInfo.CurrentCulture }; @@ -606,7 +606,7 @@ namespace ICSharpCode.AvalonEdit.Gui Debug.WriteLine("Building line " + documentLine.LineNumber); - VisualLine visualLine = new VisualLine(documentLine); + VisualLine visualLine = new VisualLine(this, documentLine); VisualLineTextSource textSource = new VisualLineTextSource(visualLine) { Document = document, GlobalTextRunProperties = globalTextRunProperties, @@ -868,12 +868,12 @@ namespace ICSharpCode.AvalonEdit.Gui void IScrollInfo.LineUp() { - ((IScrollInfo)this).SetVerticalOffset(scrollOffset.Y - LineHeight); + ((IScrollInfo)this).SetVerticalOffset(scrollOffset.Y - FontSize); } void IScrollInfo.LineDown() { - ((IScrollInfo)this).SetVerticalOffset(scrollOffset.Y + LineHeight); + ((IScrollInfo)this).SetVerticalOffset(scrollOffset.Y + FontSize); } void IScrollInfo.LineLeft() @@ -909,14 +909,14 @@ namespace ICSharpCode.AvalonEdit.Gui void IScrollInfo.MouseWheelUp() { ((IScrollInfo)this).SetVerticalOffset( - scrollOffset.Y - (SystemParameters.WheelScrollLines * LineHeight)); + scrollOffset.Y - (SystemParameters.WheelScrollLines * FontSize)); OnScrollChange(); } void IScrollInfo.MouseWheelDown() { ((IScrollInfo)this).SetVerticalOffset( - scrollOffset.Y + (SystemParameters.WheelScrollLines * LineHeight)); + scrollOffset.Y + (SystemParameters.WheelScrollLines * FontSize)); OnScrollChange(); } @@ -936,7 +936,7 @@ namespace ICSharpCode.AvalonEdit.Gui double WideSpaceWidth { get { - return LineHeight / 2; + return FontSize / 2; } } @@ -1116,6 +1116,30 @@ namespace ICSharpCode.AvalonEdit.Gui } #endregion + #region Visual Position <-> TextViewPosition + /// + /// Gets the visual position from a text view position. + /// + /// The text view position. + /// The mode how to retrieve the Y position. + /// The position in WPF device-independent pixels relative + /// to the top left corner of the document. + public Point GetVisualPosition(TextViewPosition position, VisualYPosition yPositionMode) + { + VerifyAccess(); + if (this.Document == null) + throw new InvalidOperationException("There is no document assigned to the TextView"); + DocumentLine documentLine = this.Document.GetLineByNumber(position.Line); + VisualLine visualLine = GetOrConstructVisualLine(documentLine); + int visualColumn = position.VisualColumn; + if (visualColumn < 0) { + int offset = documentLine.Offset + position.Column - 1; + visualColumn = visualLine.GetVisualColumn(offset - visualLine.FirstDocumentLine.Offset); + } + return visualLine.GetVisualPosition(visualColumn, yPositionMode); + } + #endregion + #region Service Provider readonly ServiceContainer services = new ServiceContainer(); diff --git a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Gui/VisualLine.cs b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Gui/VisualLine.cs index c718463efb..7b5e706583 100644 --- a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Gui/VisualLine.cs +++ b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Gui/VisualLine.cs @@ -23,6 +23,7 @@ namespace ICSharpCode.AvalonEdit.Gui /// public sealed class VisualLine { + TextView textView; List elements; /// @@ -60,9 +61,11 @@ namespace ICSharpCode.AvalonEdit.Gui /// public double VisualTop { get; internal set; } - internal VisualLine(DocumentLine firstDocumentLine) + internal VisualLine(TextView textView, DocumentLine firstDocumentLine) { + Debug.Assert(textView != null); Debug.Assert(firstDocumentLine != null); + this.textView = textView; this.FirstDocumentLine = firstDocumentLine; } @@ -223,18 +226,30 @@ namespace ICSharpCode.AvalonEdit.Gui /// /// Distance in device-independent pixels /// from the top of the document to the top of the specified text line. - public double GetTextLineVisualTop(TextLine textLine) + public double GetTextLineVisualYPosition(TextLine textLine, VisualYPosition yPositionMode) { - if (!TextLines.Contains(textLine)) - throw new ArgumentException("textLine is not a line in this VisualLine"); + if (textLine == null) + throw new ArgumentNullException("textLine"); double pos = VisualTop; foreach (TextLine tl in TextLines) { - if (tl == textLine) - break; - else + if (tl == textLine) { + switch (yPositionMode) { + case VisualYPosition.LineTop: + return pos; + case VisualYPosition.LineMiddle: + return pos + tl.Height / 2; + case VisualYPosition.LineBottom: + return pos + tl.Height; + case VisualYPosition.TextTop: + return pos + tl.Height - textView.FontSize; + default: + throw new ArgumentException("Invalid yPositionMode:" + yPositionMode); + } + } else { pos += tl.Height; + } } - return pos; + throw new ArgumentException("textLine is not a line in this VisualLine"); } /// @@ -257,7 +272,7 @@ namespace ICSharpCode.AvalonEdit.Gui /// /// Gets a TextLine by the visual position. /// - public TextLine GetTextLineByVisualTop(double visualTop) + public TextLine GetTextLineByVisualYPosition(double visualTop) { const double epsilon = 0.0001; double pos = this.VisualTop; @@ -274,11 +289,11 @@ namespace ICSharpCode.AvalonEdit.Gui /// /// Position in device-independent pixels /// relative to the top left of the document. - public Point GetVisualPosition(int visualColumn) + public Point GetVisualPosition(int visualColumn, VisualYPosition yPositionMode) { TextLine textLine = GetTextLine(visualColumn); double xPos = textLine.GetDistanceFromCharacterHit(new CharacterHit(visualColumn, 0)); - double yPos = GetTextLineVisualTop(textLine); + double yPos = GetTextLineVisualYPosition(textLine, yPositionMode); return new Point(xPos, yPos); } @@ -287,7 +302,7 @@ namespace ICSharpCode.AvalonEdit.Gui /// public int GetVisualColumn(Point point) { - TextLine textLine = GetTextLineByVisualTop(point.Y); + TextLine textLine = GetTextLineByVisualYPosition(point.Y); CharacterHit ch = textLine.GetCharacterHitFromDistance(point.X); return ch.FirstCharacterIndex + ch.TrailingLength; } diff --git a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Gui/VisualYPosition.cs b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Gui/VisualYPosition.cs new file mode 100644 index 0000000000..ef20f53532 --- /dev/null +++ b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Gui/VisualYPosition.cs @@ -0,0 +1,36 @@ +// +// +// +// +// $Revision$ +// + +using System; + +namespace ICSharpCode.AvalonEdit.Gui +{ + /// + /// An enum that specifies the possible Y positions that can be returned by VisualLine.GetVisualPosition. + /// + public enum VisualYPosition + { + /// + /// Returns the top of the TextLine. + /// + LineTop, + /// + /// Returns the top of the text. If the line contains inline UI elements larger than the text, TextTop + /// will be below LineTop. + /// + TextTop, + /// + /// Returns the bottom of the TextLine. This is the same as the bottom of the text (the text is always + /// aligned at the bottom border). + /// + LineBottom, + /// + /// The middle between LineTop and LineBottom. + /// + LineMiddle + } +} diff --git a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Highlighting/HighlightingColorizer.cs b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Highlighting/HighlightingColorizer.cs index 9fdf3680f4..2039fcc516 100644 --- a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Highlighting/HighlightingColorizer.cs +++ b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Highlighting/HighlightingColorizer.cs @@ -24,6 +24,7 @@ namespace ICSharpCode.AvalonEdit.Highlighting readonly TextView textView; readonly HighlightingRuleSet ruleSet; DocumentHighlighter highlighter; + bool isInTextView; /// /// Creates a new HighlightingColorizer instance. @@ -53,25 +54,39 @@ namespace ICSharpCode.AvalonEdit.Highlighting void OnDocumentChanged() { + if (highlighter != null && isInTextView) { + textView.Services.RemoveService(typeof(DocumentHighlighter)); + } + TextDocument document = textView.Document; if (document != null) highlighter = new TextViewDocumentHighlighter(this, document, ruleSet); else highlighter = null; + + if (highlighter != null && isInTextView) { + textView.Services.AddService(typeof(DocumentHighlighter), highlighter); + } } /// protected override void OnAddToTextView(TextView textView) { base.OnAddToTextView(textView); - textView.Services.AddService(typeof(DocumentHighlighter), highlighter); + isInTextView = true; + if (highlighter != null) { + textView.Services.AddService(typeof(DocumentHighlighter), highlighter); + } } /// protected override void OnRemoveFromTextView(TextView textView) { base.OnRemoveFromTextView(textView); - textView.Services.RemoveService(typeof(DocumentHighlighter)); + isInTextView = false; + if (highlighter != null) { + textView.Services.RemoveService(typeof(DocumentHighlighter)); + } } int currentLineEndOffset; diff --git a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/ICSharpCode.AvalonEdit.csproj b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/ICSharpCode.AvalonEdit.csproj index 3b6994f759..367d7f2560 100644 --- a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/ICSharpCode.AvalonEdit.csproj +++ b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/ICSharpCode.AvalonEdit.csproj @@ -59,6 +59,8 @@ 3.5 + + 3.5 @@ -71,6 +73,12 @@ Properties\GlobalAssemblyInfo.cs + + + + CompletionWindow.xaml + Code + UndoStack.cs @@ -209,6 +217,9 @@ + + VisualLine.cs + @@ -303,8 +314,10 @@ + + diff --git a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Properties/CodeAnalysisDictionary.xml b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Properties/CodeAnalysisDictionary.xml index da48eea79e..00a740e894 100644 --- a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Properties/CodeAnalysisDictionary.xml +++ b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Properties/CodeAnalysisDictionary.xml @@ -10,6 +10,8 @@ Colorizer Renderer Deletable + + y diff --git a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Utils/ExtensionMethods.cs b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Utils/ExtensionMethods.cs index b52b151fd1..563efde47a 100644 --- a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Utils/ExtensionMethods.cs +++ b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Utils/ExtensionMethods.cs @@ -19,6 +19,7 @@ namespace ICSharpCode.AvalonEdit.Utils { static class ExtensionMethods { + #region Epsilon / IsClose public const double Epsilon = 1e-8; /// @@ -46,7 +47,9 @@ namespace ICSharpCode.AvalonEdit.Utils { return IsClose(d1.X, d2.X) && IsClose(d1.Y, d2.Y); } + #endregion + #region CreateTypeface /// /// Creates typeface from the framework element. /// @@ -57,7 +60,9 @@ namespace ICSharpCode.AvalonEdit.Utils (FontWeight)fe.GetValue(TextBlock.FontWeightProperty), (FontStretch)fe.GetValue(TextBlock.FontStretchProperty)); } + #endregion + #region AddRange / Sequence public static void AddRange(this ICollection collection, IEnumerable elements) { foreach (T e in elements) @@ -71,7 +76,9 @@ namespace ICSharpCode.AvalonEdit.Utils { yield return value; } + #endregion + #region XML reading /// /// Gets the value of the attribute, or null if the attribute does not exist. /// @@ -101,7 +108,9 @@ namespace ICSharpCode.AvalonEdit.Utils else return XmlConvert.ToBoolean(attributeValue); } + #endregion + #region ISegment extensions /// /// Gets the end offset of the segment. /// @@ -141,5 +150,28 @@ namespace ICSharpCode.AvalonEdit.Utils else return new SimpleSegment(start, end - start); } + #endregion + + #region System.Drawing <-> WPF conversions + public static System.Drawing.Point ToSystemDrawing(this Point p) + { + return new System.Drawing.Point((int)p.X, (int)p.Y); + } + + public static Point ToWpf(this System.Drawing.Point p) + { + return new Point(p.X, p.Y); + } + + public static Size ToWpf(this System.Drawing.Size s) + { + return new Size(s.Width, s.Height); + } + + public static Rect ToWpf(this System.Drawing.Rectangle rect) + { + return new Rect(rect.Location.ToWpf(), rect.Size.ToWpf()); + } + #endregion } }