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.

385 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. #include "../../Application/jucer_Headers.h"
  20. //==============================================================================
  21. const char* getLineEnding() { return "\r\n"; }
  22. String joinLinesIntoSourceFile (StringArray& lines)
  23. {
  24. while (lines.size() > 10 && lines [lines.size() - 1].isEmpty())
  25. lines.remove (lines.size() - 1);
  26. return lines.joinIntoString (getLineEnding()) + getLineEnding();
  27. }
  28. String trimCommentCharsFromStartOfLine (const String& line)
  29. {
  30. return line.trimStart().trimCharactersAtStart ("*/").trimStart();
  31. }
  32. String createAlphaNumericUID()
  33. {
  34. String uid;
  35. const char chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
  36. Random r;
  37. uid << chars [r.nextInt (52)]; // make sure the first character is always a letter
  38. for (int i = 5; --i >= 0;)
  39. {
  40. r.setSeedRandomly();
  41. uid << chars [r.nextInt (62)];
  42. }
  43. return uid;
  44. }
  45. String hexString8Digits (int value)
  46. {
  47. return String::toHexString (value).paddedLeft ('0', 8);
  48. }
  49. String createGUID (const String& seed)
  50. {
  51. const String hex (MD5 ((seed + "_guidsalt").toUTF8()).toHexString().toUpperCase());
  52. return "{" + hex.substring (0, 8)
  53. + "-" + hex.substring (8, 12)
  54. + "-" + hex.substring (12, 16)
  55. + "-" + hex.substring (16, 20)
  56. + "-" + hex.substring (20, 32)
  57. + "}";
  58. }
  59. String escapeSpaces (const String& s)
  60. {
  61. return s.replace (" ", "\\ ");
  62. }
  63. String addQuotesIfContainsSpaces (const String& text)
  64. {
  65. return (text.containsChar (' ') && ! text.isQuotedString()) ? text.quoted() : text;
  66. }
  67. void setValueIfVoid (Value value, const var& defaultValue)
  68. {
  69. if (value.getValue().isVoid())
  70. value = defaultValue;
  71. }
  72. //==============================================================================
  73. StringPairArray parsePreprocessorDefs (const String& text)
  74. {
  75. StringPairArray result;
  76. auto s = text.getCharPointer();
  77. while (! s.isEmpty())
  78. {
  79. String token, value;
  80. s = s.findEndOfWhitespace();
  81. while ((! s.isEmpty()) && *s != '=' && ! s.isWhitespace())
  82. token << s.getAndAdvance();
  83. s = s.findEndOfWhitespace();
  84. if (*s == '=')
  85. {
  86. ++s;
  87. while ((! s.isEmpty()) && *s == ' ')
  88. ++s;
  89. while ((! s.isEmpty()) && ! s.isWhitespace())
  90. {
  91. if (*s == ',')
  92. {
  93. ++s;
  94. break;
  95. }
  96. if (*s == '\\' && (s[1] == ' ' || s[1] == ','))
  97. ++s;
  98. value << s.getAndAdvance();
  99. }
  100. }
  101. if (token.isNotEmpty())
  102. result.set (token, value);
  103. }
  104. return result;
  105. }
  106. StringPairArray mergePreprocessorDefs (StringPairArray inheritedDefs, const StringPairArray& overridingDefs)
  107. {
  108. for (int i = 0; i < overridingDefs.size(); ++i)
  109. inheritedDefs.set (overridingDefs.getAllKeys()[i], overridingDefs.getAllValues()[i]);
  110. return inheritedDefs;
  111. }
  112. String createGCCPreprocessorFlags (const StringPairArray& defs)
  113. {
  114. String s;
  115. for (int i = 0; i < defs.size(); ++i)
  116. {
  117. auto def = defs.getAllKeys()[i];
  118. auto value = defs.getAllValues()[i];
  119. if (value.isNotEmpty())
  120. def << "=" << value;
  121. s += " -D" + def;
  122. }
  123. return s;
  124. }
  125. String replacePreprocessorDefs (const StringPairArray& definitions, String sourceString)
  126. {
  127. for (int i = 0; i < definitions.size(); ++i)
  128. {
  129. const String key (definitions.getAllKeys()[i]);
  130. const String value (definitions.getAllValues()[i]);
  131. sourceString = sourceString.replace ("${" + key + "}", value);
  132. }
  133. return sourceString;
  134. }
  135. StringArray getSearchPathsFromString (const String& searchPath)
  136. {
  137. StringArray s;
  138. s.addTokens (searchPath, ";\r\n", StringRef());
  139. return getCleanedStringArray (s);
  140. }
  141. StringArray getCommaOrWhitespaceSeparatedItems (const String& sourceString)
  142. {
  143. StringArray s;
  144. s.addTokens (sourceString, ", \t\r\n", StringRef());
  145. return getCleanedStringArray (s);
  146. }
  147. StringArray getCleanedStringArray (StringArray s)
  148. {
  149. s.trim();
  150. s.removeEmptyStrings();
  151. return s;
  152. }
  153. //==============================================================================
  154. static bool keyFoundAndNotSequentialDuplicate (XmlElement* xml, const String& key)
  155. {
  156. forEachXmlChildElementWithTagName (*xml, element, "key")
  157. {
  158. if (element->getAllSubText().trim().equalsIgnoreCase (key))
  159. {
  160. if (element->getNextElement() != nullptr && element->getNextElement()->hasTagName ("key"))
  161. {
  162. // found broken plist format (sequential duplicate), fix by removing
  163. xml->removeChildElement (element, true);
  164. return false;
  165. }
  166. // key found (not sequential duplicate)
  167. return true;
  168. }
  169. }
  170. // key not found
  171. return false;
  172. }
  173. static bool addKeyIfNotFound (XmlElement* xml, const String& key)
  174. {
  175. if (! keyFoundAndNotSequentialDuplicate (xml, key))
  176. {
  177. xml->createNewChildElement ("key")->addTextElement (key);
  178. return true;
  179. }
  180. return false;
  181. }
  182. void addPlistDictionaryKey (XmlElement* xml, const String& key, const String& value)
  183. {
  184. if (addKeyIfNotFound (xml, key))
  185. xml->createNewChildElement ("string")->addTextElement (value);
  186. }
  187. void addPlistDictionaryKeyBool (XmlElement* xml, const String& key, const bool value)
  188. {
  189. if (addKeyIfNotFound (xml, key))
  190. xml->createNewChildElement (value ? "true" : "false");
  191. }
  192. void addPlistDictionaryKeyInt (XmlElement* xml, const String& key, int value)
  193. {
  194. if (addKeyIfNotFound (xml, key))
  195. xml->createNewChildElement ("integer")->addTextElement (String (value));
  196. }
  197. //==============================================================================
  198. void autoScrollForMouseEvent (const MouseEvent& e, bool scrollX, bool scrollY)
  199. {
  200. if (Viewport* const viewport = e.eventComponent->findParentComponentOfClass<Viewport>())
  201. {
  202. const MouseEvent e2 (e.getEventRelativeTo (viewport));
  203. viewport->autoScroll (scrollX ? e2.x : 20, scrollY ? e2.y : 20, 8, 16);
  204. }
  205. }
  206. //==============================================================================
  207. int indexOfLineStartingWith (const StringArray& lines, const String& text, int index)
  208. {
  209. const int len = text.length();
  210. for (const String* i = lines.begin() + index, * const e = lines.end(); i < e; ++i)
  211. {
  212. if (CharacterFunctions::compareUpTo (i->getCharPointer().findEndOfWhitespace(),
  213. text.getCharPointer(), len) == 0)
  214. return index;
  215. ++index;
  216. }
  217. return -1;
  218. }
  219. //==============================================================================
  220. bool fileNeedsCppSyntaxHighlighting (const File& file)
  221. {
  222. if (file.hasFileExtension (sourceOrHeaderFileExtensions))
  223. return true;
  224. // This is a bit of a bodge to deal with libc++ headers with no extension..
  225. char fileStart[128] = { 0 };
  226. FileInputStream fin (file);
  227. fin.read (fileStart, sizeof (fileStart) - 4);
  228. return CharPointer_UTF8::isValidString (fileStart, sizeof (fileStart))
  229. && String (fileStart).trimStart().startsWith ("// -*- C++ -*-");
  230. }
  231. //==============================================================================
  232. StringArray getJUCEModules() noexcept
  233. {
  234. static StringArray juceModuleIds =
  235. {
  236. "juce_analytics",
  237. "juce_audio_basics",
  238. "juce_audio_devices",
  239. "juce_audio_formats",
  240. "juce_audio_plugin_client",
  241. "juce_audio_processors",
  242. "juce_audio_utils",
  243. "juce_blocks_basics",
  244. "juce_box2d",
  245. "juce_core",
  246. "juce_cryptography",
  247. "juce_data_structures",
  248. "juce_dsp",
  249. "juce_events",
  250. "juce_graphics",
  251. "juce_gui_basics",
  252. "juce_gui_extra",
  253. "juce_opengl",
  254. "juce_osc",
  255. "juce_product_unlocking",
  256. "juce_video"
  257. };
  258. return juceModuleIds;
  259. }
  260. bool isJUCEModule (const String& moduleID) noexcept
  261. {
  262. return getJUCEModules().contains (moduleID);
  263. }
  264. StringArray getModulesRequiredForConsole() noexcept
  265. {
  266. return { "juce_core",
  267. "juce_data_structures",
  268. "juce_events"
  269. };
  270. }
  271. StringArray getModulesRequiredForComponent() noexcept
  272. {
  273. return { "juce_core",
  274. "juce_data_structures",
  275. "juce_events",
  276. "juce_graphics",
  277. "juce_gui_basics"
  278. };
  279. }
  280. StringArray getModulesRequiredForAudioProcessor() noexcept
  281. {
  282. return { "juce_audio_basics",
  283. "juce_audio_devices",
  284. "juce_audio_formats",
  285. "juce_audio_plugin_client",
  286. "juce_audio_processors",
  287. "juce_audio_utils",
  288. "juce_core",
  289. "juce_data_structures",
  290. "juce_events",
  291. "juce_graphics",
  292. "juce_gui_basics",
  293. "juce_gui_extra"
  294. };
  295. }
  296. bool isPIPFile (const File& file) noexcept
  297. {
  298. for (auto line : StringArray::fromLines (file.loadFileAsString()))
  299. {
  300. auto trimmedLine = trimCommentCharsFromStartOfLine (line);
  301. if (trimmedLine.startsWith ("BEGIN_JUCE_PIP_METADATA"))
  302. return true;
  303. }
  304. return false;
  305. }
  306. bool isValidJUCEExamplesDirectory (const File& directory) noexcept
  307. {
  308. if (! directory.exists() || ! directory.isDirectory() || ! directory.containsSubDirectories())
  309. return false;
  310. return directory.getChildFile ("Assets").getChildFile ("juce_icon.png").existsAsFile();
  311. }