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.

506 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 ('\\', '/');
  117. }
  118. const String windowsStylePath (const String& path)
  119. {
  120. return path.replaceCharacter ('/', '\\');
  121. }
  122. const String appendPath (const String& path, const String& subpath)
  123. {
  124. if (File::isAbsolutePath (subpath)
  125. || subpath.startsWithChar ('$')
  126. || subpath.startsWithChar ('~')
  127. || (CharacterFunctions::isLetter (subpath[0]) && subpath[1] == ':'))
  128. return subpath.replaceCharacter ('\\', '/');
  129. String path1 (path.replaceCharacter ('\\', '/'));
  130. if (! path1.endsWithChar ('/'))
  131. path1 << '/';
  132. return path1 + subpath.replaceCharacter ('\\', '/');
  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 ("/:").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 (" .", "__")
  157. .retainCharacters ("_ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789")
  158. + "_"
  159. + String::toHexString (file.hashCode()).toUpperCase()
  160. + "__";
  161. }
  162. //==============================================================================
  163. bool isJuceFolder (const File& folder)
  164. {
  165. return folder.getFileName().containsIgnoreCase ("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': r << "\\t"; lastWasHexEscapeCode = false; break;
  214. case '\r': r << "\\r"; lastWasHexEscapeCode = false; break;
  215. case '\n': r << "\\n"; lastWasHexEscapeCode = false; break;
  216. case '\\': r << "\\\\"; lastWasHexEscapeCode = false; break;
  217. case '\'': r << "\\\'"; lastWasHexEscapeCode = false; break;
  218. case '\"': r << "\\\""; lastWasHexEscapeCode = false; break;
  219. default:
  220. if (c < 128
  221. && ! (lastWasHexEscapeCode
  222. && String ("0123456789abcdefABCDEF").containsChar (c))) // (have to avoid following a hex escape sequence with a valid hex digit)
  223. {
  224. r << c;
  225. lastWasHexEscapeCode = false;
  226. }
  227. else
  228. {
  229. r << "\\x" << String::toHexString ((int) c);
  230. lastWasHexEscapeCode = true;
  231. }
  232. break;
  233. }
  234. }
  235. return r;
  236. }
  237. //==============================================================================
  238. const String makeValidCppIdentifier (String s,
  239. const bool capitalise,
  240. const bool removeColons,
  241. const bool allowTemplates)
  242. {
  243. if (removeColons)
  244. s = s.replaceCharacters (".,;:/@", "______");
  245. else
  246. s = s.replaceCharacters (".,;/@", "_____");
  247. int i;
  248. for (i = s.length(); --i > 0;)
  249. if (CharacterFunctions::isLetter (s[i])
  250. && CharacterFunctions::isLetter (s[i - 1])
  251. && CharacterFunctions::isUpperCase (s[i])
  252. && ! CharacterFunctions::isUpperCase (s[i - 1]))
  253. s = s.substring (0, i) + " " + s.substring (i);
  254. String allowedChars ("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_ 0123456789");
  255. if (allowTemplates)
  256. allowedChars += "<>";
  257. if (! removeColons)
  258. allowedChars += ":";
  259. StringArray words;
  260. words.addTokens (s.retainCharacters (allowedChars), false);
  261. words.trim();
  262. String n (words[0]);
  263. if (capitalise)
  264. n = n.toLowerCase();
  265. for (i = 1; i < words.size(); ++i)
  266. {
  267. if (capitalise && words[i].length() > 1)
  268. n << words[i].substring (0, 1).toUpperCase()
  269. << words[i].substring (1).toLowerCase();
  270. else
  271. n << words[i];
  272. }
  273. if (CharacterFunctions::isDigit (n[0]))
  274. n = "_" + n;
  275. // make sure it's not a reserved c++ keyword..
  276. static const tchar* const reservedWords[] =
  277. {
  278. T("auto"), T("const"), T("double"), T("float"), T("int"), T("short"), T("struct"),
  279. T("return"), T("static"), T("union"), T("while"), T("asm"), T("dynamic_cast"),
  280. T("unsigned"), T("break"), T("continue"), T("else"), T("for"), T("long"), T("signed"),
  281. T("switch"), T("void"), T("case"), T("default"), T("enum"), T("goto"), T("register"),
  282. T("sizeof"), T("typedef"), T("volatile"), T("char"), T("do"), T("extern"), T("if"),
  283. T("namespace"), T("reinterpret_cast"), T("try"), T("bool"), T("explicit"), T("new"),
  284. T("static_cast"), T("typeid"), T("catch"), T("false"), T("operator"), T("template"),
  285. T("typename"), T("class"), T("friend"), T("private"), T("this"), T("using"), T("const_cast"),
  286. T("inline"), T("public"), T("throw"), T("virtual"), T("delete"), T("mutable"), T("protected"),
  287. T("true"), T("wchar_t"), T("and"), T("bitand"), T("compl"), T("not_eq"), T("or_eq"),
  288. T("xor_eq"), T("and_eq"), T("bitor"), T("not"), T("or"), T("xor"), T("cin"), T("endl"),
  289. T("INT_MIN"), T("iomanip"), T("main"), T("npos"), T("std"), T("cout"), T("include"),
  290. T("INT_MAX"), T("iostream"), T("MAX_RAND"), T("NULL"), T("string"), T("id")
  291. };
  292. for (i = 0; i < numElementsInArray (reservedWords); ++i)
  293. if (n == reservedWords[i])
  294. n << '_';
  295. return n;
  296. }
  297. //==============================================================================
  298. const String floatToCode (const float v)
  299. {
  300. String s ((double) (float) v, 4);
  301. if (s.containsChar ('.'))
  302. s << 'f';
  303. else
  304. s << ".0f";
  305. return s;
  306. }
  307. const String doubleToCode (const double v)
  308. {
  309. String s (v, 7);
  310. if (! s.containsChar ('.'))
  311. s << ".0";
  312. return s;
  313. }
  314. const String boolToCode (const bool b)
  315. {
  316. return b ? "true" : "false";
  317. }
  318. const String colourToCode (const Colour& col)
  319. {
  320. const Colour colours[] =
  321. {
  322. #define COL(col) Colours::col,
  323. #include "jucer_Colours.h"
  324. #undef COL
  325. Colours::transparentBlack
  326. };
  327. static const char* colourNames[] =
  328. {
  329. #define COL(col) #col,
  330. #include "jucer_Colours.h"
  331. #undef COL
  332. 0
  333. };
  334. for (int i = 0; i < numElementsInArray (colourNames) - 1; ++i)
  335. if (col == colours[i])
  336. return "Colours::" + String (colourNames[i]);
  337. return "Colour (0x" + hexString8Digits ((int) col.getARGB()) + ')';
  338. }
  339. const String justificationToCode (const Justification& justification)
  340. {
  341. switch (justification.getFlags())
  342. {
  343. case Justification::centred: return "Justification::centred";
  344. case Justification::centredLeft: return "Justification::centredLeft";
  345. case Justification::centredRight: return "Justification::centredRight";
  346. case Justification::centredTop: return "Justification::centredTop";
  347. case Justification::centredBottom: return "Justification::centredBottom";
  348. case Justification::topLeft: return "Justification::topLeft";
  349. case Justification::topRight: return "Justification::topRight";
  350. case Justification::bottomLeft: return "Justification::bottomLeft";
  351. case Justification::bottomRight: return "Justification::bottomRight";
  352. case Justification::left: return "Justification::left";
  353. case Justification::right: return "Justification::right";
  354. case Justification::horizontallyCentred: return "Justification::horizontallyCentred";
  355. case Justification::top: return "Justification::top";
  356. case Justification::bottom: return "Justification::bottom";
  357. case Justification::verticallyCentred: return "Justification::verticallyCentred";
  358. case Justification::horizontallyJustified: return "Justification::horizontallyJustified";
  359. default: jassertfalse; break;
  360. }
  361. return "Justification (" + String (justification.getFlags()) + ")";
  362. }
  363. const String castToFloat (const String& expression)
  364. {
  365. if (expression.containsOnly ("0123456789.f"))
  366. {
  367. String s (expression.getFloatValue());
  368. if (s.containsChar (T('.')))
  369. return s + "f";
  370. return s + ".0f";
  371. }
  372. return "(float) (" + expression + ")";
  373. }
  374. const String indentCode (const String& code, const int numSpaces)
  375. {
  376. if (numSpaces == 0)
  377. return code;
  378. const String space (String::repeatedString (" ", numSpaces));
  379. StringArray lines;
  380. lines.addLines (code);
  381. for (int i = 1; i < lines.size(); ++i)
  382. {
  383. String s (lines[i].trimEnd());
  384. if (s.isNotEmpty())
  385. s = space + s;
  386. lines.set (i, s);
  387. }
  388. return lines.joinIntoString ("\n");
  389. }
  390. int indexOfLineStartingWith (const StringArray& lines, const String& text, int startIndex)
  391. {
  392. startIndex = jmax (0, startIndex);
  393. while (startIndex < lines.size())
  394. {
  395. if (lines[startIndex].trimStart().startsWithIgnoreCase (text))
  396. return startIndex;
  397. ++startIndex;
  398. }
  399. return -1;
  400. }