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.

354 lines
11KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2015 - ROLI Ltd.
  5. Permission is granted to use this software under the terms of either:
  6. a) the GPL v2 (or any later version)
  7. b) the Affero GPL v3
  8. Details of these licenses can be found at: www.gnu.org/licenses
  9. JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
  10. WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
  11. A PARTICULAR PURPOSE. See the GNU General Public License for more details.
  12. ------------------------------------------------------------------------------
  13. To release a closed-source product which uses JUCE, commercial licenses are
  14. available: visit www.juce.com for more information.
  15. ==============================================================================
  16. */
  17. #include "../jucer_Headers.h"
  18. #include "jucer_GeneratedCode.h"
  19. #include "jucer_JucerDocument.h"
  20. //==============================================================================
  21. GeneratedCode::GeneratedCode (const JucerDocument* const doc)
  22. : document (doc), suffix (0)
  23. {
  24. }
  25. GeneratedCode::~GeneratedCode()
  26. {
  27. }
  28. int GeneratedCode::getUniqueSuffix()
  29. {
  30. return ++suffix;
  31. }
  32. //==============================================================================
  33. String& GeneratedCode::getCallbackCode (const String& requiredParentClass,
  34. const String& returnType,
  35. const String& prototype,
  36. const bool hasPrePostUserSections)
  37. {
  38. String parentClass (requiredParentClass);
  39. if (parentClass.isNotEmpty()
  40. && ! (parentClass.startsWith ("public ")
  41. || parentClass.startsWith ("private ")
  42. || parentClass.startsWith ("protected ")))
  43. {
  44. parentClass = "public " + parentClass;
  45. }
  46. for (int i = callbacks.size(); --i >= 0;)
  47. {
  48. CallbackMethod* const cm = callbacks.getUnchecked(i);
  49. if (cm->requiredParentClass == parentClass
  50. && cm->returnType == returnType
  51. && cm->prototype == prototype)
  52. return cm->content;
  53. }
  54. CallbackMethod* const cm = new CallbackMethod();
  55. callbacks.add (cm);
  56. cm->requiredParentClass = parentClass;
  57. cm->returnType = returnType;
  58. cm->prototype = prototype;
  59. cm->hasPrePostUserSections = hasPrePostUserSections;
  60. return cm->content;
  61. }
  62. void GeneratedCode::removeCallback (const String& returnType, const String& prototype)
  63. {
  64. for (int i = callbacks.size(); --i >= 0;)
  65. {
  66. CallbackMethod* const cm = callbacks.getUnchecked(i);
  67. if (cm->returnType == returnType && cm->prototype == prototype)
  68. callbacks.remove (i);
  69. }
  70. }
  71. void GeneratedCode::addImageResourceLoader (const String& imageMemberName, const String& resourceName)
  72. {
  73. privateMemberDeclarations
  74. << "Image " << imageMemberName << ";\n";
  75. if (resourceName.isNotEmpty())
  76. constructorCode << imageMemberName << " = ImageCache::getFromMemory ("
  77. << resourceName << ", " << resourceName << "Size);\n";
  78. }
  79. StringArray GeneratedCode::getExtraParentClasses() const
  80. {
  81. StringArray s;
  82. for (int i = 0; i < callbacks.size(); ++i)
  83. {
  84. CallbackMethod* const cm = callbacks.getUnchecked(i);
  85. s.add (cm->requiredParentClass);
  86. }
  87. return s;
  88. }
  89. String GeneratedCode::getCallbackDeclarations() const
  90. {
  91. String s;
  92. for (int i = 0; i < callbacks.size(); ++i)
  93. {
  94. CallbackMethod* const cm = callbacks.getUnchecked(i);
  95. s << cm->returnType << " " << cm->prototype << " override;\n";
  96. }
  97. return s;
  98. }
  99. String GeneratedCode::getCallbackDefinitions() const
  100. {
  101. String s;
  102. for (int i = 0; i < callbacks.size(); ++i)
  103. {
  104. CallbackMethod* const cm = callbacks.getUnchecked(i);
  105. const String userCodeBlockName ("User"
  106. + CodeHelpers::makeValidIdentifier (cm->prototype.upToFirstOccurrenceOf ("(", false, false),
  107. true, true, false).trim());
  108. if (userCodeBlockName.isNotEmpty() && cm->hasPrePostUserSections)
  109. {
  110. s << cm->returnType << " " << className << "::" << cm->prototype
  111. << "\n{\n //[" << userCodeBlockName << "_Pre]\n //[/" << userCodeBlockName
  112. << "_Pre]\n\n "
  113. << CodeHelpers::indent (cm->content.trim(), 4, false)
  114. << "\n\n //[" << userCodeBlockName << "_Post]\n //[/" << userCodeBlockName
  115. << "_Post]\n}\n\n";
  116. }
  117. else
  118. {
  119. s << cm->returnType << " " << className << "::" << cm->prototype
  120. << "\n{\n "
  121. << CodeHelpers::indent (cm->content.trim(), 4, false)
  122. << "\n}\n\n";
  123. }
  124. }
  125. return s;
  126. }
  127. //==============================================================================
  128. String GeneratedCode::getClassDeclaration() const
  129. {
  130. StringArray parentClassLines;
  131. parentClassLines.addTokens (parentClasses, ",", StringRef());
  132. parentClassLines.addArray (getExtraParentClasses());
  133. parentClassLines = getCleanedStringArray (parentClassLines);
  134. if (parentClassLines.contains ("public Button", false))
  135. parentClassLines.removeString ("public Component", false);
  136. String r ("class ");
  137. r << className << " : ";
  138. r += parentClassLines.joinIntoString (",\n" + String::repeatedString (" ", r.length()));
  139. return r;
  140. }
  141. String GeneratedCode::getInitialiserList() const
  142. {
  143. StringArray inits (initialisers);
  144. if (parentClassInitialiser.isNotEmpty())
  145. inits.insert (0, parentClassInitialiser);
  146. inits = getCleanedStringArray (inits);
  147. String s;
  148. if (inits.size() == 0)
  149. return s;
  150. s << " : ";
  151. for (int i = 0; i < inits.size(); ++i)
  152. {
  153. String init (inits[i]);
  154. while (init.endsWithChar (','))
  155. init = init.dropLastCharacters (1);
  156. s << init;
  157. if (i < inits.size() - 1)
  158. s << ",\n ";
  159. else
  160. s << "\n";
  161. }
  162. return s;
  163. }
  164. static String getIncludeFileCode (const Array<File>& files, const File& targetFile)
  165. {
  166. String s;
  167. for (int i = 0; i < files.size(); ++i)
  168. s << CodeHelpers::createIncludeStatement (files.getReference(i), targetFile) << newLine;
  169. return s;
  170. }
  171. bool GeneratedCode::shouldUseTransMacro() const noexcept
  172. {
  173. return document->shouldUseTransMacro();
  174. }
  175. //==============================================================================
  176. static void replaceTemplate (String& text, const String& itemName, const String& value)
  177. {
  178. for (;;)
  179. {
  180. const int index = text.indexOf ("%%" + itemName + "%%");
  181. if (index < 0)
  182. break;
  183. int indentLevel = 0;
  184. for (int i = index; --i >= 0;)
  185. {
  186. if (text[i] == '\n')
  187. break;
  188. ++indentLevel;
  189. }
  190. text = text.replaceSection (index, itemName.length() + 4,
  191. CodeHelpers::indent (value, indentLevel, false));
  192. }
  193. }
  194. //==============================================================================
  195. static bool getUserSection (const StringArray& lines, const String& tag, StringArray& resultLines)
  196. {
  197. const int start = indexOfLineStartingWith (lines, "//[" + tag + "]", 0);
  198. if (start < 0)
  199. return false;
  200. const int end = indexOfLineStartingWith (lines, "//[/" + tag + "]", start + 1);
  201. for (int i = start + 1; i < end; ++i)
  202. resultLines.add (lines [i]);
  203. return true;
  204. }
  205. static void copyAcrossUserSections (String& dest, const String& src)
  206. {
  207. StringArray srcLines, dstLines;
  208. srcLines.addLines (src);
  209. dstLines.addLines (dest);
  210. for (int i = 0; i < dstLines.size(); ++i)
  211. {
  212. if (dstLines[i].trimStart().startsWith ("//["))
  213. {
  214. String tag (dstLines[i].trimStart().substring (3));
  215. tag = tag.upToFirstOccurrenceOf ("]", false, false);
  216. jassert (! tag.startsWithChar ('/'));
  217. if (! tag.startsWithChar ('/'))
  218. {
  219. const int endLine = indexOfLineStartingWith (dstLines,
  220. "//[/" + tag + "]",
  221. i + 1);
  222. if (endLine > i)
  223. {
  224. StringArray sourceLines;
  225. if (getUserSection (srcLines, tag, sourceLines))
  226. {
  227. for (int j = endLine - i; --j > 0;)
  228. dstLines.remove (i + 1);
  229. for (int j = 0; j < sourceLines.size(); ++j)
  230. dstLines.insert (++i, sourceLines [j].trimEnd());
  231. ++i;
  232. }
  233. else
  234. {
  235. i = endLine;
  236. }
  237. }
  238. }
  239. }
  240. dstLines.set (i, dstLines[i].trimEnd());
  241. }
  242. dest = dstLines.joinIntoString ("\n") + "\n";
  243. }
  244. //==============================================================================
  245. void GeneratedCode::applyToCode (String& code,
  246. const File& targetFile,
  247. const String& oldFileWithUserData,
  248. Project* project) const
  249. {
  250. replaceTemplate (code, "version", JUCEApplicationBase::getInstance()->getApplicationVersion());
  251. replaceTemplate (code, "creationTime", Time::getCurrentTime().toString (true, true, true));
  252. replaceTemplate (code, "className", className);
  253. replaceTemplate (code, "constructorParams", constructorParams);
  254. replaceTemplate (code, "initialisers", getInitialiserList());
  255. replaceTemplate (code, "classDeclaration", getClassDeclaration());
  256. replaceTemplate (code, "privateMemberDeclarations", privateMemberDeclarations);
  257. replaceTemplate (code, "publicMemberDeclarations", getCallbackDeclarations() + newLine + publicMemberDeclarations);
  258. replaceTemplate (code, "methodDefinitions", getCallbackDefinitions());
  259. File juceHeaderFile = project != nullptr ? project->getAppIncludeFile()
  260. : targetFile.getSiblingFile ("JuceHeader.h");
  261. replaceTemplate (code, "includeJUCEHeader", CodeHelpers::createIncludeStatement (juceHeaderFile, targetFile));
  262. replaceTemplate (code, "includeFilesH", getIncludeFileCode (includeFilesH, targetFile));
  263. replaceTemplate (code, "includeFilesCPP", getIncludeFileCode (includeFilesCPP, targetFile));
  264. replaceTemplate (code, "constructor", constructorCode);
  265. replaceTemplate (code, "destructor", destructorCode);
  266. replaceTemplate (code, "metadata", jucerMetadata);
  267. replaceTemplate (code, "staticMemberDefinitions", staticMemberDefinitions);
  268. copyAcrossUserSections (code, oldFileWithUserData);
  269. }