/* ============================================================================== 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 "../jucer_Headers.h" #include "../jucer_Application.h" #include "../../ProjectSaving/jucer_ProjectExporter.h" #include "jucer_NewProjectWizard.h" //============================================================================== static String getFileTemplate (const String& templateName) { int dataSize = 0; if (auto* data = BinaryData::getNamedResource (templateName.toUTF8(), dataSize)) return String::fromUTF8 (data, dataSize); jassertfalse; return {}; } static String getJuceHeaderInclude() { return CodeHelpers::createIncludePathIncludeStatement (Project::getJuceSourceHFilename()); } static String getContentComponentName() { return "MainComponent"; } using Opts = NewProjectTemplates::FileCreationOptions; static bool shouldCreateHeaderFile (Opts opts) noexcept { return opts == Opts::header || opts == Opts::headerAndCpp; } static bool shouldCreateCppFile (Opts opts) noexcept { return opts == Opts::headerAndCpp; } static void doBasicProjectSetup (Project& project, const NewProjectTemplates::ProjectTemplate& projectTemplate, const String& name) { project.setTitle (name); project.setProjectType (projectTemplate.projectTypeString); project.getMainGroup().addNewSubGroup ("Source", 0); project.getConfigFlag ("JUCE_STRICT_REFCOUNTEDPOINTER") = true; project.getProjectValue (Ids::useAppConfig) = false; project.getProjectValue (Ids::addUsingNamespaceToJuceHeader) = false; if (! ProjucerApplication::getApp().getLicenseController().getCurrentState().canUnlockFullFeatures()) project.getProjectValue (Ids::displaySplashScreen) = true; if (NewProjectTemplates::isPlugin (projectTemplate)) project.getConfigFlag ("JUCE_VST3_CAN_REPLACE_VST2") = 0; } static std::map getSharedFileTokenReplacements() { return { { "%%app_headers%%", getJuceHeaderInclude() } }; } static std::map getApplicationFileTokenReplacements (const String& name, NewProjectTemplates::FileCreationOptions fileOptions, const File& sourceFolder) { auto tokenReplacements = getSharedFileTokenReplacements(); tokenReplacements.insert ({ "%%app_class_name%%", build_tools::makeValidIdentifier (name + "Application", false, true, false) }); tokenReplacements.insert ({ "%%content_component_class%%", getContentComponentName() }); tokenReplacements.insert ({ "%%include_juce%%", getJuceHeaderInclude() }); if (shouldCreateHeaderFile (fileOptions)) tokenReplacements["%%app_headers%%"] << newLine << CodeHelpers::createIncludeStatement (sourceFolder.getChildFile ("MainComponent.h"), sourceFolder.getChildFile ("Main.cpp")); if (shouldCreateCppFile (fileOptions)) tokenReplacements.insert ({ "%%include_corresponding_header%%", CodeHelpers::createIncludeStatement (sourceFolder.getChildFile ("MainComponent.h"), sourceFolder.getChildFile ("MainComponent.cpp")) }); return tokenReplacements; } static std::map getPluginFileTokenReplacements (const String& name, const File& sourceFolder) { auto tokenReplacements = getSharedFileTokenReplacements(); auto processorCppFile = sourceFolder.getChildFile ("PluginProcessor.cpp"); auto processorHFile = processorCppFile.withFileExtension (".h"); auto editorCppFile = sourceFolder.getChildFile ("PluginEditor.cpp"); auto editorHFile = editorCppFile.withFileExtension (".h"); auto processorHInclude = CodeHelpers::createIncludeStatement (processorHFile, processorCppFile); auto editorHInclude = CodeHelpers::createIncludeStatement (editorHFile, processorCppFile); auto processorClassName = build_tools::makeValidIdentifier (name, true, true, false) + "AudioProcessor"; processorClassName = processorClassName.substring (0, 1).toUpperCase() + processorClassName.substring (1); auto editorClassName = processorClassName + "Editor"; tokenReplacements.insert ({"%%filter_headers%%", processorHInclude + newLine + editorHInclude }); tokenReplacements.insert ({"%%filter_class_name%%", processorClassName }); tokenReplacements.insert ({"%%editor_class_name%%", editorClassName }); tokenReplacements.insert ({"%%editor_cpp_headers%%", processorHInclude + newLine + editorHInclude }); tokenReplacements.insert ({"%%editor_headers%%", getJuceHeaderInclude() + newLine + processorHInclude }); return tokenReplacements; } static bool addFiles (Project& project, const NewProjectTemplates::ProjectTemplate& projectTemplate, const String& name, var fileOptionsVar, StringArray& failedFiles) { auto sourceFolder = project.getFile().getSiblingFile ("Source"); if (! sourceFolder.createDirectory()) { failedFiles.add (sourceFolder.getFullPathName()); return false; } auto fileOptions = NewProjectTemplates::getFileOptionForVar (fileOptionsVar); if (fileOptions == Opts::noFiles) return true; auto tokenReplacements = [&]() -> std::map { if (NewProjectTemplates::isApplication (projectTemplate)) return getApplicationFileTokenReplacements (name, fileOptions, sourceFolder); if (NewProjectTemplates::isPlugin (projectTemplate)) return getPluginFileTokenReplacements (name, sourceFolder); jassertfalse; return {}; }(); auto sourceGroup = project.getMainGroup().getOrCreateSubGroup ("Source"); for (auto& files : projectTemplate.getFilesForOption (fileOptions)) { auto file = sourceFolder.getChildFile (files.first); auto fileContent = getFileTemplate (files.second); for (auto& tokenReplacement : tokenReplacements) fileContent = fileContent.replace (tokenReplacement.first, tokenReplacement.second, false); if (! build_tools::overwriteFileWithNewDataIfDifferent (file, fileContent)) { failedFiles.add (file.getFullPathName()); return false; } sourceGroup.addFileAtIndex (file, -1, (file.hasFileExtension (sourceFileExtensions))); } return true; } static void addModules (Project& project, Array modules, const String& modulePath, bool useGlobalPath) { AvailableModulesList list; list.scanPaths ({ modulePath }); for (auto& mod : list.getAllModules()) if (modules.contains (mod.first)) project.getEnabledModules().addModule (mod.second, false, useGlobalPath); } static void addExporters (Project& project, Array exporters) { for (auto exporter : exporters) project.addNewExporter (exporter.toString()); for (Project::ExporterIterator exporter (project); exporter.next();) for (ProjectExporter::ConfigIterator config (*exporter); config.next();) config->getValue (Ids::targetName) = project.getProjectFilenameRootString(); } //============================================================================== File NewProjectWizard::getLastWizardFolder() { if (getAppSettings().lastWizardFolder.isDirectory()) return getAppSettings().lastWizardFolder; #if JUCE_WINDOWS static File lastFolderFallback (File::getSpecialLocation (File::userDocumentsDirectory)); #else static File lastFolderFallback (File::getSpecialLocation (File::userHomeDirectory)); #endif return lastFolderFallback; } std::unique_ptr NewProjectWizard::createNewProject (const NewProjectTemplates::ProjectTemplate& projectTemplate, const File& targetFolder, const String& name, var modules, var exporters, var fileOptions, const String& modulePath, bool useGlobalModulePath) { StringArray failedFiles; if (! targetFolder.exists()) { if (! targetFolder.createDirectory()) failedFiles.add (targetFolder.getFullPathName()); } else if (FileHelpers::containsAnyNonHiddenFiles (targetFolder)) { if (! AlertWindow::showOkCancelBox (AlertWindow::InfoIcon, TRANS("New JUCE Project"), TRANS("You chose the folder:\n\nXFLDRX\n\n").replace ("XFLDRX", targetFolder.getFullPathName()) + TRANS("This folder isn't empty - are you sure you want to create the project there?") + "\n\n" + TRANS("Any existing files with the same names may be overwritten by the new files."))) { return nullptr; } } auto project = std::make_unique (targetFolder.getChildFile (File::createLegalFileName (name)) .withFileExtension (Project::projectFileExtension)); if (failedFiles.isEmpty()) { doBasicProjectSetup (*project, projectTemplate, name); if (addFiles (*project, projectTemplate, name, fileOptions, failedFiles)) { addExporters (*project, *exporters.getArray()); addModules (*project, *modules.getArray(), modulePath, useGlobalModulePath); if (project->save (false, true) == FileBasedDocument::savedOk) { project->setChangedFlag (false); project->loadFrom (project->getFile(), true); } else { failedFiles.add (project->getFile().getFullPathName()); } } } if (! failedFiles.isEmpty()) { AlertWindow::showMessageBoxAsync (AlertWindow::WarningIcon, TRANS("Errors in Creating Project!"), TRANS("The following files couldn't be written:") + "\n\n" + failedFiles.joinIntoString ("\n", 0, 10)); return nullptr; } return project; }