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.

383 lines
12KB

  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. #include "jucer_GeneratedCode.h"
  20. //==============================================================================
  21. GeneratedCode::GeneratedCode (const JucerDocument* const document_)
  22. : document (document_),
  23. suffix (0)
  24. {
  25. }
  26. GeneratedCode::~GeneratedCode()
  27. {
  28. }
  29. int GeneratedCode::getUniqueSuffix()
  30. {
  31. return ++suffix;
  32. }
  33. //==============================================================================
  34. String& GeneratedCode::getCallbackCode (const String& requiredParentClass,
  35. const String& returnType,
  36. const String& prototype,
  37. const bool hasPrePostUserSections)
  38. {
  39. String parentClass (requiredParentClass);
  40. if (parentClass.isNotEmpty()
  41. && ! (parentClass.startsWith (T("public "))
  42. || parentClass.startsWith (T("private "))
  43. || parentClass.startsWith (T("protected "))))
  44. {
  45. parentClass = T("public ") + parentClass;
  46. }
  47. for (int i = callbacks.size(); --i >= 0;)
  48. {
  49. CallbackMethod* const cm = callbacks.getUnchecked(i);
  50. if (cm->requiredParentClass == parentClass
  51. && cm->returnType == returnType
  52. && cm->prototype == prototype)
  53. return cm->content;
  54. }
  55. CallbackMethod* const cm = new CallbackMethod();
  56. callbacks.add (cm);
  57. cm->requiredParentClass = parentClass;
  58. cm->returnType = returnType;
  59. cm->prototype = prototype;
  60. cm->hasPrePostUserSections = hasPrePostUserSections;
  61. return cm->content;
  62. }
  63. void GeneratedCode::removeCallback (const String& returnType, const String& prototype)
  64. {
  65. for (int i = callbacks.size(); --i >= 0;)
  66. {
  67. CallbackMethod* const cm = callbacks.getUnchecked(i);
  68. if (cm->returnType == returnType && cm->prototype == prototype)
  69. callbacks.remove (i);
  70. }
  71. }
  72. void GeneratedCode::addImageResourceLoader (const String& imageMemberName, const String& resourceName)
  73. {
  74. const String initialiser (imageMemberName + T(" (0)"));
  75. if (! initialisers.contains (initialiser, false))
  76. {
  77. initialisers.add (initialiser);
  78. privateMemberDeclarations
  79. << "Image* " << imageMemberName << ";\n";
  80. if (resourceName.isNotEmpty())
  81. {
  82. constructorCode
  83. << imageMemberName << " = ImageCache::getFromMemory ("
  84. << resourceName << ", " << resourceName << "Size);\n";
  85. destructorCode
  86. << "ImageCache::release (" << imageMemberName << ");\n";
  87. }
  88. }
  89. }
  90. const StringArray GeneratedCode::getExtraParentClasses() const
  91. {
  92. StringArray s;
  93. for (int i = 0; i < callbacks.size(); ++i)
  94. {
  95. CallbackMethod* const cm = callbacks.getUnchecked(i);
  96. s.add (cm->requiredParentClass);
  97. }
  98. return s;
  99. }
  100. const String GeneratedCode::getCallbackDeclarations() const
  101. {
  102. String s;
  103. for (int i = 0; i < callbacks.size(); ++i)
  104. {
  105. CallbackMethod* const cm = callbacks.getUnchecked(i);
  106. s << cm->returnType << " " << cm->prototype << ";\n";
  107. }
  108. return s;
  109. }
  110. const String GeneratedCode::getCallbackDefinitions() const
  111. {
  112. String s;
  113. for (int i = 0; i < callbacks.size(); ++i)
  114. {
  115. CallbackMethod* const cm = callbacks.getUnchecked(i);
  116. const String userCodeBlockName (T("User")
  117. + makeValidCppIdentifier (cm->prototype.upToFirstOccurrenceOf (T("("), false, false),
  118. true, true, false).trim());
  119. if (userCodeBlockName.isNotEmpty() && cm->hasPrePostUserSections)
  120. {
  121. s << cm->returnType << " " << className << "::" << cm->prototype
  122. << "\n{\n //[" << userCodeBlockName << "_Pre]\n //[/" << userCodeBlockName
  123. << "_Pre]\n\n "
  124. << indentCode (cm->content.trim(), 4)
  125. << "\n\n //[" << userCodeBlockName << "_Post]\n //[/" << userCodeBlockName
  126. << "_Post]\n}\n\n";
  127. }
  128. else
  129. {
  130. s << cm->returnType << " " << className << "::" << cm->prototype
  131. << "\n{\n "
  132. << indentCode (cm->content.trim(), 4)
  133. << "\n}\n\n";
  134. }
  135. }
  136. return s;
  137. }
  138. //==============================================================================
  139. const String GeneratedCode::getClassDeclaration() const
  140. {
  141. StringArray parentClassLines;
  142. parentClassLines.addTokens (parentClasses, T(","), String::empty);
  143. parentClassLines.addArray (getExtraParentClasses());
  144. parentClassLines.trim();
  145. parentClassLines.removeEmptyStrings();
  146. parentClassLines.removeDuplicates (false);
  147. if (parentClassLines.contains (T("public Button"), false))
  148. parentClassLines.removeString (("public Component"), false);
  149. String r (T("class "));
  150. r << className << T(" : ");
  151. r += parentClassLines.joinIntoString (T(",\n") + String::repeatedString (T(" "), r.length()));
  152. return r;
  153. }
  154. const String GeneratedCode::getInitialiserList() const
  155. {
  156. StringArray inits (initialisers);
  157. if (parentClassInitialiser.isNotEmpty())
  158. inits.insert (0, parentClassInitialiser);
  159. inits.trim();
  160. inits.removeEmptyStrings();
  161. inits.removeDuplicates (false);
  162. String s;
  163. if (inits.size() == 0)
  164. return s;
  165. s << " : ";
  166. for (int i = 0; i < inits.size(); ++i)
  167. {
  168. String init (inits[i]);
  169. while (init.endsWithChar (T(',')))
  170. init = init.dropLastCharacters (1);
  171. s << init;
  172. if (i < inits.size() - 1)
  173. s << ",\n ";
  174. else
  175. s << "\n";
  176. }
  177. return s;
  178. }
  179. static const String getIncludeFileCode (StringArray files)
  180. {
  181. files.trim();
  182. files.removeEmptyStrings();
  183. files.removeDuplicates (false);
  184. String s;
  185. for (int i = 0; i < files.size(); ++i)
  186. s << T("#include \"") << files[i] << T("\"\n");
  187. return s;
  188. }
  189. //==============================================================================
  190. static void replaceTemplate (String& text, const String& itemName, const String& value)
  191. {
  192. for (;;)
  193. {
  194. const int index = text.indexOf (T("%%") + itemName + T("%%"));
  195. if (index < 0)
  196. break;
  197. int indentLevel = 0;
  198. for (int i = index; --i >= 0;)
  199. {
  200. if (text[i] == T('\n'))
  201. break;
  202. ++indentLevel;
  203. }
  204. text = text.replaceSection (index, itemName.length() + 4,
  205. indentCode (value, indentLevel));
  206. }
  207. }
  208. //==============================================================================
  209. static bool getUserSection (const StringArray& lines, const String& tag, StringArray& resultLines)
  210. {
  211. const int start = indexOfLineStartingWith (lines, T("//[") + tag + T("]"), 0);
  212. if (start < 0)
  213. return false;
  214. const int end = indexOfLineStartingWith (lines, T("//[/") + tag + T("]"), start + 1);
  215. for (int i = start + 1; i < end; ++i)
  216. resultLines.add (lines [i]);
  217. return true;
  218. }
  219. static void copyAcrossUserSections (String& dest, const String& src)
  220. {
  221. StringArray srcLines, dstLines;
  222. srcLines.addLines (src);
  223. dstLines.addLines (dest);
  224. for (int i = 0; i < dstLines.size(); ++i)
  225. {
  226. if (dstLines[i].trimStart().startsWith (T("//[")))
  227. {
  228. String tag (dstLines[i].trimStart().substring (3));
  229. tag = tag.upToFirstOccurrenceOf (T("]"), false, false);
  230. jassert (! tag.startsWithChar (T('/')));
  231. if (! tag.startsWithChar (T('/')))
  232. {
  233. const int endLine = indexOfLineStartingWith (dstLines,
  234. T("//[/") + tag + T("]"),
  235. i + 1);
  236. if (endLine > i)
  237. {
  238. StringArray sourceLines;
  239. if (getUserSection (srcLines, tag, sourceLines))
  240. {
  241. int j;
  242. for (j = endLine - i; --j > 0;)
  243. dstLines.remove (i + 1);
  244. for (j = 0; j < sourceLines.size(); ++j)
  245. dstLines.insert (++i, sourceLines [j].trimEnd());
  246. ++i;
  247. }
  248. else
  249. {
  250. i = endLine;
  251. }
  252. }
  253. }
  254. }
  255. dstLines.set (i, dstLines[i].trimEnd());
  256. }
  257. dest = dstLines.joinIntoString (T("\n")) + T("\n");
  258. }
  259. //==============================================================================
  260. void GeneratedCode::applyToCode (String& code,
  261. const String& fileNameRoot,
  262. const bool isForPreview,
  263. const String& oldFileWithUserData) const
  264. {
  265. // header guard..
  266. String headerGuard ("__JUCER_HEADER_");
  267. headerGuard << className.toUpperCase().retainCharacters (T("ABCDEFGHIJKLMNOPQRSTUVWXYZ"))
  268. << "_" << fileNameRoot.toUpperCase().retainCharacters (T("ABCDEFGHIJKLMNOPQRSTUVWXYZ"))
  269. << "_" << String::toHexString (Random::getSystemRandom().nextInt()).toUpperCase()
  270. << "__";
  271. replaceTemplate (code, "headerGuard", headerGuard);
  272. replaceTemplate (code, "creationTime", Time::getCurrentTime().toString (true, true, true));
  273. replaceTemplate (code, "className", className);
  274. replaceTemplate (code, "constructorParams", constructorParams);
  275. replaceTemplate (code, "initialisers", getInitialiserList());
  276. replaceTemplate (code, "classDeclaration", getClassDeclaration());
  277. replaceTemplate (code, "privateMemberDeclarations", privateMemberDeclarations);
  278. replaceTemplate (code, "publicMemberDeclarations", getCallbackDeclarations() + "\n" + publicMemberDeclarations);
  279. replaceTemplate (code, "methodDefinitions", getCallbackDefinitions());
  280. replaceTemplate (code, "includeFilesH", getIncludeFileCode (includeFilesH));
  281. replaceTemplate (code, "includeFilesCPP", getIncludeFileCode (includeFilesCPP));
  282. replaceTemplate (code, "constructor", constructorCode);
  283. replaceTemplate (code, "destructor", destructorCode);
  284. if (! isForPreview)
  285. {
  286. replaceTemplate (code, "metadata", jucerMetadata);
  287. replaceTemplate (code, "staticMemberDefinitions", staticMemberDefinitions);
  288. }
  289. else
  290. {
  291. replaceTemplate (code, "metadata", T(" << Metadata isn't shown in the code preview >>\n"));
  292. replaceTemplate (code, "staticMemberDefinitions", T("// Static member declarations and resources would go here... (these aren't shown in the code preview)"));
  293. }
  294. copyAcrossUserSections (code, oldFileWithUserData);
  295. }