Free cookie consent management tool by TermsFeed Policy Generator

source: branches/2965_CancelablePersistence/HeuristicLab.ExtLibs/HeuristicLab.AvalonEdit/5.0.1/AvalonEdit-5.0.1/Highlighting/RichText.cs @ 16796

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

#2077: created branch and added first version

File size: 8.5 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.Collections.Generic;
21using System.Diagnostics;
22using System.Globalization;
23using System.IO;
24using System.Linq;
25using System.Windows.Documents;
26using ICSharpCode.AvalonEdit.Document;
27using ICSharpCode.AvalonEdit.Utils;
28
29namespace ICSharpCode.AvalonEdit.Highlighting
30{
31  /// <summary>
32  /// Represents a immutable piece text with highlighting information.
33  /// </summary>
34  public class RichText
35  {
36    /// <summary>
37    /// The empty string without any formatting information.
38    /// </summary>
39    public static readonly RichText Empty = new RichText(string.Empty);
40   
41    readonly string text;
42    internal readonly int[] stateChangeOffsets;
43    internal readonly HighlightingColor[] stateChanges;
44   
45    /// <summary>
46    /// Creates a RichText instance with the given text and RichTextModel.
47    /// </summary>
48    /// <param name="text">
49    /// The text to use in this RichText instance.
50    /// </param>
51    /// <param name="model">
52    /// The model that contains the formatting to use for this RichText instance.
53    /// <c>model.DocumentLength</c> should correspond to <c>text.Length</c>.
54    /// This parameter may be null, in which case the RichText instance just holds plain text.
55    /// </param>
56    public RichText(string text, RichTextModel model = null)
57    {
58      if (text == null)
59        throw new ArgumentNullException("text");
60      this.text = text;
61      if (model != null) {
62        var sections = model.GetHighlightedSections(0, text.Length).ToArray();
63        stateChangeOffsets = new int[sections.Length];
64        stateChanges = new HighlightingColor[sections.Length];
65        for (int i = 0; i < sections.Length; i++) {
66          stateChangeOffsets[i] = sections[i].Offset;
67          stateChanges[i] = sections[i].Color;
68        }
69      } else {
70        stateChangeOffsets = new int[] { 0 };
71        stateChanges = new HighlightingColor[] { HighlightingColor.Empty };
72      }
73    }
74   
75    internal RichText(string text, int[] offsets, HighlightingColor[] states)
76    {
77      this.text = text;
78      Debug.Assert(offsets[0] == 0);
79      Debug.Assert(offsets.Last() <= text.Length);
80      this.stateChangeOffsets = offsets;
81      this.stateChanges = states;
82    }
83   
84    /// <summary>
85    /// Gets the text.
86    /// </summary>
87    public string Text {
88      get { return text; }
89    }
90   
91    /// <summary>
92    /// Gets the text length.
93    /// </summary>
94    public int Length {
95      get { return text.Length; }
96    }
97   
98    int GetIndexForOffset(int offset)
99    {
100      if (offset < 0 || offset > text.Length)
101        throw new ArgumentOutOfRangeException("offset");
102      int index = Array.BinarySearch(stateChangeOffsets, offset);
103      if (index < 0) {
104        // If no color change exists directly at offset,
105        // return the index of the color segment that contains offset.
106        index = ~index - 1;
107      }
108      return index;
109    }
110   
111    int GetEnd(int index)
112    {
113      // Gets the end of the color segment no. index.
114      if (index + 1 < stateChangeOffsets.Length)
115        return stateChangeOffsets[index + 1];
116      else
117        return text.Length;
118    }
119   
120    /// <summary>
121    /// Gets the HighlightingColor for the specified offset.
122    /// </summary>
123    public HighlightingColor GetHighlightingAt(int offset)
124    {
125      return stateChanges[GetIndexForOffset(offset)];
126    }
127   
128    /// <summary>
129    /// Retrieves the highlighted sections in the specified range.
130    /// The highlighted sections will be sorted by offset, and there will not be any nested or overlapping sections.
131    /// </summary>
132    public IEnumerable<HighlightedSection> GetHighlightedSections(int offset, int length)
133    {
134      int index = GetIndexForOffset(offset);
135      int pos = offset;
136      int endOffset = offset + length;
137      while (pos < endOffset) {
138        int endPos = Math.Min(endOffset, GetEnd(index));
139        yield return new HighlightedSection {
140          Offset = pos,
141          Length = endPos - pos,
142          Color = stateChanges[index]
143        };
144        pos = endPos;
145        index++;
146      }
147    }
148   
149    /// <summary>
150    /// Creates a new RichTextModel with the formatting from this RichText.
151    /// </summary>
152    public RichTextModel ToRichTextModel()
153    {
154      return new RichTextModel(stateChangeOffsets, stateChanges.Select(ch => ch.Clone()).ToArray());
155    }
156   
157    /// <summary>
158    /// Gets the text.
159    /// </summary>
160    public override string ToString()
161    {
162      return text;
163    }
164   
165    /// <summary>
166    /// Creates WPF Run instances that can be used for TextBlock.Inlines.
167    /// </summary>
168    public Run[] CreateRuns()
169    {
170      Run[] runs = new Run[stateChanges.Length];
171      for (int i = 0; i < runs.Length; i++) {
172        int startOffset = stateChangeOffsets[i];
173        int endOffset = i + 1 < stateChangeOffsets.Length ? stateChangeOffsets[i + 1] : text.Length;
174        Run r = new Run(text.Substring(startOffset, endOffset - startOffset));
175        HighlightingColor state = stateChanges[i];
176        ApplyColorToTextElement(r, state);
177        runs[i] = r;
178      }
179      return runs;
180    }
181
182    internal static void ApplyColorToTextElement(TextElement r, HighlightingColor state)
183    {
184      if (state.Foreground != null)
185        r.Foreground = state.Foreground.GetBrush(null);
186      if (state.Background != null)
187        r.Background = state.Background.GetBrush(null);
188      if (state.FontWeight != null)
189        r.FontWeight = state.FontWeight.Value;
190      if (state.FontStyle != null)
191        r.FontStyle = state.FontStyle.Value;
192    }
193   
194    /// <summary>
195    /// Produces HTML code for the line, with &lt;span style="..."&gt; tags.
196    /// </summary>
197    public string ToHtml(HtmlOptions options = null)
198    {
199      StringWriter stringWriter = new StringWriter(CultureInfo.InvariantCulture);
200      using (var htmlWriter = new HtmlRichTextWriter(stringWriter, options)) {
201        htmlWriter.Write(this);
202      }
203      return stringWriter.ToString();
204    }
205   
206    /// <summary>
207    /// Produces HTML code for a section of the line, with &lt;span style="..."&gt; tags.
208    /// </summary>
209    public string ToHtml(int offset, int length, HtmlOptions options = null)
210    {
211      StringWriter stringWriter = new StringWriter(CultureInfo.InvariantCulture);
212      using (var htmlWriter = new HtmlRichTextWriter(stringWriter, options)) {
213        htmlWriter.Write(this, offset, length);
214      }
215      return stringWriter.ToString();
216    }
217   
218    /// <summary>
219    /// Creates a substring of this rich text.
220    /// </summary>
221    public RichText Substring(int offset, int length)
222    {
223      if (offset == 0 && length == this.Length)
224        return this;
225      string newText = text.Substring(offset, length);
226      RichTextModel model = ToRichTextModel();
227      OffsetChangeMap map = new OffsetChangeMap(2);
228      map.Add(new OffsetChangeMapEntry(offset + length, text.Length - offset - length, 0));
229      map.Add(new OffsetChangeMapEntry(0, offset, 0));
230      model.UpdateOffsets(map);
231      return new RichText(newText, model);
232    }
233   
234    /// <summary>
235    /// Concatenates the specified rich texts.
236    /// </summary>
237    public static RichText Concat(params RichText[] texts)
238    {
239      if (texts == null || texts.Length == 0)
240        return Empty;
241      else if (texts.Length == 1)
242        return texts[0];
243      string newText = string.Concat(texts.Select(txt => txt.text));
244      RichTextModel model = texts[0].ToRichTextModel();
245      int offset = texts[0].Length;
246      for (int i = 1; i < texts.Length; i++) {
247        model.Append(offset, texts[i].stateChangeOffsets, texts[i].stateChanges);
248        offset += texts[i].Length;
249      }
250      return new RichText(newText, model);
251    }
252   
253    /// <summary>
254    /// Concatenates the specified rich texts.
255    /// </summary>
256    public static RichText operator +(RichText a, RichText b)
257    {
258      return RichText.Concat(a, b);
259    }
260   
261    /// <summary>
262    /// Implicit conversion from string to RichText.
263    /// </summary>
264    public static implicit operator RichText(string text)
265    {
266      if (text != null)
267        return new RichText(text);
268      else
269        return null;
270    }
271  }
272}
Note: See TracBrowser for help on using the repository browser.