paulxstretch/deps/juce/extras/Projucer/Source/ComponentEditor/jucer_JucerDocument.cpp
essej 25bd5d8adb git subrepo clone --branch=sono6good https://github.com/essej/JUCE.git deps/juce
subrepo:
  subdir:   "deps/juce"
  merged:   "b13f9084e"
upstream:
  origin:   "https://github.com/essej/JUCE.git"
  branch:   "sono6good"
  commit:   "b13f9084e"
git-subrepo:
  version:  "0.4.3"
  origin:   "https://github.com/ingydotnet/git-subrepo.git"
  commit:   "2f68596"
2022-04-18 17:51:22 -04:00

836 lines
30 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 "../Application/jucer_Application.h"
#include "../Utility/Helpers/jucer_NewFileWizard.h"
#include "jucer_JucerDocument.h"
#include "jucer_ObjectTypes.h"
#include "UI/jucer_JucerDocumentEditor.h"
#include "UI/jucer_TestComponent.h"
#include "jucer_UtilityFunctions.h"
#include "Documents/jucer_ComponentDocument.h"
#include "Documents/jucer_ButtonDocument.h"
const char* const defaultClassName = "NewComponent";
const char* const defaultParentClasses = "public juce::Component";
//==============================================================================
JucerDocument::JucerDocument (SourceCodeDocument* c)
: cpp (c),
className (defaultClassName),
parentClasses (defaultParentClasses)
{
jassert (cpp != nullptr);
resources.setDocument (this);
ProjucerApplication::getCommandManager().commandStatusChanged();
cpp->getCodeDocument().addListener (this);
}
JucerDocument::~JucerDocument()
{
cpp->getCodeDocument().removeListener (this);
ProjucerApplication::getCommandManager().commandStatusChanged();
}
//==============================================================================
void JucerDocument::changed()
{
sendChangeMessage();
ProjucerApplication::getCommandManager().commandStatusChanged();
startTimer (800);
}
struct UserDocChangeTimer : public Timer
{
explicit UserDocChangeTimer (JucerDocument& d) : doc (d) {}
void timerCallback() override { doc.reloadFromDocument(); }
JucerDocument& doc;
};
void JucerDocument::userEditedCpp()
{
if (userDocChangeTimer == nullptr)
userDocChangeTimer.reset (new UserDocChangeTimer (*this));
userDocChangeTimer->startTimer (500);
}
void JucerDocument::beginTransaction()
{
getUndoManager().beginNewTransaction();
}
void JucerDocument::beginTransaction (const String& name)
{
getUndoManager().beginNewTransaction (name);
}
void JucerDocument::timerCallback()
{
if (! Component::isMouseButtonDownAnywhere())
{
stopTimer();
beginTransaction();
flushChangesToDocuments (nullptr, false);
}
}
void JucerDocument::codeDocumentTextInserted (const String&, int) { userEditedCpp(); }
void JucerDocument::codeDocumentTextDeleted (int, int) { userEditedCpp(); }
bool JucerDocument::perform (UndoableAction* const action, const String& actionName)
{
return undoManager.perform (action, actionName);
}
void JucerDocument::refreshAllPropertyComps()
{
if (ComponentLayout* l = getComponentLayout())
l->getSelectedSet().changed();
for (int i = getNumPaintRoutines(); --i >= 0;)
{
getPaintRoutine (i)->getSelectedElements().changed();
getPaintRoutine (i)->getSelectedPoints().changed();
}
}
//==============================================================================
void JucerDocument::setClassName (const String& newName)
{
if (newName != className
&& build_tools::makeValidIdentifier (newName, false, false, true).isNotEmpty())
{
className = build_tools::makeValidIdentifier (newName, false, false, true);
changed();
}
}
void JucerDocument::setComponentName (const String& newName)
{
if (newName != componentName)
{
componentName = newName;
changed();
}
}
void JucerDocument::setParentClasses (const String& classes)
{
if (classes != parentClasses)
{
StringArray parentClassLines (getCleanedStringArray (StringArray::fromTokens (classes, ",", StringRef())));
for (int i = parentClassLines.size(); --i >= 0;)
{
String s (parentClassLines[i]);
String type;
if (s.startsWith ("public ")
|| s.startsWith ("protected ")
|| s.startsWith ("private "))
{
type = s.upToFirstOccurrenceOf (" ", true, false);
s = s.fromFirstOccurrenceOf (" ", false, false);
if (s.trim().isEmpty())
type = s = String();
}
s = type + build_tools::makeValidIdentifier (s.trim(), false, false, true, true);
parentClassLines.set (i, s);
}
parentClasses = parentClassLines.joinIntoString (", ");
changed();
}
}
void JucerDocument::setConstructorParams (const String& newParams)
{
if (constructorParams != newParams)
{
constructorParams = newParams;
changed();
}
}
void JucerDocument::setVariableInitialisers (const String& newInitlialisers)
{
if (variableInitialisers != newInitlialisers)
{
variableInitialisers = newInitlialisers;
changed();
}
}
void JucerDocument::setFixedSize (const bool isFixed)
{
if (fixedSize != isFixed)
{
fixedSize = isFixed;
changed();
}
}
void JucerDocument::setInitialSize (int w, int h)
{
w = jmax (1, w);
h = jmax (1, h);
if (initialWidth != w || initialHeight != h)
{
initialWidth = w;
initialHeight = h;
changed();
}
}
//==============================================================================
bool JucerDocument::isSnapActive (const bool disableIfCtrlKeyDown) const noexcept
{
return snapActive != (disableIfCtrlKeyDown && ModifierKeys::currentModifiers.isCtrlDown());
}
int JucerDocument::snapPosition (int pos) const noexcept
{
if (isSnapActive (true))
{
jassert (snapGridPixels > 0);
pos = ((pos + snapGridPixels * 1024 + snapGridPixels / 2) / snapGridPixels - 1024) * snapGridPixels;
}
return pos;
}
void JucerDocument::setSnappingGrid (const int numPixels, const bool active, const bool shown)
{
if (numPixels != snapGridPixels
|| active != snapActive
|| shown != snapShown)
{
snapGridPixels = numPixels;
snapActive = active;
snapShown = shown;
changed();
}
}
void JucerDocument::setComponentOverlayOpacity (const float alpha)
{
if (alpha != componentOverlayOpacity)
{
componentOverlayOpacity = alpha;
changed();
}
}
//==============================================================================
void JucerDocument::addMethod (const String& base, const String& returnVal, const String& method, const String& initialContent,
StringArray& baseClasses, StringArray& returnValues, StringArray& methods, StringArray& initialContents)
{
baseClasses.add (base);
returnValues.add (returnVal);
methods.add (method);
initialContents.add (initialContent);
}
void JucerDocument::getOptionalMethods (StringArray& baseClasses,
StringArray& returnValues,
StringArray& methods,
StringArray& initialContents) const
{
addMethod ("juce::Component", "void", "visibilityChanged()", "", baseClasses, returnValues, methods, initialContents);
addMethod ("juce::Component", "void", "moved()", "", baseClasses, returnValues, methods, initialContents);
addMethod ("juce::Component", "void", "parentHierarchyChanged()", "", baseClasses, returnValues, methods, initialContents);
addMethod ("juce::Component", "void", "parentSizeChanged()", "", baseClasses, returnValues, methods, initialContents);
addMethod ("juce::Component", "void", "lookAndFeelChanged()", "", baseClasses, returnValues, methods, initialContents);
addMethod ("juce::Component", "bool", "hitTest (int x, int y)", "return true;", baseClasses, returnValues, methods, initialContents);
addMethod ("juce::Component", "void", "broughtToFront()", "", baseClasses, returnValues, methods, initialContents);
addMethod ("juce::Component", "void", "filesDropped (const juce::StringArray& filenames, int mouseX, int mouseY)", "", baseClasses, returnValues, methods, initialContents);
addMethod ("juce::Component", "void", "handleCommandMessage (int commandId)", "", baseClasses, returnValues, methods, initialContents);
addMethod ("juce::Component", "void", "childrenChanged()", "", baseClasses, returnValues, methods, initialContents);
addMethod ("juce::Component", "void", "enablementChanged()", "", baseClasses, returnValues, methods, initialContents);
addMethod ("juce::Component", "void", "mouseMove (const juce::MouseEvent& e)", "", baseClasses, returnValues, methods, initialContents);
addMethod ("juce::Component", "void", "mouseEnter (const juce::MouseEvent& e)", "", baseClasses, returnValues, methods, initialContents);
addMethod ("juce::Component", "void", "mouseExit (const juce::MouseEvent& e)", "", baseClasses, returnValues, methods, initialContents);
addMethod ("juce::Component", "void", "mouseDown (const juce::MouseEvent& e)", "", baseClasses, returnValues, methods, initialContents);
addMethod ("juce::Component", "void", "mouseDrag (const juce::MouseEvent& e)", "", baseClasses, returnValues, methods, initialContents);
addMethod ("juce::Component", "void", "mouseUp (const juce::MouseEvent& e)", "", baseClasses, returnValues, methods, initialContents);
addMethod ("juce::Component", "void", "mouseDoubleClick (const juce::MouseEvent& e)", "", baseClasses, returnValues, methods, initialContents);
addMethod ("juce::Component", "void", "mouseWheelMove (const juce::MouseEvent& e, const juce::MouseWheelDetails& wheel)", "", baseClasses, returnValues, methods, initialContents);
addMethod ("juce::Component", "bool", "keyPressed (const juce::KeyPress& key)", "return false; // Return true if your handler uses this key event, or false to allow it to be passed-on.", baseClasses, returnValues, methods, initialContents);
addMethod ("juce::Component", "bool", "keyStateChanged (bool isKeyDown)", "return false; // Return true if your handler uses this key event, or false to allow it to be passed-on.", baseClasses, returnValues, methods, initialContents);
addMethod ("juce::Component", "void", "modifierKeysChanged (const juce::ModifierKeys& modifiers)", "", baseClasses, returnValues, methods, initialContents);
addMethod ("juce::Component", "void", "focusGained (FocusChangeType cause)", "", baseClasses, returnValues, methods, initialContents);
addMethod ("juce::Component", "void", "focusLost (FocusChangeType cause)", "", baseClasses, returnValues, methods, initialContents);
addMethod ("juce::Component", "void", "focusOfChildComponentChanged (FocusChangeType cause)", "", baseClasses, returnValues, methods, initialContents);
addMethod ("juce::Component", "void", "modifierKeysChanged (const juce::ModifierKeys& modifiers)", "", baseClasses, returnValues, methods, initialContents);
addMethod ("juce::Component", "void", "inputAttemptWhenModal()", "", baseClasses, returnValues, methods, initialContents);
}
void JucerDocument::setOptionalMethodEnabled (const String& methodSignature, const bool enable)
{
if (enable)
activeExtraMethods.addIfNotAlreadyThere (methodSignature);
else
activeExtraMethods.removeString (methodSignature);
changed();
}
bool JucerDocument::isOptionalMethodEnabled (const String& sig) const noexcept
{
return activeExtraMethods.contains (sig)
|| activeExtraMethods.contains (sig.replace ("juce::", {}));
}
void JucerDocument::addExtraClassProperties (PropertyPanel&)
{
}
//==============================================================================
const char* const JucerDocument::jucerCompXmlTag = "JUCER_COMPONENT";
std::unique_ptr<XmlElement> JucerDocument::createXml() const
{
auto doc = std::make_unique<XmlElement> (jucerCompXmlTag);
doc->setAttribute ("documentType", getTypeName());
doc->setAttribute ("className", className);
if (templateFile.trim().isNotEmpty())
doc->setAttribute ("template", templateFile);
doc->setAttribute ("componentName", componentName);
doc->setAttribute ("parentClasses", parentClasses);
doc->setAttribute ("constructorParams", constructorParams);
doc->setAttribute ("variableInitialisers", variableInitialisers);
doc->setAttribute ("snapPixels", snapGridPixels);
doc->setAttribute ("snapActive", snapActive);
doc->setAttribute ("snapShown", snapShown);
doc->setAttribute ("overlayOpacity", String (componentOverlayOpacity, 3));
doc->setAttribute ("fixedSize", fixedSize);
doc->setAttribute ("initialWidth", initialWidth);
doc->setAttribute ("initialHeight", initialHeight);
if (activeExtraMethods.size() > 0)
{
XmlElement* extraMethods = new XmlElement ("METHODS");
doc->addChildElement (extraMethods);
for (int i = 0; i < activeExtraMethods.size(); ++i)
{
XmlElement* e = new XmlElement ("METHOD");
extraMethods ->addChildElement (e);
e->setAttribute ("name", activeExtraMethods[i]);
}
}
return doc;
}
bool JucerDocument::loadFromXml (const XmlElement& xml)
{
if (xml.hasTagName (jucerCompXmlTag)
&& getTypeName().equalsIgnoreCase (xml.getStringAttribute ("documentType")))
{
className = xml.getStringAttribute ("className", defaultClassName);
templateFile = xml.getStringAttribute ("template", String());
componentName = xml.getStringAttribute ("componentName", String());
parentClasses = xml.getStringAttribute ("parentClasses", defaultParentClasses);
constructorParams = xml.getStringAttribute ("constructorParams", String());
variableInitialisers = xml.getStringAttribute ("variableInitialisers", String());
fixedSize = xml.getBoolAttribute ("fixedSize", false);
initialWidth = xml.getIntAttribute ("initialWidth", 300);
initialHeight = xml.getIntAttribute ("initialHeight", 200);
snapGridPixels = xml.getIntAttribute ("snapPixels", snapGridPixels);
snapActive = xml.getBoolAttribute ("snapActive", snapActive);
snapShown = xml.getBoolAttribute ("snapShown", snapShown);
componentOverlayOpacity = (float) xml.getDoubleAttribute ("overlayOpacity", 0.0);
activeExtraMethods.clear();
if (XmlElement* const methods = xml.getChildByName ("METHODS"))
for (auto* e : methods->getChildWithTagNameIterator ("METHOD"))
activeExtraMethods.addIfNotAlreadyThere (e->getStringAttribute ("name"));
activeExtraMethods.trim();
activeExtraMethods.removeEmptyStrings();
changed();
getUndoManager().clearUndoHistory();
return true;
}
return false;
}
//==============================================================================
void JucerDocument::fillInGeneratedCode (GeneratedCode& code) const
{
code.className = className;
code.componentName = componentName;
code.parentClasses = parentClasses;
code.constructorParams = constructorParams;
code.initialisers.addLines (variableInitialisers);
if (! componentName.isEmpty())
code.constructorCode << "setName (" + quotedString (componentName, false) + ");\n";
// call these now, just to make sure they're the first two methods in the list.
code.getCallbackCode (String(), "void", "paint (juce::Graphics& g)", false)
<< "//[UserPrePaint] Add your own custom painting code here..\n//[/UserPrePaint]\n\n";
code.getCallbackCode (String(), "void", "resized()", false)
<< "//[UserPreResize] Add your own custom resize code here..\n//[/UserPreResize]\n\n";
if (ComponentLayout* l = getComponentLayout())
l->fillInGeneratedCode (code);
fillInPaintCode (code);
std::unique_ptr<XmlElement> e (createXml());
jassert (e != nullptr);
code.jucerMetadata = e->toString (XmlElement::TextFormat().withoutHeader());
resources.fillInGeneratedCode (code);
code.constructorCode
<< "\n//[UserPreSize]\n"
"//[/UserPreSize]\n";
if (initialWidth > 0 || initialHeight > 0)
code.constructorCode << "\nsetSize (" << initialWidth << ", " << initialHeight << ");\n";
code.getCallbackCode (String(), "void", "paint (juce::Graphics& g)", false)
<< "//[UserPaint] Add your own custom painting code here..\n//[/UserPaint]";
code.getCallbackCode (String(), "void", "resized()", false)
<< "//[UserResized] Add your own custom resize handling here..\n//[/UserResized]";
// add optional methods
StringArray baseClasses, returnValues, methods, initialContents;
getOptionalMethods (baseClasses, returnValues, methods, initialContents);
for (int i = 0; i < methods.size(); ++i)
{
if (isOptionalMethodEnabled (methods[i]))
{
String baseClassToAdd (baseClasses[i]);
if (baseClassToAdd == "juce::Component" || baseClassToAdd == "juce::Button")
baseClassToAdd.clear();
String& s = code.getCallbackCode (baseClassToAdd, returnValues[i], methods[i], false);
if (! s.contains ("//["))
{
String userCommentTag ("UserCode_");
userCommentTag += methods[i].upToFirstOccurrenceOf ("(", false, false).trim();
s << "\n//[" << userCommentTag << "] -- Add your code here...\n"
<< initialContents[i];
if (initialContents[i].isNotEmpty() && ! initialContents[i].endsWithChar ('\n'))
s << '\n';
s << "//[/" << userCommentTag << "]\n";
}
}
}
}
void JucerDocument::fillInPaintCode (GeneratedCode& code) const
{
for (int i = 0; i < getNumPaintRoutines(); ++i)
getPaintRoutine (i)
->fillInGeneratedCode (code, code.getCallbackCode (String(), "void", "paint (juce::Graphics& g)", false));
}
void JucerDocument::setTemplateFile (const String& newFile)
{
if (templateFile != newFile)
{
templateFile = newFile;
changed();
}
}
//==============================================================================
bool JucerDocument::findTemplateFiles (String& headerContent, String& cppContent) const
{
if (templateFile.isNotEmpty())
{
const File f (getCppFile().getSiblingFile (templateFile));
const File templateCpp (f.withFileExtension (".cpp"));
const File templateH (f.withFileExtension (".h"));
headerContent = templateH.loadFileAsString();
cppContent = templateCpp.loadFileAsString();
if (headerContent.isNotEmpty() && cppContent.isNotEmpty())
return true;
}
headerContent = BinaryData::jucer_ComponentTemplate_h;
cppContent = BinaryData::jucer_ComponentTemplate_cpp;
return true;
}
bool JucerDocument::flushChangesToDocuments (Project* project, bool isInitial)
{
String headerTemplate, cppTemplate;
if (! findTemplateFiles (headerTemplate, cppTemplate))
return false;
GeneratedCode generated (this);
fillInGeneratedCode (generated);
const File headerFile (getHeaderFile());
generated.includeFilesCPP.insert (0, headerFile);
OpenDocumentManager& odm = ProjucerApplication::getApp().openDocumentManager;
if (SourceCodeDocument* header = dynamic_cast<SourceCodeDocument*> (odm.openFile (nullptr, headerFile)))
{
String existingHeader (header->getCodeDocument().getAllContent());
String existingCpp (cpp->getCodeDocument().getAllContent());
generated.applyToCode (headerTemplate, headerFile, existingHeader);
generated.applyToCode (cppTemplate, headerFile.withFileExtension (".cpp"), existingCpp);
if (isInitial)
{
jassert (project != nullptr);
auto lineFeed = project->getProjectLineFeed();
headerTemplate = replaceLineFeeds (headerTemplate, lineFeed);
cppTemplate = replaceLineFeeds (cppTemplate, lineFeed);
}
else
{
headerTemplate = replaceLineFeeds (headerTemplate, getLineFeedForFile (existingHeader));
cppTemplate = replaceLineFeeds (cppTemplate, getLineFeedForFile (existingCpp));
}
if (header->getCodeDocument().getAllContent() != headerTemplate)
header->getCodeDocument().replaceAllContent (headerTemplate);
if (cpp->getCodeDocument().getAllContent() != cppTemplate)
cpp->getCodeDocument().replaceAllContent (cppTemplate);
}
userDocChangeTimer.reset();
return true;
}
bool JucerDocument::reloadFromDocument()
{
const String cppContent (cpp->getCodeDocument().getAllContent());
std::unique_ptr<XmlElement> newXML (pullMetaDataFromCppFile (cppContent));
if (newXML == nullptr || ! newXML->hasTagName (jucerCompXmlTag))
return false;
if (currentXML != nullptr && currentXML->isEquivalentTo (newXML.get(), true))
return true;
currentXML.reset (newXML.release());
stopTimer();
resources.loadFromCpp (getCppFile(), cppContent);
bool result = loadFromXml (*currentXML);
extractCustomPaintSnippetsFromCppFile (cppContent);
return result;
}
void JucerDocument::refreshCustomCodeFromDocument()
{
const String cppContent (cpp->getCodeDocument().getAllContent());
extractCustomPaintSnippetsFromCppFile (cppContent);
}
void JucerDocument::extractCustomPaintSnippetsFromCppFile (const String& cppContent)
{
StringArray customPaintSnippets;
auto lines = StringArray::fromLines (cppContent);
int last = 0;
while (last >= 0)
{
const int start = indexOfLineStartingWith (lines, "//[UserPaintCustomArguments]", last);
if (start < 0)
break;
const int end = indexOfLineStartingWith (lines, "//[/UserPaintCustomArguments]", start);
if (end < 0)
break;
last = end + 1;
String result;
for (int i = start + 1; i < end; ++i)
result << lines [i] << newLine;
customPaintSnippets.add (CodeHelpers::unindent (result, 4));
}
applyCustomPaintSnippets (customPaintSnippets);
}
std::unique_ptr<XmlElement> JucerDocument::pullMetaDataFromCppFile (const String& cpp)
{
auto lines = StringArray::fromLines (cpp);
auto startLine = indexOfLineStartingWith (lines, "BEGIN_JUCER_METADATA", 0);
if (startLine > 0)
{
auto endLine = indexOfLineStartingWith (lines, "END_JUCER_METADATA", startLine);
if (endLine > startLine)
return parseXML (lines.joinIntoString ("\n", startLine + 1, endLine - startLine - 1));
}
return nullptr;
}
bool JucerDocument::isValidJucerCppFile (const File& f)
{
if (f.hasFileExtension (cppFileExtensions))
{
std::unique_ptr<XmlElement> xml (pullMetaDataFromCppFile (f.loadFileAsString()));
if (xml != nullptr)
return xml->hasTagName (jucerCompXmlTag);
}
return false;
}
static JucerDocument* createDocument (SourceCodeDocument* cpp)
{
auto& codeDoc = cpp->getCodeDocument();
std::unique_ptr<XmlElement> xml (JucerDocument::pullMetaDataFromCppFile (codeDoc.getAllContent()));
if (xml == nullptr || ! xml->hasTagName (JucerDocument::jucerCompXmlTag))
return nullptr;
const String docType (xml->getStringAttribute ("documentType"));
std::unique_ptr<JucerDocument> newDoc;
if (docType.equalsIgnoreCase ("Button"))
newDoc.reset (new ButtonDocument (cpp));
if (docType.equalsIgnoreCase ("Component") || docType.isEmpty())
newDoc.reset (new ComponentDocument (cpp));
if (newDoc != nullptr && newDoc->reloadFromDocument())
return newDoc.release();
return nullptr;
}
JucerDocument* JucerDocument::createForCppFile (Project* p, const File& file)
{
OpenDocumentManager& odm = ProjucerApplication::getApp().openDocumentManager;
if (SourceCodeDocument* cpp = dynamic_cast<SourceCodeDocument*> (odm.openFile (p, file)))
if (dynamic_cast<SourceCodeDocument*> (odm.openFile (p, file.withFileExtension (".h"))) != nullptr)
return createDocument (cpp);
return nullptr;
}
//==============================================================================
class JucerComponentDocument : public SourceCodeDocument
{
public:
JucerComponentDocument (Project* p, const File& f)
: SourceCodeDocument (p, f)
{
}
void saveAsync (std::function<void (bool)> callback) override
{
SourceCodeDocument::saveAsync ([parent = WeakReference<JucerComponentDocument> { this }, callback] (bool saveResult)
{
if (parent == nullptr)
return;
if (! saveResult)
{
callback (false);
return;
}
parent->saveHeaderAsync ([parent, callback] (bool headerSaveResult)
{
if (parent != nullptr)
callback (headerSaveResult);
});
});
}
void saveHeaderAsync (std::function<void (bool)> callback)
{
auto& odm = ProjucerApplication::getApp().openDocumentManager;
if (auto* header = odm.openFile (nullptr, getFile().withFileExtension (".h")))
{
header->saveAsync ([parent = WeakReference<JucerComponentDocument> { this }, callback] (bool saveResult)
{
if (parent == nullptr)
return;
if (saveResult)
ProjucerApplication::getApp()
.openDocumentManager
.closeFileWithoutSaving (parent->getFile().withFileExtension (".h"));
callback (saveResult);
});
return;
}
callback (false);
}
std::unique_ptr<Component> createEditor() override
{
if (ProjucerApplication::getApp().isGUIEditorEnabled())
{
std::unique_ptr<JucerDocument> jucerDoc (JucerDocument::createForCppFile (getProject(), getFile()));
if (jucerDoc != nullptr)
return std::make_unique<JucerDocumentEditor> (jucerDoc.release());
}
return SourceCodeDocument::createEditor();
}
struct Type : public OpenDocumentManager::DocumentType
{
Type() {}
bool canOpenFile (const File& f) override { return JucerDocument::isValidJucerCppFile (f); }
Document* openFile (Project* p, const File& f) override { return new JucerComponentDocument (p, f); }
};
JUCE_DECLARE_WEAK_REFERENCEABLE (JucerComponentDocument)
};
OpenDocumentManager::DocumentType* createGUIDocumentType();
OpenDocumentManager::DocumentType* createGUIDocumentType()
{
return new JucerComponentDocument::Type();
}
//==============================================================================
struct NewGUIComponentWizard : public NewFileWizard::Type
{
NewGUIComponentWizard (Project& proj)
: project (proj)
{}
String getName() override { return "GUI Component"; }
void createNewFile (Project& p, Project::Item parent) override
{
jassert (&p == &project);
askUserToChooseNewFile (String (defaultClassName) + ".h", "*.h;*.cpp", parent, [this, parent] (File newFile) mutable
{
if (newFile != File())
{
auto headerFile = newFile.withFileExtension (".h");
auto cppFile = newFile.withFileExtension (".cpp");
headerFile.replaceWithText (String());
cppFile.replaceWithText (String());
auto& odm = ProjucerApplication::getApp().openDocumentManager;
if (auto* cpp = dynamic_cast<SourceCodeDocument*> (odm.openFile (&project, cppFile)))
{
if (auto* header = dynamic_cast<SourceCodeDocument*> (odm.openFile (&project, headerFile)))
{
std::unique_ptr<JucerDocument> jucerDoc (new ComponentDocument (cpp));
if (jucerDoc != nullptr)
{
jucerDoc->setClassName (newFile.getFileNameWithoutExtension());
jucerDoc->flushChangesToDocuments (&project, true);
jucerDoc.reset();
for (auto* doc : { cpp, header })
{
doc->saveAsync ([doc] (bool)
{
ProjucerApplication::getApp()
.openDocumentManager
.closeDocumentAsync (doc, OpenDocumentManager::SaveIfNeeded::yes, nullptr);
});
}
parent.addFileRetainingSortOrder (headerFile, true);
parent.addFileRetainingSortOrder (cppFile, true);
}
}
}
}
});
}
Project& project;
};
NewFileWizard::Type* createGUIComponentWizard (Project&);
NewFileWizard::Type* createGUIComponentWizard (Project& p)
{
return new NewGUIComponentWizard (p);
}