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.

350 lines
11KB

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