Free cookie consent management tool by TermsFeed Policy Generator

source: branches/RefactorPluginInfrastructure-2522/HeuristicLab.ExtLibs/HeuristicLab.AvalonEdit/5.0.1/AvalonEdit-5.0.1/Editing/LineNumberMargin.cs @ 13332

Last change on this file since 13332 was 11700, checked in by jkarder, 10 years ago

#2077: created branch and added first version

File size: 8.8 KB
Line 
1// Copyright (c) 2014 AlphaSierraPapa for the SharpDevelop Team
2//
3// Permission is hereby granted, free of charge, to any person obtaining a copy of this
4// software and associated documentation files (the "Software"), to deal in the Software
5// without restriction, including without limitation the rights to use, copy, modify, merge,
6// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
7// to whom the Software is furnished to do so, subject to the following conditions:
8//
9// The above copyright notice and this permission notice shall be included in all copies or
10// substantial portions of the Software.
11//
12// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
13// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
14// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
15// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
16// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
17// DEALINGS IN THE SOFTWARE.
18
19using System;
20using System.ComponentModel;
21using System.Globalization;
22using System.Windows;
23using System.Windows.Controls;
24using System.Windows.Input;
25using System.Windows.Media;
26using System.Windows.Media.TextFormatting;
27
28using ICSharpCode.AvalonEdit.Document;
29using ICSharpCode.AvalonEdit.Rendering;
30using ICSharpCode.AvalonEdit.Utils;
31
32namespace ICSharpCode.AvalonEdit.Editing
33{
34  /// <summary>
35  /// Margin showing line numbers.
36  /// </summary>
37  public class LineNumberMargin : AbstractMargin, IWeakEventListener
38  {
39    static LineNumberMargin()
40    {
41      DefaultStyleKeyProperty.OverrideMetadata(typeof(LineNumberMargin),
42                                               new FrameworkPropertyMetadata(typeof(LineNumberMargin)));
43    }
44   
45    TextArea textArea;
46   
47    /// <summary>
48    /// The typeface used for rendering the line number margin.
49    /// This field is calculated in MeasureOverride() based on the FontFamily etc. properties.
50    /// </summary>
51    protected Typeface typeface;
52   
53    /// <summary>
54    /// The font size used for rendering the line number margin.
55    /// This field is calculated in MeasureOverride() based on the FontFamily etc. properties.
56    /// </summary>
57    protected double emSize;
58   
59    /// <inheritdoc/>
60    protected override Size MeasureOverride(Size availableSize)
61    {
62      typeface = this.CreateTypeface();
63      emSize = (double)GetValue(TextBlock.FontSizeProperty);
64     
65      FormattedText text = TextFormatterFactory.CreateFormattedText(
66        this,
67        new string('9', maxLineNumberLength),
68        typeface,
69        emSize,
70        (Brush)GetValue(Control.ForegroundProperty)
71      );
72      return new Size(text.Width, 0);
73    }
74   
75    /// <inheritdoc/>
76    protected override void OnRender(DrawingContext drawingContext)
77    {
78      TextView textView = this.TextView;
79      Size renderSize = this.RenderSize;
80      if (textView != null && textView.VisualLinesValid) {
81        var foreground = (Brush)GetValue(Control.ForegroundProperty);
82        foreach (VisualLine line in textView.VisualLines) {
83          int lineNumber = line.FirstDocumentLine.LineNumber;
84          FormattedText text = TextFormatterFactory.CreateFormattedText(
85            this,
86            lineNumber.ToString(CultureInfo.CurrentCulture),
87            typeface, emSize, foreground
88          );
89          double y = line.GetTextLineVisualYPosition(line.TextLines[0], VisualYPosition.TextTop);
90          drawingContext.DrawText(text, new Point(renderSize.Width - text.Width, y - textView.VerticalOffset));
91        }
92      }
93    }
94   
95    /// <inheritdoc/>
96    protected override void OnTextViewChanged(TextView oldTextView, TextView newTextView)
97    {
98      if (oldTextView != null) {
99        oldTextView.VisualLinesChanged -= TextViewVisualLinesChanged;
100      }
101      base.OnTextViewChanged(oldTextView, newTextView);
102      if (newTextView != null) {
103        newTextView.VisualLinesChanged += TextViewVisualLinesChanged;
104       
105        // find the text area belonging to the new text view
106        textArea = newTextView.GetService(typeof(TextArea)) as TextArea;
107      } else {
108        textArea = null;
109      }
110      InvalidateVisual();
111    }
112   
113    /// <inheritdoc/>
114    protected override void OnDocumentChanged(TextDocument oldDocument, TextDocument newDocument)
115    {
116      if (oldDocument != null) {
117        PropertyChangedEventManager.RemoveListener(oldDocument, this, "LineCount");
118      }
119      base.OnDocumentChanged(oldDocument, newDocument);
120      if (newDocument != null) {
121        PropertyChangedEventManager.AddListener(newDocument, this, "LineCount");
122      }
123      OnDocumentLineCountChanged();
124    }
125   
126    /// <inheritdoc cref="IWeakEventListener.ReceiveWeakEvent"/>
127    protected virtual bool ReceiveWeakEvent(Type managerType, object sender, EventArgs e)
128    {
129      if (managerType == typeof(PropertyChangedEventManager)) {
130        OnDocumentLineCountChanged();
131        return true;
132      }
133      return false;
134    }
135   
136    bool IWeakEventListener.ReceiveWeakEvent(Type managerType, object sender, EventArgs e)
137    {
138      return ReceiveWeakEvent(managerType, sender, e);
139    }
140   
141    /// <summary>
142    /// Maximum length of a line number, in characters
143    /// </summary>
144    protected int maxLineNumberLength = 1;
145   
146    void OnDocumentLineCountChanged()
147    {
148      int documentLineCount = Document != null ? Document.LineCount : 1;
149      int newLength = documentLineCount.ToString(CultureInfo.CurrentCulture).Length;
150     
151      // The margin looks too small when there is only one digit, so always reserve space for
152      // at least two digits
153      if (newLength < 2)
154        newLength = 2;
155     
156      if (newLength != maxLineNumberLength) {
157        maxLineNumberLength = newLength;
158        InvalidateMeasure();
159      }
160    }
161   
162    void TextViewVisualLinesChanged(object sender, EventArgs e)
163    {
164      InvalidateVisual();
165    }
166   
167    AnchorSegment selectionStart;
168    bool selecting;
169   
170    /// <inheritdoc/>
171    protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e)
172    {
173      base.OnMouseLeftButtonDown(e);
174      if (!e.Handled && TextView != null && textArea != null) {
175        e.Handled = true;
176        textArea.Focus();
177       
178        SimpleSegment currentSeg = GetTextLineSegment(e);
179        if (currentSeg == SimpleSegment.Invalid)
180          return;
181        textArea.Caret.Offset = currentSeg.Offset + currentSeg.Length;
182        if (CaptureMouse()) {
183          selecting = true;
184          selectionStart = new AnchorSegment(Document, currentSeg.Offset, currentSeg.Length);
185          if ((Keyboard.Modifiers & ModifierKeys.Shift) == ModifierKeys.Shift) {
186            SimpleSelection simpleSelection = textArea.Selection as SimpleSelection;
187            if (simpleSelection != null)
188              selectionStart = new AnchorSegment(Document, simpleSelection.SurroundingSegment);
189          }
190          textArea.Selection = Selection.Create(textArea, selectionStart);
191          if ((Keyboard.Modifiers & ModifierKeys.Shift) == ModifierKeys.Shift) {
192            ExtendSelection(currentSeg);
193          }
194        }
195      }
196    }
197   
198    SimpleSegment GetTextLineSegment(MouseEventArgs e)
199    {
200      Point pos = e.GetPosition(TextView);
201      pos.X = 0;
202      pos.Y += TextView.VerticalOffset;
203      VisualLine vl = TextView.GetVisualLineFromVisualTop(pos.Y);
204      if (vl == null)
205        return SimpleSegment.Invalid;
206      TextLine tl = vl.GetTextLineByVisualYPosition(pos.Y);
207      int visualStartColumn = vl.GetTextLineVisualStartColumn(tl);
208      int visualEndColumn = visualStartColumn + tl.Length;
209      int relStart = vl.FirstDocumentLine.Offset;
210      int startOffset = vl.GetRelativeOffset(visualStartColumn) + relStart;
211      int endOffset = vl.GetRelativeOffset(visualEndColumn) + relStart;
212      if (endOffset == vl.LastDocumentLine.Offset + vl.LastDocumentLine.Length)
213        endOffset += vl.LastDocumentLine.DelimiterLength;
214      return new SimpleSegment(startOffset, endOffset - startOffset);
215    }
216   
217    void ExtendSelection(SimpleSegment currentSeg)
218    {
219      if (currentSeg.Offset < selectionStart.Offset) {
220        textArea.Caret.Offset = currentSeg.Offset;
221        textArea.Selection = Selection.Create(textArea, currentSeg.Offset, selectionStart.Offset + selectionStart.Length);
222      } else {
223        textArea.Caret.Offset = currentSeg.Offset + currentSeg.Length;
224        textArea.Selection = Selection.Create(textArea, selectionStart.Offset, currentSeg.Offset + currentSeg.Length);
225      }
226    }
227   
228    /// <inheritdoc/>
229    protected override void OnMouseMove(MouseEventArgs e)
230    {
231      if (selecting && textArea != null && TextView != null) {
232        e.Handled = true;
233        SimpleSegment currentSeg = GetTextLineSegment(e);
234        if (currentSeg == SimpleSegment.Invalid)
235          return;
236        ExtendSelection(currentSeg);
237      }
238      base.OnMouseMove(e);
239    }
240   
241    /// <inheritdoc/>
242    protected override void OnMouseLeftButtonUp(MouseButtonEventArgs e)
243    {
244      if (selecting) {
245        selecting = false;
246        selectionStart = null;
247        ReleaseMouseCapture();
248        e.Handled = true;
249      }
250      base.OnMouseLeftButtonUp(e);
251    }
252   
253    /// <inheritdoc/>
254    protected override HitTestResult HitTestCore(PointHitTestParameters hitTestParameters)
255    {
256      // accept clicks even when clicking on the background
257      return new PointHitTestResult(this, hitTestParameters.HitPoint);
258    }
259  }
260}
Note: See TracBrowser for help on using the repository browser.