/AvalonEdit/ICSharpCode.AvalonEdit/Rendering/VisualLineElement.cs

http://github.com/icsharpcode/ILSpy · C# · 264 lines · 122 code · 24 blank · 118 comment · 33 complexity · 213e195624b46029de4edc4401e1c48b MD5 · raw file

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