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.

349 lines
11KB

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