/* ------------------------------------------------------------------ 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/>. */ #include <iostream> // ============================================================================ // ============================================================================ // ============================================================================ 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); return Result::ok(); } 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) 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); } return 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); String processorHeaders = CodeHelpers::createIncludeStatement (newProcessorHFile, newProcessorCppFile) + newLine; if (m_pluginType == PLUGIN_TYPE_PROCESSOR) processorHeaders += CodeHelpers::createIncludeStatement (newEditorHFile, newProcessorCppFile); String processorCppFileContent = project.getFileTemplate (processorFileTemplateName + "_cpp") .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" }; private: 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) };