The JUCE cross-platform C++ framework, with DISTRHO/KXStudio specific changes
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

352 lines
11KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2017 - ROLI Ltd.
  5. JUCE is an open source library subject to commercial or open-source
  6. licensing.
  7. By using JUCE, you agree to the terms of both the JUCE 5 End-User License
  8. Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
  9. 27th April 2017).
  10. End User License Agreement: www.juce.com/juce-5-licence
  11. Privacy Policy: www.juce.com/juce-5-privacy-policy
  12. Or: You may also use this code under the terms of the GPL v3 (see
  13. www.gnu.org/licenses).
  14. JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
  15. EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
  16. DISCLAIMED.
  17. ==============================================================================
  18. */
  19. namespace
  20. {
  21. int getLength (const Array<AttributedString::Attribute>& atts) noexcept
  22. {
  23. return atts.size() != 0 ? atts.getReference (atts.size() - 1).range.getEnd() : 0;
  24. }
  25. void splitAttributeRanges (Array<AttributedString::Attribute>& atts, int position)
  26. {
  27. for (int i = atts.size(); --i >= 0;)
  28. {
  29. const AttributedString::Attribute& att = atts.getReference (i);
  30. const int offset = position - att.range.getStart();
  31. if (offset >= 0)
  32. {
  33. if (offset > 0 && position < att.range.getEnd())
  34. {
  35. atts.insert (i + 1, att);
  36. atts.getReference (i).range.setEnd (position);
  37. atts.getReference (i + 1).range.setStart (position);
  38. }
  39. break;
  40. }
  41. }
  42. }
  43. Range<int> splitAttributeRanges (Array<AttributedString::Attribute>& atts, Range<int> newRange)
  44. {
  45. newRange = newRange.getIntersectionWith (Range<int> (0, getLength (atts)));
  46. if (! newRange.isEmpty())
  47. {
  48. splitAttributeRanges (atts, newRange.getStart());
  49. splitAttributeRanges (atts, newRange.getEnd());
  50. }
  51. return newRange;
  52. }
  53. void mergeAdjacentRanges (Array<AttributedString::Attribute>& atts)
  54. {
  55. for (int i = atts.size() - 1; --i >= 0;)
  56. {
  57. AttributedString::Attribute& a1 = atts.getReference (i);
  58. AttributedString::Attribute& a2 = atts.getReference (i + 1);
  59. if (a1.colour == a2.colour && a1.font == a2.font)
  60. {
  61. a1.range.setEnd (a2.range.getEnd());
  62. atts.remove (i + 1);
  63. if (i < atts.size() - 1)
  64. ++i;
  65. }
  66. }
  67. }
  68. void appendRange (Array<AttributedString::Attribute>& atts,
  69. int length, const Font* f, const Colour* c)
  70. {
  71. if (atts.size() == 0)
  72. {
  73. atts.add (AttributedString::Attribute (Range<int> (0, length),
  74. f != nullptr ? *f : Font(),
  75. c != nullptr ? *c : Colour (0xff000000)));
  76. }
  77. else
  78. {
  79. const int start = getLength (atts);
  80. atts.add (AttributedString::Attribute (Range<int> (start, start + length),
  81. f != nullptr ? *f : atts.getReference (atts.size() - 1).font,
  82. c != nullptr ? *c : atts.getReference (atts.size() - 1).colour));
  83. mergeAdjacentRanges (atts);
  84. }
  85. }
  86. void applyFontAndColour (Array<AttributedString::Attribute>& atts,
  87. Range<int> range, const Font* f, const Colour* c)
  88. {
  89. range = splitAttributeRanges (atts, range);
  90. for (int i = 0; i < atts.size(); ++i)
  91. {
  92. AttributedString::Attribute& att = atts.getReference (i);
  93. if (range.getStart() < att.range.getEnd())
  94. {
  95. if (range.getEnd() <= att.range.getStart())
  96. break;
  97. if (c != nullptr) att.colour = *c;
  98. if (f != nullptr) att.font = *f;
  99. }
  100. }
  101. mergeAdjacentRanges (atts);
  102. }
  103. void truncate (Array<AttributedString::Attribute>& atts, int newLength)
  104. {
  105. splitAttributeRanges (atts, newLength);
  106. for (int i = atts.size(); --i >= 0;)
  107. if (atts.getReference (i).range.getStart() >= newLength)
  108. atts.remove (i);
  109. }
  110. }
  111. //==============================================================================
  112. AttributedString::Attribute::Attribute() noexcept : colour (0xff000000) {}
  113. AttributedString::Attribute::~Attribute() noexcept {}
  114. AttributedString::Attribute::Attribute (Attribute&& other) noexcept
  115. : range (other.range),
  116. font (static_cast<Font&&> (other.font)),
  117. colour (other.colour)
  118. {
  119. }
  120. AttributedString::Attribute& AttributedString::Attribute::operator= (Attribute&& other) noexcept
  121. {
  122. range = other.range;
  123. font = static_cast<Font&&> (other.font);
  124. colour = other.colour;
  125. return *this;
  126. }
  127. AttributedString::Attribute::Attribute (const Attribute& other) noexcept
  128. : range (other.range),
  129. font (other.font),
  130. colour (other.colour)
  131. {
  132. }
  133. AttributedString::Attribute& AttributedString::Attribute::operator= (const Attribute& other) noexcept
  134. {
  135. range = other.range;
  136. font = other.font;
  137. colour = other.colour;
  138. return *this;
  139. }
  140. AttributedString::Attribute::Attribute (Range<int> r, const Font& f, Colour c) noexcept
  141. : range (r), font (f), colour (c)
  142. {
  143. }
  144. //==============================================================================
  145. AttributedString::AttributedString()
  146. : lineSpacing (0.0f),
  147. justification (Justification::left),
  148. wordWrap (AttributedString::byWord),
  149. readingDirection (AttributedString::natural)
  150. {
  151. }
  152. AttributedString::AttributedString (const String& newString)
  153. : lineSpacing (0.0f),
  154. justification (Justification::left),
  155. wordWrap (AttributedString::byWord),
  156. readingDirection (AttributedString::natural)
  157. {
  158. setText (newString);
  159. }
  160. AttributedString::AttributedString (const AttributedString& other)
  161. : text (other.text),
  162. lineSpacing (other.lineSpacing),
  163. justification (other.justification),
  164. wordWrap (other.wordWrap),
  165. readingDirection (other.readingDirection),
  166. attributes (other.attributes)
  167. {
  168. }
  169. AttributedString& AttributedString::operator= (const AttributedString& other)
  170. {
  171. if (this != &other)
  172. {
  173. text = other.text;
  174. lineSpacing = other.lineSpacing;
  175. justification = other.justification;
  176. wordWrap = other.wordWrap;
  177. readingDirection = other.readingDirection;
  178. attributes = other.attributes;
  179. }
  180. return *this;
  181. }
  182. AttributedString::AttributedString (AttributedString&& other) noexcept
  183. : text (static_cast<String&&> (other.text)),
  184. lineSpacing (other.lineSpacing),
  185. justification (other.justification),
  186. wordWrap (other.wordWrap),
  187. readingDirection (other.readingDirection),
  188. attributes (static_cast<Array<Attribute>&&> (other.attributes))
  189. {
  190. }
  191. AttributedString& AttributedString::operator= (AttributedString&& other) noexcept
  192. {
  193. text = static_cast<String&&> (other.text);
  194. lineSpacing = other.lineSpacing;
  195. justification = other.justification;
  196. wordWrap = other.wordWrap;
  197. readingDirection = other.readingDirection;
  198. attributes = static_cast<Array<Attribute>&&> (other.attributes);
  199. return *this;
  200. }
  201. AttributedString::~AttributedString() noexcept {}
  202. void AttributedString::setText (const String& newText)
  203. {
  204. const int newLength = newText.length();
  205. const int oldLength = getLength (attributes);
  206. if (newLength > oldLength)
  207. appendRange (attributes, newLength - oldLength, nullptr, nullptr);
  208. else if (newLength < oldLength)
  209. truncate (attributes, newLength);
  210. text = newText;
  211. }
  212. void AttributedString::append (const String& textToAppend)
  213. {
  214. text += textToAppend;
  215. appendRange (attributes, textToAppend.length(), nullptr, nullptr);
  216. }
  217. void AttributedString::append (const String& textToAppend, const Font& font)
  218. {
  219. text += textToAppend;
  220. appendRange (attributes, textToAppend.length(), &font, nullptr);
  221. }
  222. void AttributedString::append (const String& textToAppend, Colour colour)
  223. {
  224. text += textToAppend;
  225. appendRange (attributes, textToAppend.length(), nullptr, &colour);
  226. }
  227. void AttributedString::append (const String& textToAppend, const Font& font, Colour colour)
  228. {
  229. text += textToAppend;
  230. appendRange (attributes, textToAppend.length(), &font, &colour);
  231. }
  232. void AttributedString::append (const AttributedString& other)
  233. {
  234. const int originalLength = getLength (attributes);
  235. const int originalNumAtts = attributes.size();
  236. text += other.text;
  237. attributes.addArray (other.attributes);
  238. for (int i = originalNumAtts; i < attributes.size(); ++i)
  239. attributes.getReference (i).range += originalLength;
  240. mergeAdjacentRanges (attributes);
  241. }
  242. void AttributedString::clear()
  243. {
  244. text.clear();
  245. attributes.clear();
  246. }
  247. void AttributedString::setJustification (Justification newJustification) noexcept
  248. {
  249. justification = newJustification;
  250. }
  251. void AttributedString::setWordWrap (WordWrap newWordWrap) noexcept
  252. {
  253. wordWrap = newWordWrap;
  254. }
  255. void AttributedString::setReadingDirection (ReadingDirection newReadingDirection) noexcept
  256. {
  257. readingDirection = newReadingDirection;
  258. }
  259. void AttributedString::setLineSpacing (const float newLineSpacing) noexcept
  260. {
  261. lineSpacing = newLineSpacing;
  262. }
  263. void AttributedString::setColour (Range<int> range, Colour colour)
  264. {
  265. applyFontAndColour (attributes, range, nullptr, &colour);
  266. }
  267. void AttributedString::setFont (Range<int> range, const Font& font)
  268. {
  269. applyFontAndColour (attributes, range, &font, nullptr);
  270. }
  271. void AttributedString::setColour (Colour colour)
  272. {
  273. setColour (Range<int> (0, getLength (attributes)), colour);
  274. }
  275. void AttributedString::setFont (const Font& font)
  276. {
  277. setFont (Range<int> (0, getLength (attributes)), font);
  278. }
  279. void AttributedString::draw (Graphics& g, const Rectangle<float>& area) const
  280. {
  281. if (text.isNotEmpty() && g.clipRegionIntersects (area.getSmallestIntegerContainer()))
  282. {
  283. jassert (text.length() == getLength (attributes));
  284. if (! g.getInternalContext().drawTextLayout (*this, area))
  285. {
  286. TextLayout layout;
  287. layout.createLayout (*this, area.getWidth());
  288. layout.draw (g, area);
  289. }
  290. }
  291. }