349 lines
11 KiB
C++
349 lines
11 KiB
C++
|
/*
|
||
|
==============================================================================
|
||
|
|
||
|
This file is part of the JUCE library.
|
||
|
Copyright (c) 2020 - Raw Material Software Limited
|
||
|
|
||
|
JUCE is an open source library subject to commercial or open-source
|
||
|
licensing.
|
||
|
|
||
|
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||
|
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||
|
|
||
|
End User License Agreement: www.juce.com/juce-6-licence
|
||
|
Privacy Policy: www.juce.com/juce-privacy-policy
|
||
|
|
||
|
Or: You may also use this code under the terms of the GPL v3 (see
|
||
|
www.gnu.org/licenses).
|
||
|
|
||
|
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||
|
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||
|
DISCLAIMED.
|
||
|
|
||
|
==============================================================================
|
||
|
*/
|
||
|
|
||
|
#include "../Application/jucer_Headers.h"
|
||
|
#include "jucer_GeneratedCode.h"
|
||
|
#include "jucer_JucerDocument.h"
|
||
|
|
||
|
//==============================================================================
|
||
|
GeneratedCode::GeneratedCode (const JucerDocument* const doc)
|
||
|
: document (doc), suffix (0)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
GeneratedCode::~GeneratedCode()
|
||
|
{
|
||
|
}
|
||
|
|
||
|
int GeneratedCode::getUniqueSuffix()
|
||
|
{
|
||
|
return ++suffix;
|
||
|
}
|
||
|
|
||
|
//==============================================================================
|
||
|
String& GeneratedCode::getCallbackCode (const String& requiredParentClass,
|
||
|
const String& returnType,
|
||
|
const String& prototype,
|
||
|
const bool hasPrePostUserSections)
|
||
|
{
|
||
|
String parentClass (requiredParentClass);
|
||
|
if (parentClass.isNotEmpty()
|
||
|
&& ! (parentClass.startsWith ("public ")
|
||
|
|| parentClass.startsWith ("private ")
|
||
|
|| parentClass.startsWith ("protected ")))
|
||
|
{
|
||
|
parentClass = "public " + parentClass;
|
||
|
}
|
||
|
|
||
|
for (int i = callbacks.size(); --i >= 0;)
|
||
|
{
|
||
|
CallbackMethod* const cm = callbacks.getUnchecked(i);
|
||
|
|
||
|
if (cm->requiredParentClass == parentClass
|
||
|
&& cm->returnType == returnType
|
||
|
&& cm->prototype == prototype)
|
||
|
return cm->content;
|
||
|
}
|
||
|
|
||
|
CallbackMethod* const cm = new CallbackMethod();
|
||
|
callbacks.add (cm);
|
||
|
|
||
|
cm->requiredParentClass = parentClass;
|
||
|
cm->returnType = returnType;
|
||
|
cm->prototype = prototype;
|
||
|
cm->hasPrePostUserSections = hasPrePostUserSections;
|
||
|
return cm->content;
|
||
|
}
|
||
|
|
||
|
void GeneratedCode::removeCallback (const String& returnType, const String& prototype)
|
||
|
{
|
||
|
for (int i = callbacks.size(); --i >= 0;)
|
||
|
{
|
||
|
CallbackMethod* const cm = callbacks.getUnchecked(i);
|
||
|
|
||
|
if (cm->returnType == returnType && cm->prototype == prototype)
|
||
|
callbacks.remove (i);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void GeneratedCode::addImageResourceLoader (const String& imageMemberName, const String& resourceName)
|
||
|
{
|
||
|
privateMemberDeclarations
|
||
|
<< "juce::Image " << imageMemberName << ";\n";
|
||
|
|
||
|
if (resourceName.isNotEmpty())
|
||
|
constructorCode << imageMemberName << " = juce::ImageCache::getFromMemory ("
|
||
|
<< resourceName << ", " << resourceName << "Size);\n";
|
||
|
}
|
||
|
|
||
|
StringArray GeneratedCode::getExtraParentClasses() const
|
||
|
{
|
||
|
StringArray s;
|
||
|
|
||
|
for (int i = 0; i < callbacks.size(); ++i)
|
||
|
{
|
||
|
CallbackMethod* const cm = callbacks.getUnchecked(i);
|
||
|
s.add (cm->requiredParentClass);
|
||
|
}
|
||
|
|
||
|
return s;
|
||
|
}
|
||
|
|
||
|
String GeneratedCode::getCallbackDeclarations() const
|
||
|
{
|
||
|
String s;
|
||
|
|
||
|
for (int i = 0; i < callbacks.size(); ++i)
|
||
|
{
|
||
|
CallbackMethod* const cm = callbacks.getUnchecked(i);
|
||
|
|
||
|
s << cm->returnType << " " << cm->prototype << " override;\n";
|
||
|
}
|
||
|
|
||
|
return s;
|
||
|
}
|
||
|
|
||
|
String GeneratedCode::getCallbackDefinitions() const
|
||
|
{
|
||
|
String s;
|
||
|
|
||
|
for (int i = 0; i < callbacks.size(); ++i)
|
||
|
{
|
||
|
CallbackMethod* const cm = callbacks.getUnchecked(i);
|
||
|
|
||
|
const String userCodeBlockName ("User"
|
||
|
+ build_tools::makeValidIdentifier (cm->prototype.upToFirstOccurrenceOf ("(", false, false),
|
||
|
true, true, false).trim());
|
||
|
|
||
|
if (userCodeBlockName.isNotEmpty() && cm->hasPrePostUserSections)
|
||
|
{
|
||
|
s << cm->returnType << " " << className << "::" << cm->prototype
|
||
|
<< "\n{\n //[" << userCodeBlockName << "_Pre]\n //[/" << userCodeBlockName
|
||
|
<< "_Pre]\n\n "
|
||
|
<< CodeHelpers::indent (cm->content.trim(), 4, false)
|
||
|
<< "\n\n //[" << userCodeBlockName << "_Post]\n //[/" << userCodeBlockName
|
||
|
<< "_Post]\n}\n\n";
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
s << cm->returnType << " " << className << "::" << cm->prototype
|
||
|
<< "\n{\n "
|
||
|
<< CodeHelpers::indent (cm->content.trim(), 4, false)
|
||
|
<< "\n}\n\n";
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return s;
|
||
|
}
|
||
|
|
||
|
//==============================================================================
|
||
|
String GeneratedCode::getClassDeclaration() const
|
||
|
{
|
||
|
StringArray parentClassLines;
|
||
|
parentClassLines.addTokens (parentClasses, ",", StringRef());
|
||
|
parentClassLines.addArray (getExtraParentClasses());
|
||
|
|
||
|
parentClassLines = getCleanedStringArray (parentClassLines);
|
||
|
|
||
|
if (parentClassLines.contains ("public juce::Button", false))
|
||
|
parentClassLines.removeString ("public juce::Component", false);
|
||
|
|
||
|
String r ("class ");
|
||
|
r << className << " : ";
|
||
|
|
||
|
r += parentClassLines.joinIntoString (",\n" + String::repeatedString (" ", r.length()));
|
||
|
|
||
|
return r;
|
||
|
}
|
||
|
|
||
|
String GeneratedCode::getInitialiserList() const
|
||
|
{
|
||
|
StringArray inits (initialisers);
|
||
|
|
||
|
if (parentClassInitialiser.isNotEmpty())
|
||
|
inits.insert (0, parentClassInitialiser);
|
||
|
|
||
|
inits = getCleanedStringArray (inits);
|
||
|
|
||
|
String s;
|
||
|
|
||
|
if (inits.size() == 0)
|
||
|
return s;
|
||
|
|
||
|
s << " : ";
|
||
|
|
||
|
for (int i = 0; i < inits.size(); ++i)
|
||
|
{
|
||
|
String init (inits[i]);
|
||
|
|
||
|
while (init.endsWithChar (','))
|
||
|
init = init.dropLastCharacters (1);
|
||
|
|
||
|
s << init;
|
||
|
|
||
|
if (i < inits.size() - 1)
|
||
|
s << ",\n ";
|
||
|
else
|
||
|
s << "\n";
|
||
|
}
|
||
|
|
||
|
return s;
|
||
|
}
|
||
|
|
||
|
static String getIncludeFileCode (const Array<File>& files, const File& targetFile)
|
||
|
{
|
||
|
String s;
|
||
|
|
||
|
for (int i = 0; i < files.size(); ++i)
|
||
|
s << CodeHelpers::createIncludeStatement (files.getReference(i), targetFile) << newLine;
|
||
|
|
||
|
return s;
|
||
|
}
|
||
|
|
||
|
bool GeneratedCode::shouldUseTransMacro() const noexcept
|
||
|
{
|
||
|
return document->shouldUseTransMacro();
|
||
|
}
|
||
|
|
||
|
//==============================================================================
|
||
|
static void replaceTemplate (String& text, const String& itemName, const String& value)
|
||
|
{
|
||
|
for (;;)
|
||
|
{
|
||
|
const int index = text.indexOf ("%%" + itemName + "%%");
|
||
|
|
||
|
if (index < 0)
|
||
|
break;
|
||
|
|
||
|
int indentLevel = 0;
|
||
|
|
||
|
for (int i = index; --i >= 0;)
|
||
|
{
|
||
|
if (text[i] == '\n')
|
||
|
break;
|
||
|
|
||
|
++indentLevel;
|
||
|
}
|
||
|
|
||
|
text = text.replaceSection (index, itemName.length() + 4,
|
||
|
CodeHelpers::indent (value, indentLevel, false));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//==============================================================================
|
||
|
static bool getUserSection (const StringArray& lines, const String& tag, StringArray& resultLines)
|
||
|
{
|
||
|
const int start = indexOfLineStartingWith (lines, "//[" + tag + "]", 0);
|
||
|
|
||
|
if (start < 0)
|
||
|
return false;
|
||
|
|
||
|
const int end = indexOfLineStartingWith (lines, "//[/" + tag + "]", start + 1);
|
||
|
|
||
|
for (int i = start + 1; i < end; ++i)
|
||
|
resultLines.add (lines [i]);
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
static void copyAcrossUserSections (String& dest, const String& src)
|
||
|
{
|
||
|
StringArray srcLines, dstLines;
|
||
|
srcLines.addLines (src);
|
||
|
dstLines.addLines (dest);
|
||
|
|
||
|
for (int i = 0; i < dstLines.size(); ++i)
|
||
|
{
|
||
|
if (dstLines[i].trimStart().startsWith ("//["))
|
||
|
{
|
||
|
String tag (dstLines[i].trimStart().substring (3));
|
||
|
tag = tag.upToFirstOccurrenceOf ("]", false, false);
|
||
|
|
||
|
jassert (! tag.startsWithChar ('/'));
|
||
|
|
||
|
if (! tag.startsWithChar ('/'))
|
||
|
{
|
||
|
const int endLine = indexOfLineStartingWith (dstLines,
|
||
|
"//[/" + tag + "]",
|
||
|
i + 1);
|
||
|
|
||
|
if (endLine > i)
|
||
|
{
|
||
|
StringArray sourceLines;
|
||
|
|
||
|
if (tag != "UserPaintCustomArguments" && getUserSection (srcLines, tag, sourceLines))
|
||
|
{
|
||
|
for (int j = endLine - i; --j > 0;)
|
||
|
dstLines.remove (i + 1);
|
||
|
|
||
|
for (int j = 0; j < sourceLines.size(); ++j)
|
||
|
dstLines.insert (++i, sourceLines [j].trimEnd());
|
||
|
|
||
|
++i;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
i = endLine;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
dstLines.set (i, dstLines[i].trimEnd());
|
||
|
}
|
||
|
|
||
|
dest = dstLines.joinIntoString ("\n") + "\n";
|
||
|
}
|
||
|
|
||
|
//==============================================================================
|
||
|
void GeneratedCode::applyToCode (String& code, const File& targetFile, const String& oldFileWithUserData) const
|
||
|
{
|
||
|
replaceTemplate (code, "version", JUCEApplicationBase::getInstance()->getApplicationVersion());
|
||
|
replaceTemplate (code, "creationTime", Time::getCurrentTime().toString (true, true, true));
|
||
|
|
||
|
replaceTemplate (code, "class_name", className);
|
||
|
replaceTemplate (code, "constructor_params", constructorParams);
|
||
|
replaceTemplate (code, "initialisers", getInitialiserList());
|
||
|
|
||
|
replaceTemplate (code, "class_declaration", getClassDeclaration());
|
||
|
replaceTemplate (code, "private_member_declarations", privateMemberDeclarations);
|
||
|
replaceTemplate (code, "public_member_declarations", getCallbackDeclarations() + newLine + publicMemberDeclarations);
|
||
|
|
||
|
replaceTemplate (code, "method_definitions", getCallbackDefinitions());
|
||
|
|
||
|
replaceTemplate (code, "include_juce", CodeHelpers::createIncludePathIncludeStatement (Project::getJuceSourceHFilename()));
|
||
|
|
||
|
replaceTemplate (code, "include_files_h", getIncludeFileCode (includeFilesH, targetFile));
|
||
|
replaceTemplate (code, "include_files_cpp", getIncludeFileCode (includeFilesCPP, targetFile));
|
||
|
|
||
|
replaceTemplate (code, "constructor", constructorCode);
|
||
|
replaceTemplate (code, "destructor", destructorCode);
|
||
|
|
||
|
replaceTemplate (code, "metadata", jucerMetadata);
|
||
|
replaceTemplate (code, "static_member_definitions", staticMemberDefinitions);
|
||
|
|
||
|
copyAcrossUserSections (code, oldFileWithUserData);
|
||
|
}
|