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.

492 lines
13KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library - "Jules' Utility Class Extensions"
  4. Copyright 2004-11 by Raw Material Software Ltd.
  5. ------------------------------------------------------------------------------
  6. JUCE can be redistributed and/or modified under the terms of the GNU General
  7. Public License (Version 2), as published by the Free Software Foundation.
  8. A copy of the license is included in the JUCE distribution, or can be found
  9. online at www.gnu.org/licenses.
  10. JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
  11. WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
  12. A PARTICULAR PURPOSE. See the GNU General Public License for more details.
  13. ------------------------------------------------------------------------------
  14. To release a closed-source product which uses JUCE, commercial licenses are
  15. available: visit www.rawmaterialsoftware.com/juce for more information.
  16. ==============================================================================
  17. */
  18. BEGIN_JUCE_NAMESPACE
  19. //==============================================================================
  20. StringArray::StringArray() noexcept
  21. {
  22. }
  23. StringArray::StringArray (const StringArray& other)
  24. : strings (other.strings)
  25. {
  26. }
  27. #if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS
  28. StringArray::StringArray (StringArray&& other) noexcept
  29. : strings (static_cast <Array <String>&&> (other.strings))
  30. {
  31. }
  32. #endif
  33. StringArray::StringArray (const String& firstValue)
  34. {
  35. strings.add (firstValue);
  36. }
  37. namespace StringArrayHelpers
  38. {
  39. template <typename CharType>
  40. void addArray (Array<String>& dest, const CharType* const* strings)
  41. {
  42. if (strings != nullptr)
  43. while (*strings != nullptr)
  44. dest.add (*strings++);
  45. }
  46. template <typename CharType>
  47. void addArray (Array<String>& dest, const CharType* const* const strings, const int numberOfStrings)
  48. {
  49. for (int i = 0; i < numberOfStrings; ++i)
  50. dest.add (strings [i]);
  51. }
  52. }
  53. StringArray::StringArray (const char* const* const initialStrings)
  54. {
  55. StringArrayHelpers::addArray (strings, initialStrings);
  56. }
  57. StringArray::StringArray (const char* const* const initialStrings, const int numberOfStrings)
  58. {
  59. StringArrayHelpers::addArray (strings, initialStrings, numberOfStrings);
  60. }
  61. StringArray::StringArray (const wchar_t* const* const initialStrings)
  62. {
  63. StringArrayHelpers::addArray (strings, initialStrings);
  64. }
  65. StringArray::StringArray (const wchar_t* const* const initialStrings, const int numberOfStrings)
  66. {
  67. StringArrayHelpers::addArray (strings, initialStrings, numberOfStrings);
  68. }
  69. StringArray& StringArray::operator= (const StringArray& other)
  70. {
  71. strings = other.strings;
  72. return *this;
  73. }
  74. #if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS
  75. StringArray& StringArray::operator= (StringArray&& other) noexcept
  76. {
  77. strings = static_cast <Array<String>&&> (other.strings);
  78. return *this;
  79. }
  80. #endif
  81. StringArray::~StringArray()
  82. {
  83. }
  84. bool StringArray::operator== (const StringArray& other) const noexcept
  85. {
  86. if (other.size() != size())
  87. return false;
  88. for (int i = size(); --i >= 0;)
  89. if (other.strings.getReference(i) != strings.getReference(i))
  90. return false;
  91. return true;
  92. }
  93. bool StringArray::operator!= (const StringArray& other) const noexcept
  94. {
  95. return ! operator== (other);
  96. }
  97. void StringArray::clear()
  98. {
  99. strings.clear();
  100. }
  101. const String& StringArray::operator[] (const int index) const noexcept
  102. {
  103. if (isPositiveAndBelow (index, strings.size()))
  104. return strings.getReference (index);
  105. return String::empty;
  106. }
  107. String& StringArray::getReference (const int index) noexcept
  108. {
  109. jassert (isPositiveAndBelow (index, strings.size()));
  110. return strings.getReference (index);
  111. }
  112. void StringArray::add (const String& newString)
  113. {
  114. strings.add (newString);
  115. }
  116. void StringArray::insert (const int index, const String& newString)
  117. {
  118. strings.insert (index, newString);
  119. }
  120. void StringArray::addIfNotAlreadyThere (const String& newString, const bool ignoreCase)
  121. {
  122. if (! contains (newString, ignoreCase))
  123. add (newString);
  124. }
  125. void StringArray::addArray (const StringArray& otherArray, int startIndex, int numElementsToAdd)
  126. {
  127. if (startIndex < 0)
  128. {
  129. jassertfalse;
  130. startIndex = 0;
  131. }
  132. if (numElementsToAdd < 0 || startIndex + numElementsToAdd > otherArray.size())
  133. numElementsToAdd = otherArray.size() - startIndex;
  134. while (--numElementsToAdd >= 0)
  135. strings.add (otherArray.strings.getReference (startIndex++));
  136. }
  137. void StringArray::set (const int index, const String& newString)
  138. {
  139. strings.set (index, newString);
  140. }
  141. bool StringArray::contains (const String& stringToLookFor, const bool ignoreCase) const
  142. {
  143. if (ignoreCase)
  144. {
  145. for (int i = size(); --i >= 0;)
  146. if (strings.getReference(i).equalsIgnoreCase (stringToLookFor))
  147. return true;
  148. }
  149. else
  150. {
  151. for (int i = size(); --i >= 0;)
  152. if (stringToLookFor == strings.getReference(i))
  153. return true;
  154. }
  155. return false;
  156. }
  157. int StringArray::indexOf (const String& stringToLookFor, const bool ignoreCase, int i) const
  158. {
  159. if (i < 0)
  160. i = 0;
  161. const int numElements = size();
  162. if (ignoreCase)
  163. {
  164. while (i < numElements)
  165. {
  166. if (strings.getReference(i).equalsIgnoreCase (stringToLookFor))
  167. return i;
  168. ++i;
  169. }
  170. }
  171. else
  172. {
  173. while (i < numElements)
  174. {
  175. if (stringToLookFor == strings.getReference (i))
  176. return i;
  177. ++i;
  178. }
  179. }
  180. return -1;
  181. }
  182. //==============================================================================
  183. void StringArray::remove (const int index)
  184. {
  185. strings.remove (index);
  186. }
  187. void StringArray::removeString (const String& stringToRemove,
  188. const bool ignoreCase)
  189. {
  190. if (ignoreCase)
  191. {
  192. for (int i = size(); --i >= 0;)
  193. if (strings.getReference(i).equalsIgnoreCase (stringToRemove))
  194. strings.remove (i);
  195. }
  196. else
  197. {
  198. for (int i = size(); --i >= 0;)
  199. if (stringToRemove == strings.getReference (i))
  200. strings.remove (i);
  201. }
  202. }
  203. void StringArray::removeRange (int startIndex, int numberToRemove)
  204. {
  205. strings.removeRange (startIndex, numberToRemove);
  206. }
  207. //==============================================================================
  208. void StringArray::removeEmptyStrings (const bool removeWhitespaceStrings)
  209. {
  210. if (removeWhitespaceStrings)
  211. {
  212. for (int i = size(); --i >= 0;)
  213. if (! strings.getReference(i).containsNonWhitespaceChars())
  214. strings.remove (i);
  215. }
  216. else
  217. {
  218. for (int i = size(); --i >= 0;)
  219. if (strings.getReference(i).isEmpty())
  220. strings.remove (i);
  221. }
  222. }
  223. void StringArray::trim()
  224. {
  225. for (int i = size(); --i >= 0;)
  226. {
  227. String& s = strings.getReference(i);
  228. s = s.trim();
  229. }
  230. }
  231. //==============================================================================
  232. struct InternalStringArrayComparator_CaseSensitive
  233. {
  234. static int compareElements (String& first, String& second) { return first.compare (second); }
  235. };
  236. struct InternalStringArrayComparator_CaseInsensitive
  237. {
  238. static int compareElements (String& first, String& second) { return first.compareIgnoreCase (second); }
  239. };
  240. void StringArray::sort (const bool ignoreCase)
  241. {
  242. if (ignoreCase)
  243. {
  244. InternalStringArrayComparator_CaseInsensitive comp;
  245. strings.sort (comp);
  246. }
  247. else
  248. {
  249. InternalStringArrayComparator_CaseSensitive comp;
  250. strings.sort (comp);
  251. }
  252. }
  253. void StringArray::move (const int currentIndex, int newIndex) noexcept
  254. {
  255. strings.move (currentIndex, newIndex);
  256. }
  257. //==============================================================================
  258. String StringArray::joinIntoString (const String& separator, int start, int numberToJoin) const
  259. {
  260. const int last = (numberToJoin < 0) ? size()
  261. : jmin (size(), start + numberToJoin);
  262. if (start < 0)
  263. start = 0;
  264. if (start >= last)
  265. return String::empty;
  266. if (start == last - 1)
  267. return strings.getReference (start);
  268. const size_t separatorBytes = separator.getCharPointer().sizeInBytes() - sizeof (String::CharPointerType::CharType);
  269. size_t bytesNeeded = separatorBytes * (last - start - 1);
  270. for (int i = start; i < last; ++i)
  271. bytesNeeded += strings.getReference(i).getCharPointer().sizeInBytes() - sizeof (String::CharPointerType::CharType);
  272. String result;
  273. result.preallocateBytes (bytesNeeded);
  274. String::CharPointerType dest (result.getCharPointer());
  275. while (start < last)
  276. {
  277. const String& s = strings.getReference (start);
  278. if (! s.isEmpty())
  279. dest.writeAll (s.getCharPointer());
  280. if (++start < last && separatorBytes > 0)
  281. dest.writeAll (separator.getCharPointer());
  282. }
  283. dest.writeNull();
  284. return result;
  285. }
  286. int StringArray::addTokens (const String& text, const bool preserveQuotedStrings)
  287. {
  288. return addTokens (text, " \n\r\t", preserveQuotedStrings ? "\"" : "");
  289. }
  290. int StringArray::addTokens (const String& text, const String& breakCharacters, const String& quoteCharacters)
  291. {
  292. int num = 0;
  293. String::CharPointerType t (text.getCharPointer());
  294. if (! t.isEmpty())
  295. {
  296. for (;;)
  297. {
  298. String::CharPointerType tokenEnd (CharacterFunctions::findEndOfToken (t,
  299. breakCharacters.getCharPointer(),
  300. quoteCharacters.getCharPointer()));
  301. add (String (t, tokenEnd));
  302. ++num;
  303. if (tokenEnd.isEmpty())
  304. break;
  305. t = ++tokenEnd;
  306. }
  307. }
  308. return num;
  309. }
  310. int StringArray::addLines (const String& sourceText)
  311. {
  312. int numLines = 0;
  313. String::CharPointerType text (sourceText.getCharPointer());
  314. bool finished = text.isEmpty();
  315. while (! finished)
  316. {
  317. String::CharPointerType startOfLine (text);
  318. size_t numChars = 0;
  319. for (;;)
  320. {
  321. const juce_wchar c = text.getAndAdvance();
  322. if (c == 0)
  323. {
  324. finished = true;
  325. break;
  326. }
  327. if (c == '\n')
  328. break;
  329. if (c == '\r')
  330. {
  331. if (*text == '\n')
  332. ++text;
  333. break;
  334. }
  335. ++numChars;
  336. }
  337. add (String (startOfLine, numChars));
  338. ++numLines;
  339. }
  340. return numLines;
  341. }
  342. //==============================================================================
  343. void StringArray::removeDuplicates (const bool ignoreCase)
  344. {
  345. for (int i = 0; i < size() - 1; ++i)
  346. {
  347. const String s (strings.getReference(i));
  348. int nextIndex = i + 1;
  349. for (;;)
  350. {
  351. nextIndex = indexOf (s, ignoreCase, nextIndex);
  352. if (nextIndex < 0)
  353. break;
  354. strings.remove (nextIndex);
  355. }
  356. }
  357. }
  358. void StringArray::appendNumbersToDuplicates (const bool ignoreCase,
  359. const bool appendNumberToFirstInstance,
  360. CharPointer_UTF8 preNumberString,
  361. CharPointer_UTF8 postNumberString)
  362. {
  363. CharPointer_UTF8 defaultPre (" ("), defaultPost (")");
  364. if (preNumberString.getAddress() == nullptr)
  365. preNumberString = defaultPre;
  366. if (postNumberString.getAddress() == nullptr)
  367. postNumberString = defaultPost;
  368. for (int i = 0; i < size() - 1; ++i)
  369. {
  370. String& s = strings.getReference(i);
  371. int nextIndex = indexOf (s, ignoreCase, i + 1);
  372. if (nextIndex >= 0)
  373. {
  374. const String original (s);
  375. int number = 0;
  376. if (appendNumberToFirstInstance)
  377. s = original + String (preNumberString) + String (++number) + String (postNumberString);
  378. else
  379. ++number;
  380. while (nextIndex >= 0)
  381. {
  382. set (nextIndex, (*this)[nextIndex] + String (preNumberString) + String (++number) + String (postNumberString));
  383. nextIndex = indexOf (original, ignoreCase, nextIndex + 1);
  384. }
  385. }
  386. }
  387. }
  388. void StringArray::minimiseStorageOverheads()
  389. {
  390. strings.minimiseStorageOverheads();
  391. }
  392. END_JUCE_NAMESPACE