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.

377 lines
12KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library - "Jules' Utility Class Extensions"
  4. Copyright 2004-11 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 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. const String initialiser (imageMemberName + " (0)");
  74. if (! initialisers.contains (initialiser, false))
  75. {
  76. initialisers.add (initialiser);
  77. privateMemberDeclarations
  78. << "Image " << imageMemberName << ";\n";
  79. if (resourceName.isNotEmpty())
  80. {
  81. constructorCode
  82. << imageMemberName << " = ImageCache::getFromMemory ("
  83. << resourceName << ", " << resourceName << "Size);\n";
  84. }
  85. }
  86. }
  87. StringArray GeneratedCode::getExtraParentClasses() const
  88. {
  89. StringArray s;
  90. for (int i = 0; i < callbacks.size(); ++i)
  91. {
  92. CallbackMethod* const cm = callbacks.getUnchecked(i);
  93. s.add (cm->requiredParentClass);
  94. }
  95. return s;
  96. }
  97. String GeneratedCode::getCallbackDeclarations() const
  98. {
  99. String s;
  100. for (int i = 0; i < callbacks.size(); ++i)
  101. {
  102. CallbackMethod* const cm = callbacks.getUnchecked(i);
  103. s << cm->returnType << " " << cm->prototype << ";\n";
  104. }
  105. return s;
  106. }
  107. String GeneratedCode::getCallbackDefinitions() const
  108. {
  109. String s;
  110. for (int i = 0; i < callbacks.size(); ++i)
  111. {
  112. CallbackMethod* const cm = callbacks.getUnchecked(i);
  113. const String userCodeBlockName ("User"
  114. + CodeHelpers::makeValidIdentifier (cm->prototype.upToFirstOccurrenceOf ("(", false, false),
  115. true, true, false).trim());
  116. if (userCodeBlockName.isNotEmpty() && cm->hasPrePostUserSections)
  117. {
  118. s << cm->returnType << " " << className << "::" << cm->prototype
  119. << "\n{\n //[" << userCodeBlockName << "_Pre]\n //[/" << userCodeBlockName
  120. << "_Pre]\n\n "
  121. << CodeHelpers::indent (cm->content.trim(), 4, false)
  122. << "\n\n //[" << userCodeBlockName << "_Post]\n //[/" << userCodeBlockName
  123. << "_Post]\n}\n\n";
  124. }
  125. else
  126. {
  127. s << cm->returnType << " " << className << "::" << cm->prototype
  128. << "\n{\n "
  129. << CodeHelpers::indent (cm->content.trim(), 4, false)
  130. << "\n}\n\n";
  131. }
  132. }
  133. return s;
  134. }
  135. //==============================================================================
  136. String GeneratedCode::getClassDeclaration() const
  137. {
  138. StringArray parentClassLines;
  139. parentClassLines.addTokens (parentClasses, ",", String::empty);
  140. parentClassLines.addArray (getExtraParentClasses());
  141. parentClassLines.trim();
  142. parentClassLines.removeEmptyStrings();
  143. parentClassLines.removeDuplicates (false);
  144. if (parentClassLines.contains ("public Button", false))
  145. parentClassLines.removeString ("public Component", false);
  146. String r ("class ");
  147. r << className << " : ";
  148. r += parentClassLines.joinIntoString (",\n" + String::repeatedString (" ", r.length()));
  149. return r;
  150. }
  151. String GeneratedCode::getInitialiserList() const
  152. {
  153. StringArray inits (initialisers);
  154. if (parentClassInitialiser.isNotEmpty())
  155. inits.insert (0, parentClassInitialiser);
  156. inits.trim();
  157. inits.removeEmptyStrings();
  158. inits.removeDuplicates (false);
  159. String s;
  160. if (inits.size() == 0)
  161. return s;
  162. s << " : ";
  163. for (int i = 0; i < inits.size(); ++i)
  164. {
  165. String init (inits[i]);
  166. while (init.endsWithChar (','))
  167. init = init.dropLastCharacters (1);
  168. s << init;
  169. if (i < inits.size() - 1)
  170. s << ",\n ";
  171. else
  172. s << "\n";
  173. }
  174. return s;
  175. }
  176. static String getIncludeFileCode (StringArray files)
  177. {
  178. files.trim();
  179. files.removeEmptyStrings();
  180. files.removeDuplicates (false);
  181. String s;
  182. for (int i = 0; i < files.size(); ++i)
  183. s << "#include \"" << files[i] << "\"\n";
  184. return s;
  185. }
  186. //==============================================================================
  187. static void replaceTemplate (String& text, const String& itemName, const String& value)
  188. {
  189. for (;;)
  190. {
  191. const int index = text.indexOf ("%%" + itemName + "%%");
  192. if (index < 0)
  193. break;
  194. int indentLevel = 0;
  195. for (int i = index; --i >= 0;)
  196. {
  197. if (text[i] == '\n')
  198. break;
  199. ++indentLevel;
  200. }
  201. text = text.replaceSection (index, itemName.length() + 4,
  202. CodeHelpers::indent (value, indentLevel, false));
  203. }
  204. }
  205. //==============================================================================
  206. static bool getUserSection (const StringArray& lines, const String& tag, StringArray& resultLines)
  207. {
  208. const int start = indexOfLineStartingWith (lines, "//[" + tag + "]", 0);
  209. if (start < 0)
  210. return false;
  211. const int end = indexOfLineStartingWith (lines, "//[/" + tag + "]", start + 1);
  212. for (int i = start + 1; i < end; ++i)
  213. resultLines.add (lines [i]);
  214. return true;
  215. }
  216. static void copyAcrossUserSections (String& dest, const String& src)
  217. {
  218. StringArray srcLines, dstLines;
  219. srcLines.addLines (src);
  220. dstLines.addLines (dest);
  221. for (int i = 0; i < dstLines.size(); ++i)
  222. {
  223. if (dstLines[i].trimStart().startsWith ("//["))
  224. {
  225. String tag (dstLines[i].trimStart().substring (3));
  226. tag = tag.upToFirstOccurrenceOf ("]", false, false);
  227. jassert (! tag.startsWithChar ('/'));
  228. if (! tag.startsWithChar ('/'))
  229. {
  230. const int endLine = indexOfLineStartingWith (dstLines,
  231. "//[/" + tag + "]",
  232. i + 1);
  233. if (endLine > i)
  234. {
  235. StringArray sourceLines;
  236. if (getUserSection (srcLines, tag, sourceLines))
  237. {
  238. int j;
  239. for (j = endLine - i; --j > 0;)
  240. dstLines.remove (i + 1);
  241. for (j = 0; j < sourceLines.size(); ++j)
  242. dstLines.insert (++i, sourceLines [j].trimEnd());
  243. ++i;
  244. }
  245. else
  246. {
  247. i = endLine;
  248. }
  249. }
  250. }
  251. }
  252. dstLines.set (i, dstLines[i].trimEnd());
  253. }
  254. dest = dstLines.joinIntoString ("\n") + "\n";
  255. }
  256. //==============================================================================
  257. void GeneratedCode::applyToCode (String& code,
  258. const String& fileNameRoot,
  259. const bool isForPreview,
  260. const String& oldFileWithUserData) const
  261. {
  262. // header guard..
  263. String headerGuard ("__JUCE_HEADER_");
  264. headerGuard << String::toHexString ((className + "xx" + fileNameRoot).hashCode64()).toUpperCase() << "__";
  265. replaceTemplate (code, "headerGuard", headerGuard);
  266. replaceTemplate (code, "version", JUCEApplication::getInstance()->getApplicationVersion());
  267. replaceTemplate (code, "creationTime", Time::getCurrentTime().toString (true, true, true));
  268. replaceTemplate (code, "className", className);
  269. replaceTemplate (code, "constructorParams", constructorParams);
  270. replaceTemplate (code, "initialisers", getInitialiserList());
  271. replaceTemplate (code, "classDeclaration", getClassDeclaration());
  272. replaceTemplate (code, "privateMemberDeclarations", privateMemberDeclarations);
  273. replaceTemplate (code, "publicMemberDeclarations", getCallbackDeclarations() + "\n" + publicMemberDeclarations);
  274. replaceTemplate (code, "methodDefinitions", getCallbackDefinitions());
  275. replaceTemplate (code, "includeFilesH", getIncludeFileCode (includeFilesH));
  276. replaceTemplate (code, "includeFilesCPP", getIncludeFileCode (includeFilesCPP));
  277. replaceTemplate (code, "constructor", constructorCode);
  278. replaceTemplate (code, "destructor", destructorCode);
  279. if (! isForPreview)
  280. {
  281. replaceTemplate (code, "metadata", jucerMetadata);
  282. replaceTemplate (code, "staticMemberDefinitions", staticMemberDefinitions);
  283. }
  284. else
  285. {
  286. replaceTemplate (code, "metadata", " << Metadata isn't shown in the code preview >>\n");
  287. replaceTemplate (code, "staticMemberDefinitions", "// Static member declarations and resources would go here... (these aren't shown in the code preview)");
  288. }
  289. copyAcrossUserSections (code, oldFileWithUserData);
  290. }