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 | |
---|
19 | using System; |
---|
20 | using System.Collections.Generic; |
---|
21 | using System.Windows.Documents; |
---|
22 | using System.Windows.Input; |
---|
23 | using System.Windows.Media; |
---|
24 | using System.Windows.Media.TextFormatting; |
---|
25 | |
---|
26 | using ICSharpCode.AvalonEdit.Document; |
---|
27 | |
---|
28 | namespace ICSharpCode.AvalonEdit.Rendering |
---|
29 | { |
---|
30 | /// <summary> |
---|
31 | /// Represents a visual element in the document. |
---|
32 | /// </summary> |
---|
33 | public abstract class VisualLineElement |
---|
34 | { |
---|
35 | /// <summary> |
---|
36 | /// Creates a new VisualLineElement. |
---|
37 | /// </summary> |
---|
38 | /// <param name="visualLength">The length of the element in VisualLine coordinates. Must be positive.</param> |
---|
39 | /// <param name="documentLength">The length of the element in the document. Must be non-negative.</param> |
---|
40 | protected VisualLineElement(int visualLength, int documentLength) |
---|
41 | { |
---|
42 | if (visualLength < 1) |
---|
43 | throw new ArgumentOutOfRangeException("visualLength", visualLength, "Value must be at least 1"); |
---|
44 | if (documentLength < 0) |
---|
45 | throw new ArgumentOutOfRangeException("documentLength", documentLength, "Value must be at least 0"); |
---|
46 | this.VisualLength = visualLength; |
---|
47 | this.DocumentLength = documentLength; |
---|
48 | } |
---|
49 | |
---|
50 | /// <summary> |
---|
51 | /// Gets the length of this element in visual columns. |
---|
52 | /// </summary> |
---|
53 | public int VisualLength { get; private set; } |
---|
54 | |
---|
55 | /// <summary> |
---|
56 | /// Gets the length of this element in the text document. |
---|
57 | /// </summary> |
---|
58 | public int DocumentLength { get; private set; } |
---|
59 | |
---|
60 | /// <summary> |
---|
61 | /// Gets the visual column where this element starts. |
---|
62 | /// </summary> |
---|
63 | [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1721:PropertyNamesShouldNotMatchGetMethods", |
---|
64 | Justification = "This property holds the start visual column, use GetVisualColumn to get inner visual columns.")] |
---|
65 | public int VisualColumn { get; internal set; } |
---|
66 | |
---|
67 | /// <summary> |
---|
68 | /// Gets the text offset where this element starts, relative to the start text offset of the visual line. |
---|
69 | /// </summary> |
---|
70 | public int RelativeTextOffset { get; internal set; } |
---|
71 | |
---|
72 | /// <summary> |
---|
73 | /// Gets the text run properties. |
---|
74 | /// A unique <see cref="VisualLineElementTextRunProperties"/> instance is used for each |
---|
75 | /// <see cref="VisualLineElement"/>; colorizing code may assume that modifying the |
---|
76 | /// <see cref="VisualLineElementTextRunProperties"/> will affect only this |
---|
77 | /// <see cref="VisualLineElement"/>. |
---|
78 | /// </summary> |
---|
79 | public VisualLineElementTextRunProperties TextRunProperties { get; private set; } |
---|
80 | |
---|
81 | /// <summary> |
---|
82 | /// Gets/sets the brush used for the background of this <see cref="VisualLineElement" />. |
---|
83 | /// </summary> |
---|
84 | public Brush BackgroundBrush { get; set; } |
---|
85 | |
---|
86 | internal void SetTextRunProperties(VisualLineElementTextRunProperties p) |
---|
87 | { |
---|
88 | this.TextRunProperties = p; |
---|
89 | } |
---|
90 | |
---|
91 | /// <summary> |
---|
92 | /// Creates the TextRun for this line element. |
---|
93 | /// </summary> |
---|
94 | /// <param name="startVisualColumn"> |
---|
95 | /// The visual column from which the run should be constructed. |
---|
96 | /// Normally the same value as the <see cref="VisualColumn"/> property is used to construct the full run; |
---|
97 | /// but when word-wrapping is active, partial runs might be created. |
---|
98 | /// </param> |
---|
99 | /// <param name="context"> |
---|
100 | /// Context object that contains information relevant for text run creation. |
---|
101 | /// </param> |
---|
102 | public abstract TextRun CreateTextRun(int startVisualColumn, ITextRunConstructionContext context); |
---|
103 | |
---|
104 | /// <summary> |
---|
105 | /// Retrieves the text span immediately before the visual column. |
---|
106 | /// </summary> |
---|
107 | /// <remarks>This method is used for word-wrapping in bidirectional text.</remarks> |
---|
108 | public virtual TextSpan<CultureSpecificCharacterBufferRange> GetPrecedingText(int visualColumnLimit, ITextRunConstructionContext context) |
---|
109 | { |
---|
110 | return null; |
---|
111 | } |
---|
112 | |
---|
113 | /// <summary> |
---|
114 | /// Gets if this VisualLineElement can be split. |
---|
115 | /// </summary> |
---|
116 | public virtual bool CanSplit { |
---|
117 | get { return false; } |
---|
118 | } |
---|
119 | |
---|
120 | /// <summary> |
---|
121 | /// Splits the element. |
---|
122 | /// </summary> |
---|
123 | /// <param name="splitVisualColumn">Position inside this element at which it should be broken</param> |
---|
124 | /// <param name="elements">The collection of line elements</param> |
---|
125 | /// <param name="elementIndex">The index at which this element is in the elements list.</param> |
---|
126 | public virtual void Split(int splitVisualColumn, IList<VisualLineElement> elements, int elementIndex) |
---|
127 | { |
---|
128 | throw new NotSupportedException(); |
---|
129 | } |
---|
130 | |
---|
131 | /// <summary> |
---|
132 | /// Helper method for splitting this line element into two, correctly updating the |
---|
133 | /// <see cref="VisualLength"/>, <see cref="DocumentLength"/>, <see cref="VisualColumn"/> |
---|
134 | /// and <see cref="RelativeTextOffset"/> properties. |
---|
135 | /// </summary> |
---|
136 | /// <param name="firstPart">The element before the split position.</param> |
---|
137 | /// <param name="secondPart">The element after the split position.</param> |
---|
138 | /// <param name="splitVisualColumn">The split position as visual column.</param> |
---|
139 | /// <param name="splitRelativeTextOffset">The split position as text offset.</param> |
---|
140 | protected void SplitHelper(VisualLineElement firstPart, VisualLineElement secondPart, int splitVisualColumn, int splitRelativeTextOffset) |
---|
141 | { |
---|
142 | if (firstPart == null) |
---|
143 | throw new ArgumentNullException("firstPart"); |
---|
144 | if (secondPart == null) |
---|
145 | throw new ArgumentNullException("secondPart"); |
---|
146 | int relativeSplitVisualColumn = splitVisualColumn - VisualColumn; |
---|
147 | int relativeSplitRelativeTextOffset = splitRelativeTextOffset - RelativeTextOffset; |
---|
148 | |
---|
149 | if (relativeSplitVisualColumn <= 0 || relativeSplitVisualColumn >= VisualLength) |
---|
150 | throw new ArgumentOutOfRangeException("splitVisualColumn", splitVisualColumn, "Value must be between " + (VisualColumn + 1) + " and " + (VisualColumn + VisualLength - 1)); |
---|
151 | if (relativeSplitRelativeTextOffset < 0 || relativeSplitRelativeTextOffset > DocumentLength) |
---|
152 | throw new ArgumentOutOfRangeException("splitRelativeTextOffset", splitRelativeTextOffset, "Value must be between " + (RelativeTextOffset) + " and " + (RelativeTextOffset + DocumentLength)); |
---|
153 | int oldVisualLength = VisualLength; |
---|
154 | int oldDocumentLength = DocumentLength; |
---|
155 | int oldVisualColumn = VisualColumn; |
---|
156 | int oldRelativeTextOffset = RelativeTextOffset; |
---|
157 | firstPart.VisualColumn = oldVisualColumn; |
---|
158 | secondPart.VisualColumn = oldVisualColumn + relativeSplitVisualColumn; |
---|
159 | firstPart.RelativeTextOffset = oldRelativeTextOffset; |
---|
160 | secondPart.RelativeTextOffset = oldRelativeTextOffset + relativeSplitRelativeTextOffset; |
---|
161 | firstPart.VisualLength = relativeSplitVisualColumn; |
---|
162 | secondPart.VisualLength = oldVisualLength - relativeSplitVisualColumn; |
---|
163 | firstPart.DocumentLength = relativeSplitRelativeTextOffset; |
---|
164 | secondPart.DocumentLength = oldDocumentLength - relativeSplitRelativeTextOffset; |
---|
165 | if (firstPart.TextRunProperties == null) |
---|
166 | firstPart.TextRunProperties = TextRunProperties.Clone(); |
---|
167 | if (secondPart.TextRunProperties == null) |
---|
168 | secondPart.TextRunProperties = TextRunProperties.Clone(); |
---|
169 | } |
---|
170 | |
---|
171 | /// <summary> |
---|
172 | /// Gets the visual column of a text location inside this element. |
---|
173 | /// The text offset is given relative to the visual line start. |
---|
174 | /// </summary> |
---|
175 | public virtual int GetVisualColumn(int relativeTextOffset) |
---|
176 | { |
---|
177 | if (relativeTextOffset >= this.RelativeTextOffset + DocumentLength) |
---|
178 | return VisualColumn + VisualLength; |
---|
179 | else |
---|
180 | return VisualColumn; |
---|
181 | } |
---|
182 | |
---|
183 | /// <summary> |
---|
184 | /// Gets the text offset of a visual column inside this element. |
---|
185 | /// </summary> |
---|
186 | /// <returns>A text offset relative to the visual line start.</returns> |
---|
187 | public virtual int GetRelativeOffset(int visualColumn) |
---|
188 | { |
---|
189 | if (visualColumn >= this.VisualColumn + VisualLength) |
---|
190 | return RelativeTextOffset + DocumentLength; |
---|
191 | else |
---|
192 | return RelativeTextOffset; |
---|
193 | } |
---|
194 | |
---|
195 | /// <summary> |
---|
196 | /// Gets the next caret position inside this element. |
---|
197 | /// </summary> |
---|
198 | /// <param name="visualColumn">The visual column from which the search should be started.</param> |
---|
199 | /// <param name="direction">The search direction (forwards or backwards).</param> |
---|
200 | /// <param name="mode">Whether to stop only at word borders.</param> |
---|
201 | /// <returns>The visual column of the next caret position, or -1 if there is no next caret position.</returns> |
---|
202 | /// <remarks> |
---|
203 | /// In the space between two line elements, it is sufficient that one of them contains a caret position; |
---|
204 | /// though in many cases, both of them contain one. |
---|
205 | /// </remarks> |
---|
206 | public virtual int GetNextCaretPosition(int visualColumn, LogicalDirection direction, CaretPositioningMode mode) |
---|
207 | { |
---|
208 | int stop1 = this.VisualColumn; |
---|
209 | int stop2 = this.VisualColumn + this.VisualLength; |
---|
210 | if (direction == LogicalDirection.Backward) { |
---|
211 | if (visualColumn > stop2 && mode != CaretPositioningMode.WordStart && mode != CaretPositioningMode.WordStartOrSymbol) |
---|
212 | return stop2; |
---|
213 | else if (visualColumn > stop1) |
---|
214 | return stop1; |
---|
215 | } else { |
---|
216 | if (visualColumn < stop1) |
---|
217 | return stop1; |
---|
218 | else if (visualColumn < stop2 && mode != CaretPositioningMode.WordStart && mode != CaretPositioningMode.WordStartOrSymbol) |
---|
219 | return stop2; |
---|
220 | } |
---|
221 | return -1; |
---|
222 | } |
---|
223 | |
---|
224 | /// <summary> |
---|
225 | /// Gets whether the specified offset in this element is considered whitespace. |
---|
226 | /// </summary> |
---|
227 | public virtual bool IsWhitespace(int visualColumn) |
---|
228 | { |
---|
229 | return false; |
---|
230 | } |
---|
231 | |
---|
232 | /// <summary> |
---|
233 | /// Gets whether the <see cref="GetNextCaretPosition"/> implementation handles line borders. |
---|
234 | /// If this property returns false, the caller of GetNextCaretPosition should handle the line |
---|
235 | /// borders (i.e. place caret stops at the start and end of the line). |
---|
236 | /// This property has an effect only for VisualLineElements that are at the start or end of a |
---|
237 | /// <see cref="VisualLine"/>. |
---|
238 | /// </summary> |
---|
239 | public virtual bool HandlesLineBorders { |
---|
240 | get { return false; } |
---|
241 | } |
---|
242 | |
---|
243 | /// <summary> |
---|
244 | /// Queries the cursor over the visual line element. |
---|
245 | /// </summary> |
---|
246 | protected internal virtual void OnQueryCursor(QueryCursorEventArgs e) |
---|
247 | { |
---|
248 | } |
---|
249 | |
---|
250 | /// <summary> |
---|
251 | /// Allows the visual line element to handle a mouse event. |
---|
252 | /// </summary> |
---|
253 | protected internal virtual void OnMouseDown(MouseButtonEventArgs e) |
---|
254 | { |
---|
255 | } |
---|
256 | |
---|
257 | /// <summary> |
---|
258 | /// Allows the visual line element to handle a mouse event. |
---|
259 | /// </summary> |
---|
260 | protected internal virtual void OnMouseUp(MouseButtonEventArgs e) |
---|
261 | { |
---|
262 | } |
---|
263 | } |
---|
264 | } |
---|