Skip to content
Snippets Groups Projects
openEphys_ProjectWizard_OpenEphysPlugin.h 23.3 KiB
Newer Older
  • Learn to ignore specific revisions
  •    ------------------------------------------------------------------
    
       This file is part of the Open Ephys GUI
       Copyright (C) 2016 Open Ephys
    
       ------------------------------------------------------------------
    
       This program is free software: you can redistribute it and/or modify
       it under the terms of the GNU General Public License as published by
       the Free Software Foundation, either version 3 of the License, or
       (at your option) any later version.
    
       This program is distributed in the hope that it will be useful,
       but WITHOUT ANY WARRANTY; without even the implied warranty of
       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
       GNU General Public License for more details.
    
       You should have received a copy of the GNU General Public License
       along with this program.  If not, see <http://www.gnu.org/licenses/>.
    
    // ============================================================================
    // ============================================================================
    // ============================================================================
    
    struct OpenEphysPluginAppWizard   : public NewProjectWizard
    {
    
        OpenEphysPluginAppWizard()
        {
            modulesFolder = File::getCurrentWorkingDirectory()
                                        .getParentDirectory().getParentDirectory().getParentDirectory()
    
                                        .getChildFile ("JuceLibraryCode").getChildFile ("modules");
    
    
        String getName() const override         { return TRANS("Open-Ephys Plug-In"); }
        String getDescription() const override  { return TRANS("Creates an Open-Ephys neuro plug-in."); }
        const char* getIcon() const override    { return BinaryData::wizard_AudioPlugin_svg; }
    
        StringArray getDefaultModules() override
        {
            //StringArray s (NewProjectWizard::getDefaultModules());
            //s.add ("juce_audio_plugin_client");
    
            // TODO <Kirill A>
            // Just an empty modules for now
            StringArray s;
            return s;
        }
    
    
        Result getResultsFromConfigPage (const PluginTemplatesPageComponent* configPage)
        {
            m_pluginType    = configPage->getSelectedPluginType();
            m_processorType = configPage->getSelectedProcessorType();
    
            m_shouldUseVisualizerEditor = configPage->shouldUseVisualizerEditor();
    
    
            m_guiTemplateName           = configPage->getSelectedTemplateName();
            m_guiVisualizerTemplateName = configPage->getSelectedVisualizerTemplateName();
    
    
            DBG (String ("GUI Template name: ") + m_guiTemplateName);
    
            if (m_shouldUseVisualizerEditor)
    
                DBG (String ("GUI Visualizer template name: ") + m_guiVisualizerTemplateName);
    
            m_contentLookAndFeelClassName = configPage->getSelectedLookAndFeelClassName();
            m_shouldChangeContentLookAndFeel = (m_contentLookAndFeelClassName != "DEFAULT");
    
            if (m_shouldChangeContentLookAndFeel)
                DBG (String ("Content LookAndFeel name: ") + m_contentLookAndFeelClassName);
    
    
        bool initialiseProject (Project& project) override
        {
            createSourceFolder();
    
    
            if (auto templatesPage = dynamic_cast<PluginTemplatesPageComponent*>
                    (ownerWizardComp->findParentComponentOfClass<StartPageComponent>()
                        ->getPluginTemplatesPage()))
            {
                getResultsFromConfigPage (templatesPage);
            }
    
    
            String pluginProcessorName  = CodeHelpers::makeValidIdentifier (appTitle, true, true, false) + "Processor";
            pluginProcessorName         = pluginProcessorName.substring (0, 1).toUpperCase() + pluginProcessorName.substring (1);
    
    
            const String pluginEditorName     = pluginProcessorName + "Editor";
            const String processorType        = getProcessorTypeString (m_processorType);
            const String pluginFriendlyName   = appTitle;
    
    
            DBG (String ("Processor type: ") + processorType);
    
    
            const String pluginContentComponentName = pluginProcessorName + "ContentComponent";
    
            project.getProjectTypeValue() = ProjectType_OpenEphysPlugin::getTypeName();
    
            project.setPluginType (m_pluginType);
            project.setPluginProcessorType (m_processorType);
    
    
            Project::Item sourceGroup (createSourceGroup (project));
            project.getConfigFlag ("JUCE_QUICKTIME") = Project::configFlagDisabled; // disabled because it interferes with RTAS build on PC
    
            project.shouldBuildVST().setValue (false);
            project.shouldBuildVST3().setValue (false);
    
    
            setExecutableNameForAllTargets (project, File::createLegalFileName (appTitle));
    
    
            //String appHeaders (CodeHelpers::createIncludeStatement (project.getAppIncludeFile(), filterCppFile));
    
            generatePluginMakeFile  (project, sourceGroup);
            generatePluginLibFile   (project, sourceGroup, pluginProcessorName, pluginFriendlyName);
            generatePluginProcessorFiles (project, sourceGroup, pluginProcessorName, pluginEditorName, pluginFriendlyName);
    
            // No need to generate content component files for any plugins except Processors.
            if (m_pluginType != Plugin::PLUGIN_TYPE_PROCESSOR)
    
    Septen's avatar
    Septen committed
                return true;
    
    
            generatePluginEditorFiles    (project, sourceGroup, pluginProcessorName, pluginEditorName, pluginFriendlyName, pluginContentComponentName);
            generatePluginEditorContentComponentFiles (project, sourceGroup, pluginProcessorName, pluginEditorName, pluginContentComponentName);
    
            if (m_shouldUseVisualizerEditor)
            {
    
                const String visualizerCanvasName                 = pluginProcessorName + "Visualizer";
    
                const String visualizerCanvasContentComponentName = visualizerCanvasName + "ContentComponent";
                generatePluginVisualizerEditorCanvasFiles (project, sourceGroup,
                                                           pluginProcessorName, visualizerCanvasName,
                                                           visualizerCanvasName, visualizerCanvasContentComponentName);
                generatePluginEditorContentComponentFiles (project, sourceGroup,
                                                           pluginProcessorName, visualizerCanvasName,
                                                           visualizerCanvasContentComponentName,
                                                           true);
            }
    
    
        bool generatePluginLibFile (const Project& project,
                                    Project::Item& sourceGroup,
    
                                    const String& pluginProcessorName,
                                    const String& pluginFriendlyName)
        {
            String libPluginType            = getLibPluginTypeString            (m_pluginType);
            String libCreateFunctionName    = getLibPluginCreateFunctionString  (m_pluginType);
    
            String libPluginInfoType        = getLibPluginInfoType              (m_pluginType);
    
            const auto newPluginLibFile = getSourceFilesFolder().getChildFile ("OpenEphysLib.cpp");
    
            String libPluginProcessorType = m_pluginType == PLUGIN_TYPE_PROCESSOR
                                            ? ("info->processor.type = " + getLibProcessorTypeString (m_processorType) + ";")
                                            : "";
    
    
            String pluginLibCppFileContent  = project.getFileTemplate ("openEphys_OpenEphysLibTemplate_cpp")
    
                .replace ("PROCESSORCLASSNAME", pluginProcessorName, false)
    
                .replace ("PLUGINLIBRARYNAME", pluginFriendlyName + " plugin library", false) // TODO <Kirill A>: set library name variable
    
                .replace ("PLUGINLIBRARYVERSION", "1", false) // TODO <Kirill A>: set library version variable
    
                .replace ("PLUGINGUINAME", pluginFriendlyName, false) // TODO <Kirill A>: set library gui name variable
    
                .replace ("LIBPLUGINTYPE", libPluginType, false)
    
                .replace ("LIBPLUGININFOTYPE", libPluginInfoType, false)
    
                .replace ("LIBPLUGINCREATEFUNCTION", libCreateFunctionName, false)
                .replace ("LIBPLUGINPROCESSORTYPE", libPluginProcessorType, false);
    
            bool wasGeneratedSuccessfully = true;
    
            if (! FileHelpers::overwriteFileWithNewDataIfDifferent (newPluginLibFile, pluginLibCppFileContent))
            {
                failedFiles.add (newPluginLibFile.getFullPathName());
    
                wasGeneratedSuccessfully = false;
            }
    
            sourceGroup.addFileAtIndex (newPluginLibFile, -1, true);
    
            return wasGeneratedSuccessfully;
        }
    
    
    
        bool generatePluginMakeFile (const Project& project, Project::Item& sourceGroup)
    
            String templatePluginMakeFileContent = project.getFileTemplate ("openEphys_PluginMakefile_example");
            const auto sourceFolder = getSourceFilesFolder();
    
    
            auto newPluginMakeFile = sourceFolder.getChildFile ("Makefile");
    
            bool wasGeneratedSuccessfully = true;
    
    
            if (! FileHelpers::overwriteFileWithNewDataIfDifferent (newPluginMakeFile, templatePluginMakeFileContent))
    
            {
                failedFiles.add (newPluginMakeFile.getFullPathName());
    
                wasGeneratedSuccessfully = false;
            }
    
            return wasGeneratedSuccessfully;
        }
    
    
    
        bool generatePluginProcessorFiles (const Project& project,
                                           Project::Item& sourceGroup,
    
                                           const String& processorName,
                                           const String& editorName,
                                           const String& pluginFriendlyName)
        {
    
            const auto sourceFolder = getSourceFilesFolder();
    
    
            auto newProcessorCppFile  = sourceFolder.getChildFile (processorName + ".cpp");
            auto newProcessorHFile    = sourceFolder.getChildFile (processorName + ".h");
            auto newEditorHFile       = sourceFolder.getChildFile (editorName + ".h");
    
    
            String processorFileTemplateName = getTemplateProcessorFileName (m_pluginType);
            String processorType             = getProcessorTypeString (m_processorType);
    
    Septen's avatar
    Septen committed
            String processorHeaders = CodeHelpers::createIncludeStatement (newProcessorHFile, newProcessorCppFile) + newLine;
    
    
            if (m_pluginType == PLUGIN_TYPE_PROCESSOR)
    
    Septen's avatar
    Septen committed
                processorHeaders += CodeHelpers::createIncludeStatement (newEditorHFile, newProcessorCppFile);
    
    
            String processorCppFileContent = project.getFileTemplate (processorFileTemplateName + "_cpp")
    
    Septen's avatar
    Septen committed
                .replace ("PROCESSORHEADERS",   processorHeaders, false)
    
                .replace ("PROCESSORCLASSNAME", processorName, false)
    
                .replace ("PLUGINGUINAME",      pluginFriendlyName, false)
    
                .replace ("EDITORCLASSNAME",    editorName, false)
                .replace ("PROCESSORTYPE",      processorType,   false);
    
            String processorHFileConent   = project.getFileTemplate (processorFileTemplateName + "_h")
    
                //.replace ("APPHEADERS", appHeaders, false)
                .replace ("PROCESSORCLASSNAME", processorName, false)
                .replace ("HEADERGUARD", CodeHelpers::makeHeaderGuardName (newProcessorHFile), false);
    
            bool wasGeneratedSuccessfully = true;
    
            if (! FileHelpers::overwriteFileWithNewDataIfDifferent (newProcessorCppFile, processorCppFileContent))
            {
                failedFiles.add (newProcessorCppFile.getFullPathName());
    
                wasGeneratedSuccessfully = false;
            }
    
            if (! FileHelpers::overwriteFileWithNewDataIfDifferent (newProcessorHFile, processorHFileConent))
            {
                failedFiles.add (newProcessorHFile.getFullPathName());
    
                wasGeneratedSuccessfully = false;
            }
    
            sourceGroup.addFileAtIndex (newProcessorCppFile, -1, true);
            sourceGroup.addFileAtIndex (newProcessorHFile,   -1, false);
    
            return wasGeneratedSuccessfully;
        }
    
    
    
        bool generatePluginEditorFiles (const Project& project,
                                        Project::Item& sourceGroup,
    
                                        const String& processorName,
                                        const String& editorName,
    
                                        const String& pluginFriendlyName,
                                        const String& contentComponentName)
    
            const auto sourceFolder = getSourceFilesFolder();
    
    
            auto newEditorCppFile  = sourceFolder.getChildFile (editorName + ".cpp");
            auto newEditorHFile    = sourceFolder.getChildFile (editorName + ".h");
    
            String templateFileNameWithoutExtension = m_shouldUseVisualizerEditor
                                                        ? "openEphys_ProcessorVisualizerEditorPluginTemplate"
                                                        : "openEphys_ProcessorEditorPluginTemplate";
    
            String editorCppFileContent = project.getFileTemplate (templateFileNameWithoutExtension + "_cpp")
    
                //.replace ("EDITORCPPHEADERS", CodeHelpers::createIncludeStatement (filterHFile, filterCppFile)
                //                                   + newLine + CodeHelpers::createIncludeStatement (editorHFile, filterCppFile), false)
                .replace ("PROCESSORCLASSNAME", processorName, false)
    
                .replace ("EDITORCLASSNAME", editorName, false)
                .replace ("PLUGINGUINAME", pluginFriendlyName, false);
    
            String editorHFileContent   = project.getFileTemplate (templateFileNameWithoutExtension + "_h")
    
                //.replace ("EDITORHEADERS", appHeaders + newLine + CodeHelpers::createIncludeStatement (filterHFile, filterCppFile), false)
                .replace ("PROCESSORCLASSNAME", processorName, false)
                .replace ("EDITORCLASSNAME", editorName, false)
    
                .replace ("CONTENTCOMPONENTCLASSNAME", contentComponentName, false)
    
                .replace ("HEADERGUARD", CodeHelpers::makeHeaderGuardName (newEditorHFile), false);
    
    
            if (m_shouldUseVisualizerEditor)
            {
    
                const String visualizerCanvasClassName = processorName + "Visualizer";
    
                editorCppFileContent = editorCppFileContent
    
                                        .replace ("EDITORCANVASCLASSNAME", visualizerCanvasClassName, false)
    
                                        .replace ("GenericEditor", "VisualizerEditor", false);
            }
    
    
            if (m_shouldChangeContentLookAndFeel)
            {
                const auto lookAndFeelDeclarationCode = "ScopedPointer<LookAndFeel> m_contentLookAndFeel;";
                const auto lookAndFeelSetterCode      = "content.setLookAndFeel (m_contentLookAndFeel);";
                const auto lookAndFeelCreationCode    = "m_contentLookAndFeel = new LOOKANDFEELCLASSNAME();";
    
                editorCppFileContent = editorCppFileContent
                                        // Uncomment some pieces of code
                                        .replace (String ("//") + lookAndFeelCreationCode, lookAndFeelCreationCode, false)
                                        .replace (String ("//") + lookAndFeelSetterCode, lookAndFeelSetterCode, false)
                                        // Change LookAndFeel class name
                                        .replace ("LOOKANDFEELCLASSNAME", m_contentLookAndFeelClassName, false);
    
                editorHFileContent = editorHFileContent
                                        // Uncomment some pieces of code
                                        .replace (String ("//") + lookAndFeelDeclarationCode, lookAndFeelDeclarationCode, false);
            }
    
    
            bool wasGeneratedSuccessfully = true;
    
            if (! FileHelpers::overwriteFileWithNewDataIfDifferent (newEditorCppFile, editorCppFileContent))
            {
    
                failedFiles.add (newEditorCppFile.getFullPathName());
    
    
                wasGeneratedSuccessfully = false;
            }
    
            if (! FileHelpers::overwriteFileWithNewDataIfDifferent (newEditorHFile, editorHFileContent))
            {
                failedFiles.add (newEditorHFile.getFullPathName());
    
                wasGeneratedSuccessfully = false;
            }
    
    
            sourceGroup.addFileAtIndex (newEditorCppFile, -1, true);
            sourceGroup.addFileAtIndex (newEditorHFile,   -1, false);
    
    
            return wasGeneratedSuccessfully;
    
    
        bool generatePluginEditorContentComponentFiles (const Project& project,
                                                        Project::Item& sourceGroup,
                                                        const String& processorName,
                                                        const String& editorName,
    
                                                        const String& contentComponentName,
                                                        bool useVisualizerEditorTemplates = false)
    
        {
            const auto sourceFolder = getSourceFilesFolder();
    
            auto newContentComponentCppFile = sourceFolder.getChildFile (contentComponentName + ".cpp");
            auto newContentComponentHFile   = sourceFolder.getChildFile (contentComponentName + ".h");
    
    
            const String guiTemplateName = useVisualizerEditorTemplates
                                                ? m_guiVisualizerTemplateName
                                                : m_guiTemplateName;
    
            String contentComponentCppFileContent;
            String contentComponentHFileContent;
    
            if (guiTemplateName == "DEFAULT" || (! isExistsGuiTemplate (guiTemplateName)))
    
            {
                contentComponentCppFileContent = project.getFileTemplate ("openEphys_ProcessorContentComponentTemplate_cpp")
                    .replace ("CONTENTCOMPONENTCLASSNAME", contentComponentName, false);
    
                contentComponentHFileContent = project.getFileTemplate ("openEphys_ProcessorContentComponentTemplate_h")
                    .replace ("CONTENTCOMPONENTCLASSNAME", contentComponentName, false)
                    .replace ("EDITORCLASSNAME", editorName, false)
                    .replace ("HEADERGUARD", CodeHelpers::makeHeaderGuardName (newContentComponentHFile), false);
            }
            else
            {
    
                const auto guiTemplateClassName = getGUITemplateClassName (guiTemplateName);
    
                contentComponentCppFileContent = getGUITemplate (guiTemplateName, false)
    
                    .replace (guiTemplateClassName, contentComponentName);
    
                const auto userVariablesMacro = String ("//[UserVariables]   -- You can add your own custom variables in this section.") + newLine;
    
                const auto friendClassDefinition = CodeHelpers::indent (String ("friend class ") + editorName, 4, true) + ";" + newLine;
                contentComponentHFileContent = getGUITemplate (guiTemplateName, true)
    
                    .replace (guiTemplateClassName, contentComponentName)
                    .replace (userVariablesMacro, userVariablesMacro + friendClassDefinition , false);
            }
    
    
            bool wasGeneratedSuccessfully = true;
    
            if (! FileHelpers::overwriteFileWithNewDataIfDifferent (newContentComponentCppFile, contentComponentCppFileContent))
            {
                failedFiles.add (newContentComponentCppFile.getFullPathName());
    
                wasGeneratedSuccessfully = false;
            }
    
            if (! FileHelpers::overwriteFileWithNewDataIfDifferent (newContentComponentHFile, contentComponentHFileContent))
            {
                failedFiles.add (newContentComponentHFile.getFullPathName());
    
                wasGeneratedSuccessfully = false;
            }
    
            sourceGroup.addFileAtIndex (newContentComponentCppFile, -1, true);
            sourceGroup.addFileAtIndex (newContentComponentHFile,   -1, false);
    
            return wasGeneratedSuccessfully;
        }
    
    
    
        bool generatePluginVisualizerEditorCanvasFiles (const Project& project,
                                                        Project::Item& sourceGroup,
                                                        const String& processorName,
                                                        const String& editorName,
                                                        const String& canvasName,
                                                        const String& contentComponentName)
        {
            const auto sourceFolder = getSourceFilesFolder();
    
            auto newCanvasComponentCppFile = sourceFolder.getChildFile (canvasName + ".cpp");
            auto newCanvasComponentHFile   = sourceFolder.getChildFile (canvasName + ".h");
    
            String canvasComponentCppFileContent;
            String canvasComponentHFileContent;
    
            canvasComponentCppFileContent = project.getFileTemplate ("openEphys_ProcessorVisualizerCanvasTemplate_cpp")
                .replace ("PROCESSORCLASSNAME", processorName, false)
                .replace ("EDITORCANVASCLASSNAME", editorName, false);
    
            canvasComponentHFileContent = project.getFileTemplate ("openEphys_ProcessorVisualizerCanvasTemplate_h")
                .replace ("PROCESSORCLASSNAME", processorName,false)
                .replace ("EDITORCANVASCLASSNAME", editorName, false)
                .replace ("CONTENTCOMPONENTCLASSNAME", contentComponentName, false)
                .replace ("HEADERGUARD", CodeHelpers::makeHeaderGuardName (newCanvasComponentHFile), false);
    
            bool wasGeneratedSuccessfully = true;
    
    
            if (m_shouldChangeContentLookAndFeel)
            {
                const auto lookAndFeelDeclarationCode = "ScopedPointer<LookAndFeel> m_contentLookAndFeel;";
                const auto lookAndFeelSetterCode      = "content.setLookAndFeel (m_contentLookAndFeel);";
                const auto lookAndFeelCreationCode    = "m_contentLookAndFeel = new LOOKANDFEELCLASSNAME();";
    
                canvasComponentCppFileContent = canvasComponentCppFileContent 
                                        // Uncomment some pieces of code
                                        .replace (String ("//") + lookAndFeelCreationCode, lookAndFeelCreationCode, false)
                                        .replace (String ("//") + lookAndFeelSetterCode, lookAndFeelSetterCode, false)
                                        // Change LookAndFeel class name
                                        .replace ("LOOKANDFEELCLASSNAME", m_contentLookAndFeelClassName, false);
    
                canvasComponentHFileContent = canvasComponentHFileContent
                                        // Uncomment some pieces of code
                                        .replace (String ("//") + lookAndFeelDeclarationCode, lookAndFeelDeclarationCode, false);
            }
    
    
            if (! FileHelpers::overwriteFileWithNewDataIfDifferent (newCanvasComponentCppFile, canvasComponentCppFileContent))
            {
                failedFiles.add (newCanvasComponentCppFile.getFullPathName());
    
                wasGeneratedSuccessfully = false;
            }
    
            if (! FileHelpers::overwriteFileWithNewDataIfDifferent (newCanvasComponentHFile, canvasComponentHFileContent))
            {
                failedFiles.add (newCanvasComponentHFile.getFullPathName());
    
                wasGeneratedSuccessfully = false;
            }
    
            sourceGroup.addFileAtIndex (newCanvasComponentCppFile, -1, true);
            sourceGroup.addFileAtIndex (newCanvasComponentHFile,   -1, false);
    
            return wasGeneratedSuccessfully;
        }
    
    
        const char* COMBOBOX_ID_PLUGIN_TYPE    = "pluginTypeComboBox";//    { "pluginTypeComboBox" };
        const char* COMBOBOX_ID_PROCESSOR_TYPE = "processorTypeComboBox";// { "processorTypeComboBox" };
    
        PluginType          m_pluginType    { NOT_A_PLUGIN_TYPE } ;
    
        PluginProcessorType m_processorType { PROCESSOR_TYPE_INVALID };
    
        /** The name of the GUI template which is used to create content component for plugin. */
        String m_guiTemplateName;
    
    
        /** The name of the GUI template for VisualizerEditor's Canvas (if used) to create content component for plugin. */
        String m_guiVisualizerTemplateName;
    
    
        /** The class name of the LookAndFeel for contents. If it's equal to "DEFAULT" - this means that user didn't
            selected the new lookAndFeel for component and we have no need to change it..*/
        String m_contentLookAndFeelClassName;
    
    
        bool m_shouldUseVisualizerEditor;
    
        bool m_shouldChangeContentLookAndFeel;
    
        JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OpenEphysPluginAppWizard)
    };