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.

526 lines
16KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library - "Jules' Utility Class Extensions"
  4. Copyright 2004-9 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. #include "../jucer_Headers.h"
  19. //==============================================================================
  20. int64 calculateStreamHashCode (InputStream& in)
  21. {
  22. int64 t = 0;
  23. const int bufferSize = 4096;
  24. HeapBlock <uint8> buffer;
  25. buffer.malloc (bufferSize);
  26. for (;;)
  27. {
  28. const int num = in.read (buffer, bufferSize);
  29. if (num <= 0)
  30. break;
  31. for (int i = 0; i < num; ++i)
  32. t = t * 65599 + buffer[i];
  33. }
  34. return t;
  35. }
  36. int64 calculateFileHashCode (const File& file)
  37. {
  38. ScopedPointer <FileInputStream> stream (file.createInputStream());
  39. return stream != 0 ? calculateStreamHashCode (*stream) : 0;
  40. }
  41. bool areFilesIdentical (const File& file1, const File& file2)
  42. {
  43. return file1.getSize() == file2.getSize()
  44. && calculateFileHashCode (file1) == calculateFileHashCode (file2);
  45. }
  46. bool overwriteFileWithNewDataIfDifferent (const File& file, const char* data, int numBytes)
  47. {
  48. if (file.getSize() == numBytes)
  49. {
  50. MemoryInputStream newStream (data, numBytes, false);
  51. if (calculateStreamHashCode (newStream) == calculateFileHashCode (file))
  52. return true;
  53. }
  54. TemporaryFile temp (file);
  55. return temp.getFile().appendData (data, numBytes)
  56. && temp.overwriteTargetFileWithTemporary();
  57. }
  58. bool overwriteFileWithNewDataIfDifferent (const File& file, const MemoryOutputStream& newData)
  59. {
  60. return overwriteFileWithNewDataIfDifferent (file, newData.getData(), newData.getDataSize());
  61. }
  62. bool overwriteFileWithNewDataIfDifferent (const File& file, const String& newData)
  63. {
  64. return overwriteFileWithNewDataIfDifferent (file, newData.toUTF8(), strlen ((const char*) newData.toUTF8()));
  65. }
  66. bool containsAnyNonHiddenFiles (const File& folder)
  67. {
  68. DirectoryIterator di (folder, false);
  69. while (di.next())
  70. if (! di.getFile().isHidden())
  71. return true;
  72. return false;
  73. }
  74. //==============================================================================
  75. const int64 hashCode64 (const String& s)
  76. {
  77. return s.hashCode64() + s.length() * s.hashCode() + s.toUpperCase().hashCode();
  78. }
  79. const String createAlphaNumericUID()
  80. {
  81. String uid;
  82. static const char chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
  83. Random r (Random::getSystemRandom().nextInt64());
  84. for (int i = 9; --i >= 0;)
  85. {
  86. r.setSeedRandomly();
  87. uid << (juce_wchar) chars [r.nextInt (sizeof (chars))];
  88. }
  89. return uid;
  90. }
  91. const String randomHexString (Random& random, int numChars)
  92. {
  93. String s;
  94. const char hexChars[] = "0123456789ABCDEF";
  95. while (--numChars >= 0)
  96. s << hexChars [random.nextInt (16)];
  97. return s;
  98. }
  99. const String hexString8Digits (int value)
  100. {
  101. return String::toHexString (value).paddedLeft ('0', 8);
  102. }
  103. const String createGUID (const String& seed)
  104. {
  105. String guid;
  106. Random r (hashCode64 (seed + "_jucersalt"));
  107. guid << "{" << randomHexString (r, 8); // (written as separate statements to enforce the order of execution)
  108. guid << "-" << randomHexString (r, 4);
  109. guid << "-" << randomHexString (r, 4);
  110. guid << "-" << randomHexString (r, 4);
  111. guid << "-" << randomHexString (r, 12) << "}";
  112. return guid;
  113. }
  114. const String unixStylePath (const String& path)
  115. {
  116. return path.replaceCharacter (T('\\'), T('/'));
  117. }
  118. const String windowsStylePath (const String& path)
  119. {
  120. return path.replaceCharacter (T('/'), T('\\'));
  121. }
  122. const String appendPath (const String& path, const String& subpath)
  123. {
  124. if (File::isAbsolutePath (subpath)
  125. || subpath.startsWithChar (T('$'))
  126. || subpath.startsWithChar (T('~'))
  127. || (CharacterFunctions::isLetter (subpath[0]) && subpath[1] == T(':')))
  128. return subpath.replaceCharacter (T('\\'), T('/'));
  129. String path1 (path.replaceCharacter (T('\\'), T('/')));
  130. if (! path1.endsWithChar (T('/')))
  131. path1 << '/';
  132. return path1 + subpath.replaceCharacter (T('\\'), T('/'));
  133. }
  134. bool shouldPathsBeRelative (String path1, String path2)
  135. {
  136. path1 = unixStylePath (path1);
  137. path2 = unixStylePath (path2);
  138. const int len = jmin (path1.length(), path2.length());
  139. int commonBitLength = 0;
  140. for (int i = 0; i < len; ++i)
  141. {
  142. if (CharacterFunctions::toLowerCase (path1[i]) != CharacterFunctions::toLowerCase (path2[i]))
  143. break;
  144. ++commonBitLength;
  145. }
  146. return path1.substring (0, commonBitLength).removeCharacters (T("/:")).isNotEmpty();
  147. }
  148. const String createIncludeStatement (const File& includeFile, const File& targetFile)
  149. {
  150. return "#include \"" + unixStylePath (includeFile.getRelativePathFrom (targetFile.getParentDirectory()))
  151. + "\"";
  152. }
  153. const String makeHeaderGuardName (const File& file)
  154. {
  155. return "__" + file.getFileName().toUpperCase()
  156. .replaceCharacters (T(" ."), T("__"))
  157. .retainCharacters (T("_ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"))
  158. + "_"
  159. + String::toHexString (file.hashCode()).toUpperCase()
  160. + "__";
  161. }
  162. //==============================================================================
  163. bool isJuceFolder (const File& folder)
  164. {
  165. return folder.getFileName().containsIgnoreCase (T("juce"))
  166. && folder.getChildFile ("juce.h").exists()
  167. && folder.getChildFile ("juce_Config.h").exists();
  168. }
  169. static const File lookInFolderForJuceFolder (const File& folder)
  170. {
  171. for (DirectoryIterator di (folder, false, "*juce*", File::findDirectories); di.next();)
  172. {
  173. if (isJuceFolder (di.getFile()))
  174. return di.getFile();
  175. }
  176. return File::nonexistent;
  177. }
  178. const File findParentJuceFolder (const File& file)
  179. {
  180. File f (file);
  181. while (f.exists() && f.getParentDirectory() != f)
  182. {
  183. if (isJuceFolder (f))
  184. return f;
  185. File found = lookInFolderForJuceFolder (f);
  186. if (found.exists())
  187. return found;
  188. f = f.getParentDirectory();
  189. }
  190. return File::nonexistent;
  191. }
  192. const File findDefaultJuceFolder()
  193. {
  194. File f = findParentJuceFolder (File::getSpecialLocation (File::currentApplicationFile));
  195. if (! f.exists())
  196. f = lookInFolderForJuceFolder (File::getSpecialLocation (File::userHomeDirectory));
  197. if (! f.exists())
  198. f = lookInFolderForJuceFolder (File::getSpecialLocation (File::userDocumentsDirectory));
  199. return f;
  200. }
  201. //==============================================================================
  202. const String replaceCEscapeChars (const String& s)
  203. {
  204. const int len = s.length();
  205. String r;
  206. r.preallocateStorage (len + 2);
  207. bool lastWasHexEscapeCode = false;
  208. for (int i = 0; i < len; ++i)
  209. {
  210. const tchar c = s[i];
  211. switch (c)
  212. {
  213. case '\t':
  214. r << T("\\t");
  215. lastWasHexEscapeCode = false;
  216. break;
  217. case '\r':
  218. r << T("\\r");
  219. lastWasHexEscapeCode = false;
  220. break;
  221. case '\n':
  222. r << T("\\n");
  223. lastWasHexEscapeCode = false;
  224. break;
  225. case '\\':
  226. r << T("\\\\");
  227. lastWasHexEscapeCode = false;
  228. break;
  229. case '\'':
  230. r << T("\\\'");
  231. lastWasHexEscapeCode = false;
  232. break;
  233. case '\"':
  234. r << T("\\\"");
  235. lastWasHexEscapeCode = false;
  236. break;
  237. default:
  238. if (c < 128 &&
  239. ! (lastWasHexEscapeCode
  240. && String (T("0123456789abcdefABCDEF")).containsChar (c))) // (have to avoid following a hex escape sequence with a valid hex digit)
  241. {
  242. r << c;
  243. lastWasHexEscapeCode = false;
  244. }
  245. else
  246. {
  247. lastWasHexEscapeCode = true;
  248. r << T("\\x") << String::toHexString ((int) c);
  249. }
  250. break;
  251. }
  252. }
  253. return r;
  254. }
  255. //==============================================================================
  256. const String makeValidCppIdentifier (String s,
  257. const bool capitalise,
  258. const bool removeColons,
  259. const bool allowTemplates)
  260. {
  261. if (removeColons)
  262. s = s.replaceCharacters (T(".,;:/@"), T("______"));
  263. else
  264. s = s.replaceCharacters (T(".,;/@"), T("_____"));
  265. int i;
  266. for (i = s.length(); --i > 0;)
  267. if (CharacterFunctions::isLetter (s[i])
  268. && CharacterFunctions::isLetter (s[i - 1])
  269. && CharacterFunctions::isUpperCase (s[i])
  270. && ! CharacterFunctions::isUpperCase (s[i - 1]))
  271. s = s.substring (0, i) + T(" ") + s.substring (i);
  272. String allowedChars (T("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_ 0123456789"));
  273. if (allowTemplates)
  274. allowedChars += T("<>");
  275. if (! removeColons)
  276. allowedChars += T(":");
  277. StringArray words;
  278. words.addTokens (s.retainCharacters (allowedChars), false);
  279. words.trim();
  280. String n (words[0]);
  281. if (capitalise)
  282. n = n.toLowerCase();
  283. for (i = 1; i < words.size(); ++i)
  284. {
  285. if (capitalise && words[i].length() > 1)
  286. n << words[i].substring (0, 1).toUpperCase()
  287. << words[i].substring (1).toLowerCase();
  288. else
  289. n << words[i];
  290. }
  291. if (CharacterFunctions::isDigit (n[0]))
  292. n = T("_") + n;
  293. // make sure it's not a reserved c++ keyword..
  294. static const tchar* const reservedWords[] =
  295. {
  296. T("auto"), T("const"), T("double"), T("float"), T("int"), T("short"), T("struct"),
  297. T("return"), T("static"), T("union"), T("while"), T("asm"), T("dynamic_cast"),
  298. T("unsigned"), T("break"), T("continue"), T("else"), T("for"), T("long"), T("signed"),
  299. T("switch"), T("void"), T("case"), T("default"), T("enum"), T("goto"), T("register"),
  300. T("sizeof"), T("typedef"), T("volatile"), T("char"), T("do"), T("extern"), T("if"),
  301. T("namespace"), T("reinterpret_cast"), T("try"), T("bool"), T("explicit"), T("new"),
  302. T("static_cast"), T("typeid"), T("catch"), T("false"), T("operator"), T("template"),
  303. T("typename"), T("class"), T("friend"), T("private"), T("this"), T("using"), T("const_cast"),
  304. T("inline"), T("public"), T("throw"), T("virtual"), T("delete"), T("mutable"), T("protected"),
  305. T("true"), T("wchar_t"), T("and"), T("bitand"), T("compl"), T("not_eq"), T("or_eq"),
  306. T("xor_eq"), T("and_eq"), T("bitor"), T("not"), T("or"), T("xor"), T("cin"), T("endl"),
  307. T("INT_MIN"), T("iomanip"), T("main"), T("npos"), T("std"), T("cout"), T("include"),
  308. T("INT_MAX"), T("iostream"), T("MAX_RAND"), T("NULL"), T("string"), T("id")
  309. };
  310. for (i = 0; i < numElementsInArray (reservedWords); ++i)
  311. if (n == reservedWords[i])
  312. n << '_';
  313. return n;
  314. }
  315. //==============================================================================
  316. const String floatToCode (const float v)
  317. {
  318. String s ((double) (float) v, 4);
  319. if (s.containsChar (T('.')))
  320. s << 'f';
  321. else
  322. s << ".0f";
  323. return s;
  324. }
  325. const String doubleToCode (const double v)
  326. {
  327. String s (v, 7);
  328. if (! s.containsChar (T('.')))
  329. s << ".0";
  330. return s;
  331. }
  332. const String boolToCode (const bool b)
  333. {
  334. return b ? T("true") : T("false");
  335. }
  336. const String colourToCode (const Colour& col)
  337. {
  338. #define COL(col) Colours::col,
  339. const Colour colours[] =
  340. {
  341. #include "jucer_Colours.h"
  342. Colours::transparentBlack
  343. };
  344. #undef COL
  345. #define COL(col) #col,
  346. static const char* colourNames[] =
  347. {
  348. #include "jucer_Colours.h"
  349. 0
  350. };
  351. #undef COL
  352. for (int i = 0; i < numElementsInArray (colourNames) - 1; ++i)
  353. if (col == colours[i])
  354. return T("Colours::") + String (colourNames[i]);
  355. return T("Colour (0x") + hexString8Digits ((int) col.getARGB()) + T(')');
  356. }
  357. const String justificationToCode (const Justification& justification)
  358. {
  359. switch (justification.getFlags())
  360. {
  361. case Justification::centred: return "Justification::centred";
  362. case Justification::centredLeft: return "Justification::centredLeft";
  363. case Justification::centredRight: return "Justification::centredRight";
  364. case Justification::centredTop: return "Justification::centredTop";
  365. case Justification::centredBottom: return "Justification::centredBottom";
  366. case Justification::topLeft: return "Justification::topLeft";
  367. case Justification::topRight: return "Justification::topRight";
  368. case Justification::bottomLeft: return "Justification::bottomLeft";
  369. case Justification::bottomRight: return "Justification::bottomRight";
  370. case Justification::left: return "Justification::left";
  371. case Justification::right: return "Justification::right";
  372. case Justification::horizontallyCentred: return "Justification::horizontallyCentred";
  373. case Justification::top: return "Justification::top";
  374. case Justification::bottom: return "Justification::bottom";
  375. case Justification::verticallyCentred: return "Justification::verticallyCentred";
  376. case Justification::horizontallyJustified: return "Justification::horizontallyJustified";
  377. default: jassertfalse; break;
  378. }
  379. return T("Justification (") + String (justification.getFlags()) + T(")");
  380. }
  381. const String castToFloat (const String& expression)
  382. {
  383. if (expression.containsOnly (T("0123456789.f")))
  384. {
  385. String s (expression.getFloatValue());
  386. if (s.containsChar (T('.')))
  387. return s + T("f");
  388. return s + T(".0f");
  389. }
  390. return T("(float) (") + expression + T(")");
  391. }
  392. const String indentCode (const String& code, const int numSpaces)
  393. {
  394. if (numSpaces == 0)
  395. return code;
  396. const String space (String::repeatedString (T(" "), numSpaces));
  397. StringArray lines;
  398. lines.addLines (code);
  399. for (int i = 1; i < lines.size(); ++i)
  400. {
  401. String s (lines[i].trimEnd());
  402. if (s.isNotEmpty())
  403. s = space + s;
  404. lines.set (i, s);
  405. }
  406. return lines.joinIntoString (T("\n"));
  407. }
  408. int indexOfLineStartingWith (const StringArray& lines, const String& text, int startIndex)
  409. {
  410. startIndex = jmax (0, startIndex);
  411. while (startIndex < lines.size())
  412. {
  413. if (lines[startIndex].trimStart().startsWithIgnoreCase (text))
  414. return startIndex;
  415. ++startIndex;
  416. }
  417. return -1;
  418. }