Free cookie consent management tool by TermsFeed Policy Generator

source: branches/CodeEditor/HeuristicLab.ExtLibs/HeuristicLab.AvalonEdit/5.0.1/AvalonEdit-5.0.1/Rendering/SingleCharacterElementGenerator.cs @ 11700

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

#2077: created branch and added first version

File size: 9.6 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.Windows;
21using System.Windows.Documents;
22using System.Windows.Media;
23using System.Windows.Media.TextFormatting;
24
25using ICSharpCode.AvalonEdit.Document;
26using ICSharpCode.AvalonEdit.Utils;
27
28namespace ICSharpCode.AvalonEdit.Rendering
29{
30  // This class is internal because it does not need to be accessed by the user - it can be configured using TextEditorOptions.
31 
32  /// <summary>
33  /// Element generator that displays · for spaces and » for tabs and a box for control characters.
34  /// </summary>
35  /// <remarks>
36  /// This element generator is present in every TextView by default; the enabled features can be configured using the
37  /// <see cref="TextEditorOptions"/>.
38  /// </remarks>
39  [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly", MessageId = "Whitespace")]
40  sealed class SingleCharacterElementGenerator : VisualLineElementGenerator, IBuiltinElementGenerator
41  {
42    /// <summary>
43    /// Gets/Sets whether to show · for spaces.
44    /// </summary>
45    public bool ShowSpaces { get; set; }
46   
47    /// <summary>
48    /// Gets/Sets whether to show » for tabs.
49    /// </summary>
50    public bool ShowTabs { get; set; }
51   
52    /// <summary>
53    /// Gets/Sets whether to show a box with the hex code for control characters.
54    /// </summary>
55    public bool ShowBoxForControlCharacters { get; set; }
56   
57    /// <summary>
58    /// Creates a new SingleCharacterElementGenerator instance.
59    /// </summary>
60    public SingleCharacterElementGenerator()
61    {
62      this.ShowSpaces = true;
63      this.ShowTabs = true;
64      this.ShowBoxForControlCharacters = true;
65    }
66   
67    void IBuiltinElementGenerator.FetchOptions(TextEditorOptions options)
68    {
69      this.ShowSpaces = options.ShowSpaces;
70      this.ShowTabs = options.ShowTabs;
71      this.ShowBoxForControlCharacters = options.ShowBoxForControlCharacters;
72    }
73   
74    public override int GetFirstInterestedOffset(int startOffset)
75    {
76      DocumentLine endLine = CurrentContext.VisualLine.LastDocumentLine;
77      StringSegment relevantText = CurrentContext.GetText(startOffset, endLine.EndOffset - startOffset);
78     
79      for (int i = 0; i < relevantText.Count; i++) {
80        char c = relevantText.Text[relevantText.Offset + i];
81        switch (c) {
82          case ' ':
83            if (ShowSpaces)
84              return startOffset + i;
85            break;
86          case '\t':
87            if (ShowTabs)
88              return startOffset + i;
89            break;
90          default:
91            if (ShowBoxForControlCharacters && char.IsControl(c)) {
92              return startOffset + i;
93            }
94            break;
95        }
96      }
97      return -1;
98    }
99   
100    public override VisualLineElement ConstructElement(int offset)
101    {
102      char c = CurrentContext.Document.GetCharAt(offset);
103      if (ShowSpaces && c == ' ') {
104        return new SpaceTextElement(CurrentContext.TextView.cachedElements.GetTextForNonPrintableCharacter("\u00B7", CurrentContext));
105      } else if (ShowTabs && c == '\t') {
106        return new TabTextElement(CurrentContext.TextView.cachedElements.GetTextForNonPrintableCharacter("\u00BB", CurrentContext));
107      } else if (ShowBoxForControlCharacters && char.IsControl(c)) {
108        var p = new VisualLineElementTextRunProperties(CurrentContext.GlobalTextRunProperties);
109        p.SetForegroundBrush(Brushes.White);
110        var textFormatter = TextFormatterFactory.Create(CurrentContext.TextView);
111        var text = FormattedTextElement.PrepareText(textFormatter,
112                                                    TextUtilities.GetControlCharacterName(c), p);
113        return new SpecialCharacterBoxElement(text);
114      } else {
115        return null;
116      }
117    }
118   
119    sealed class SpaceTextElement : FormattedTextElement
120    {
121      public SpaceTextElement(TextLine textLine) : base(textLine, 1)
122      {
123        BreakBefore = LineBreakCondition.BreakPossible;
124        BreakAfter = LineBreakCondition.BreakDesired;
125      }
126     
127      public override int GetNextCaretPosition(int visualColumn, LogicalDirection direction, CaretPositioningMode mode)
128      {
129        if (mode == CaretPositioningMode.Normal || mode == CaretPositioningMode.EveryCodepoint)
130          return base.GetNextCaretPosition(visualColumn, direction, mode);
131        else
132          return -1;
133      }
134     
135      public override bool IsWhitespace(int visualColumn)
136      {
137        return true;
138      }
139    }
140   
141    sealed class TabTextElement : VisualLineElement
142    {
143      internal readonly TextLine text;
144     
145      public TabTextElement(TextLine text) : base(2, 1)
146      {
147        this.text = text;
148      }
149     
150      public override TextRun CreateTextRun(int startVisualColumn, ITextRunConstructionContext context)
151      {
152        // the TabTextElement consists of two TextRuns:
153        // first a TabGlyphRun, then TextCharacters '\t' to let WPF handle the tab indentation
154        if (startVisualColumn == this.VisualColumn)
155          return new TabGlyphRun(this, this.TextRunProperties);
156        else if (startVisualColumn == this.VisualColumn + 1)
157          return new TextCharacters("\t", 0, 1, this.TextRunProperties);
158        else
159          throw new ArgumentOutOfRangeException("startVisualColumn");
160      }
161     
162      public override int GetNextCaretPosition(int visualColumn, LogicalDirection direction, CaretPositioningMode mode)
163      {
164        if (mode == CaretPositioningMode.Normal || mode == CaretPositioningMode.EveryCodepoint)
165          return base.GetNextCaretPosition(visualColumn, direction, mode);
166        else
167          return -1;
168      }
169     
170      public override bool IsWhitespace(int visualColumn)
171      {
172        return true;
173      }
174    }
175   
176    sealed class TabGlyphRun : TextEmbeddedObject
177    {
178      readonly TabTextElement element;
179      TextRunProperties properties;
180     
181      public TabGlyphRun(TabTextElement element, TextRunProperties properties)
182      {
183        if (properties == null)
184          throw new ArgumentNullException("properties");
185        this.properties = properties;
186        this.element = element;
187      }
188     
189      public override LineBreakCondition BreakBefore {
190        get { return LineBreakCondition.BreakPossible; }
191      }
192     
193      public override LineBreakCondition BreakAfter {
194        get { return LineBreakCondition.BreakRestrained; }
195      }
196     
197      public override bool HasFixedSize {
198        get { return true; }
199      }
200     
201      public override CharacterBufferReference CharacterBufferReference {
202        get { return new CharacterBufferReference(); }
203      }
204     
205      public override int Length {
206        get { return 1; }
207      }
208     
209      public override TextRunProperties Properties {
210        get { return properties; }
211      }
212     
213      public override TextEmbeddedObjectMetrics Format(double remainingParagraphWidth)
214      {
215        double width = Math.Min(0, element.text.WidthIncludingTrailingWhitespace - 1);
216        return new TextEmbeddedObjectMetrics(width, element.text.Height, element.text.Baseline);
217      }
218     
219      public override Rect ComputeBoundingBox(bool rightToLeft, bool sideways)
220      {
221        double width = Math.Min(0, element.text.WidthIncludingTrailingWhitespace - 1);
222        return new Rect(0, 0, width, element.text.Height);
223      }
224     
225      public override void Draw(DrawingContext drawingContext, Point origin, bool rightToLeft, bool sideways)
226      {
227        origin.Y -= element.text.Baseline;
228        element.text.Draw(drawingContext, origin, InvertAxes.None);
229      }
230    }
231   
232    sealed class SpecialCharacterBoxElement : FormattedTextElement
233    {
234      public SpecialCharacterBoxElement(TextLine text) : base(text, 1)
235      {
236      }
237     
238      public override TextRun CreateTextRun(int startVisualColumn, ITextRunConstructionContext context)
239      {
240        return new SpecialCharacterTextRun(this, this.TextRunProperties);
241      }
242    }
243   
244    sealed class SpecialCharacterTextRun : FormattedTextRun
245    {
246      static readonly SolidColorBrush darkGrayBrush;
247     
248      static SpecialCharacterTextRun()
249      {
250        darkGrayBrush = new SolidColorBrush(Color.FromArgb(200, 128, 128, 128));
251        darkGrayBrush.Freeze();
252      }
253     
254      public SpecialCharacterTextRun(FormattedTextElement element, TextRunProperties properties)
255        : base(element, properties)
256      {
257      }
258     
259      public override void Draw(DrawingContext drawingContext, Point origin, bool rightToLeft, bool sideways)
260      {
261        Point newOrigin = new Point(origin.X + 1.5, origin.Y);
262        var metrics = base.Format(double.PositiveInfinity);
263        Rect r = new Rect(newOrigin.X - 0.5, newOrigin.Y - metrics.Baseline, metrics.Width + 2, metrics.Height);
264        drawingContext.DrawRoundedRectangle(darkGrayBrush, null, r, 2.5, 2.5);
265        base.Draw(drawingContext, newOrigin, rightToLeft, sideways);
266      }
267     
268      public override TextEmbeddedObjectMetrics Format(double remainingParagraphWidth)
269      {
270        TextEmbeddedObjectMetrics metrics = base.Format(remainingParagraphWidth);
271        return new TextEmbeddedObjectMetrics(metrics.Width + 3,
272                                             metrics.Height, metrics.Baseline);
273      }
274     
275      public override Rect ComputeBoundingBox(bool rightToLeft, bool sideways)
276      {
277        Rect r = base.ComputeBoundingBox(rightToLeft, sideways);
278        r.Width += 3;
279        return r;
280      }
281    }
282  }
283}
Note: See TracBrowser for help on using the repository browser.