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.

342 lines
11KB

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