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.

356 lines
11KB

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