|  | /*
  ==============================================================================
   This file is part of the JUCE library.
   Copyright (c) 2017 - ROLI Ltd.
   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 5 End-User License
   Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
   27th April 2017).
   End User License Agreement: www.juce.com/juce-5-licence
   Privacy Policy: www.juce.com/juce-5-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 "../jucer_Headers.h"
#include "../Application/jucer_Application.h"
#include "../Wizards/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 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
{
    UserDocChangeTimer (JucerDocument& d) : doc (d) {}
    void timerCallback() override       { doc.reloadFromDocument(); }
    JucerDocument& doc;
};
void JucerDocument::userEditedCpp()
{
    if (userDocChangeTimer == nullptr)
        userDocChangeTimer = 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);
    }
}
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
         && CodeHelpers::makeValidIdentifier (newName, false, false, true).isNotEmpty())
    {
        className = CodeHelpers::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 + CodeHelpers::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::getCurrentModifiers().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 ("Component", "void", "visibilityChanged()", "", baseClasses, returnValues, methods, initialContents);
    addMethod ("Component", "void", "moved()", "", baseClasses, returnValues, methods, initialContents);
    addMethod ("Component", "void", "parentHierarchyChanged()", "", baseClasses, returnValues, methods, initialContents);
    addMethod ("Component", "void", "parentSizeChanged()", "", baseClasses, returnValues, methods, initialContents);
    addMethod ("Component", "void", "lookAndFeelChanged()", "", baseClasses, returnValues, methods, initialContents);
    addMethod ("Component", "bool", "hitTest (int x, int y)", "return true;", baseClasses, returnValues, methods, initialContents);
    addMethod ("Component", "void", "broughtToFront()", "", baseClasses, returnValues, methods, initialContents);
    addMethod ("Component", "void", "filesDropped (const StringArray& filenames, int mouseX, int mouseY)", "", baseClasses, returnValues, methods, initialContents);
    addMethod ("Component", "void", "handleCommandMessage (int commandId)", "", baseClasses, returnValues, methods, initialContents);
    addMethod ("Component", "void", "childrenChanged()", "", baseClasses, returnValues, methods, initialContents);
    addMethod ("Component", "void", "enablementChanged()", "", baseClasses, returnValues, methods, initialContents);
    addMethod ("Component", "void", "mouseMove (const MouseEvent& e)", "", baseClasses, returnValues, methods, initialContents);
    addMethod ("Component", "void", "mouseEnter (const MouseEvent& e)", "", baseClasses, returnValues, methods, initialContents);
    addMethod ("Component", "void", "mouseExit (const MouseEvent& e)", "", baseClasses, returnValues, methods, initialContents);
    addMethod ("Component", "void", "mouseDown (const MouseEvent& e)", "", baseClasses, returnValues, methods, initialContents);
    addMethod ("Component", "void", "mouseDrag (const MouseEvent& e)", "", baseClasses, returnValues, methods, initialContents);
    addMethod ("Component", "void", "mouseUp (const MouseEvent& e)", "", baseClasses, returnValues, methods, initialContents);
    addMethod ("Component", "void", "mouseDoubleClick (const MouseEvent& e)", "", baseClasses, returnValues, methods, initialContents);
    addMethod ("Component", "void", "mouseWheelMove (const MouseEvent& e, const MouseWheelDetails& wheel)", "", baseClasses, returnValues, methods, initialContents);
    addMethod ("Component", "bool", "keyPressed (const 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 ("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 ("Component", "void", "modifierKeysChanged (const ModifierKeys& modifiers)", "", baseClasses, returnValues, methods, initialContents);
    addMethod ("Component", "void", "focusGained (FocusChangeType cause)", "", baseClasses, returnValues, methods, initialContents);
    addMethod ("Component", "void", "focusLost (FocusChangeType cause)", "", baseClasses, returnValues, methods, initialContents);
    addMethod ("Component", "void", "focusOfChildComponentChanged (FocusChangeType cause)", "", baseClasses, returnValues, methods, initialContents);
    addMethod ("Component", "void", "modifierKeysChanged (const ModifierKeys& modifiers)", "", baseClasses, returnValues, methods, initialContents);
    addMethod ("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);
}
void JucerDocument::addExtraClassProperties (PropertyPanel&)
{
}
//==============================================================================
const char* const JucerDocument::jucerCompXmlTag = "JUCER_COMPONENT";
XmlElement* JucerDocument::createXml() const
{
    XmlElement* doc = new 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"))
            forEachXmlChildElementWithTagName (*methods, e, "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 (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);
    ScopedPointer<XmlElement> e (createXml());
    jassert (e != nullptr);
    code.jucerMetadata = e->createDocument ("", false, false);
    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 (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 == "Component" || baseClassToAdd == "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 (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;
}
static String fixLineEndings (const String& s)
{
    StringArray lines;
    lines.addLines (s);
    for (int i = 0; i < lines.size(); ++i)
        lines.set (i, lines[i].trimEnd());
    while (lines.size() > 0 && lines [lines.size() - 1].trim().isEmpty())
        lines.remove (lines.size() - 1);
    lines.add (String());
    return lines.joinIntoString ("\r\n");
}
bool JucerDocument::flushChangesToDocuments (Project* project)
{
    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, project);
        generated.applyToCode (cppTemplate, headerFile.withFileExtension (".cpp"),
                               existingCpp, project);
        headerTemplate = fixLineEndings (headerTemplate);
        cppTemplate    = fixLineEndings (cppTemplate);
        if (header->getCodeDocument().getAllContent() != headerTemplate)
            header->getCodeDocument().replaceAllContent (headerTemplate);
        if (cpp->getCodeDocument().getAllContent() != cppTemplate)
            cpp->getCodeDocument().replaceAllContent (cppTemplate);
    }
    userDocChangeTimer = nullptr;
    return true;
}
bool JucerDocument::reloadFromDocument()
{
    const String cppContent (cpp->getCodeDocument().getAllContent());
    ScopedPointer<XmlElement> newXML (pullMetaDataFromCppFile (cppContent));
    if (newXML == nullptr || ! newXML->hasTagName (jucerCompXmlTag))
        return false;
    if (currentXML != nullptr && currentXML->isEquivalentTo (newXML, true))
        return true;
    currentXML = newXML;
    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);
}
XmlElement* JucerDocument::pullMetaDataFromCppFile (const String& cpp)
{
    auto lines = StringArray::fromLines (cpp);
    const int startLine = indexOfLineStartingWith (lines, "BEGIN_JUCER_METADATA", 0);
    if (startLine > 0)
    {
        const int endLine = indexOfLineStartingWith (lines, "END_JUCER_METADATA", startLine);
        if (endLine > startLine)
            return XmlDocument::parse (lines.joinIntoString ("\n", startLine + 1,
                                                             endLine - startLine - 1));
    }
    return nullptr;
}
bool JucerDocument::isValidJucerCppFile (const File& f)
{
    if (f.hasFileExtension (".cpp"))
        if (ScopedPointer<XmlElement> xml = pullMetaDataFromCppFile (f.loadFileAsString()))
            return xml->hasTagName (jucerCompXmlTag);
    return false;
}
static JucerDocument* createDocument (SourceCodeDocument* cpp)
{
    auto& codeDoc = cpp->getCodeDocument();
    ScopedPointer<XmlElement> xml (JucerDocument::pullMetaDataFromCppFile (codeDoc.getAllContent()));
    if (xml == nullptr || ! xml->hasTagName (JucerDocument::jucerCompXmlTag))
        return nullptr;
    const String docType (xml->getStringAttribute ("documentType"));
    ScopedPointer<JucerDocument> newDoc;
    if (docType.equalsIgnoreCase ("Button"))
        newDoc = new ButtonDocument (cpp);
    if (docType.equalsIgnoreCase ("Component") || docType.isEmpty())
        newDoc = 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)
    {
    }
    bool save() override
    {
        return SourceCodeDocument::save() && saveHeader();
    }
    bool saveHeader()
    {
        auto& odm = ProjucerApplication::getApp().openDocumentManager;
        if (auto* header = odm.openFile (nullptr, getFile().withFileExtension (".h")))
        {
            if (header->save())
            {
                odm.closeFile (getFile().withFileExtension(".h"), false);
                return true;
            }
        }
        return false;
    }
    Component* createEditor() override
    {
        if (ScopedPointer<JucerDocument> jucerDoc = JucerDocument::createForCppFile (getProject(), getFile()))
            return new 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); }
    };
};
OpenDocumentManager::DocumentType* createGUIDocumentType()
{
    return new JucerComponentDocument::Type();
}
//==============================================================================
class NewGUIComponentWizard  : public NewFileWizard::Type
{
public:
    NewGUIComponentWizard() {}
    String getName() override  { return "GUI Component"; }
    void createNewFile (Project& project, Project::Item parent) override
    {
        const File newFile (askUserToChooseNewFile (String (defaultClassName) + ".h", "*.h;*.cpp", parent));
        if (newFile != File())
        {
            const File headerFile (newFile.withFileExtension (".h"));
            const File cppFile (newFile.withFileExtension (".cpp"));
            headerFile.replaceWithText (String());
            cppFile.replaceWithText (String());
            auto& odm = ProjucerApplication::getApp().openDocumentManager;
            if (auto* cpp = dynamic_cast<SourceCodeDocument*> (odm.openFile (nullptr, cppFile)))
            {
                if (auto* header = dynamic_cast<SourceCodeDocument*> (odm.openFile (nullptr, headerFile)))
                {
                    if (ScopedPointer<JucerDocument> jucerDoc = new ComponentDocument (cpp))
                    {
                        jucerDoc->setClassName (newFile.getFileNameWithoutExtension());
                        jucerDoc->flushChangesToDocuments (&project);
                        jucerDoc = nullptr;
                        cpp->save();
                        header->save();
                        odm.closeDocument (cpp, true);
                        odm.closeDocument (header, true);
                        parent.addFileRetainingSortOrder (headerFile, true);
                        parent.addFileRetainingSortOrder (cppFile, true);
                    }
                }
            }
        }
    }
};
NewFileWizard::Type* createGUIComponentWizard()
{
    return new NewGUIComponentWizard();
}
 |