1 /*
2   ==============================================================================
3 
4    This file is part of the JUCE library.
5    Copyright (c) 2020 - Raw Material Software Limited
6 
7    JUCE is an open source library subject to commercial or open-source
8    licensing.
9 
10    By using JUCE, you agree to the terms of both the JUCE 6 End-User License
11    Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
12 
13    End User License Agreement: www.juce.com/juce-6-licence
14    Privacy Policy: www.juce.com/juce-privacy-policy
15 
16    Or: You may also use this code under the terms of the GPL v3 (see
17    www.gnu.org/licenses).
18 
19    JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
20    EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
21    DISCLAIMED.
22 
23   ==============================================================================
24 */
25 
26 #pragma once
27 
28 #include "../Application/UserAccount/jucer_LicenseController.h"
29 #include "Modules/jucer_AvailableModulesList.h"
30 
31 class ProjectExporter;
32 class LibraryModule;
33 class EnabledModulesList;
34 class CompileEngineSettings;
35 
36 namespace ProjectMessages
37 {
38     namespace Ids
39     {
40        #define DECLARE_ID(name)  static const Identifier name (#name)
41 
42         DECLARE_ID (projectMessages);
43 
44         DECLARE_ID (incompatibleLicense);
45         DECLARE_ID (cppStandard);
46         DECLARE_ID (moduleNotFound);
47         DECLARE_ID (jucePath);
48         DECLARE_ID (jucerFileModified);
49         DECLARE_ID (missingModuleDependencies);
50         DECLARE_ID (oldProjucer);
51         DECLARE_ID (cLion);
52         DECLARE_ID (newVersionAvailable);
53 
54         DECLARE_ID (notification);
55         DECLARE_ID (warning);
56 
57         DECLARE_ID (isVisible);
58 
59        #undef DECLARE_ID
60     }
61 
getTypeForMessage(const Identifier & message)62     inline Identifier getTypeForMessage (const Identifier& message)
63     {
64         static Identifier warnings[] = { Ids::incompatibleLicense, Ids::cppStandard, Ids::moduleNotFound,
65                                          Ids::jucePath, Ids::jucerFileModified, Ids::missingModuleDependencies,
66                                          Ids::oldProjucer, Ids::cLion };
67 
68         if (std::find (std::begin (warnings), std::end (warnings), message) != std::end (warnings))
69             return Ids::warning;
70 
71         if (message == Ids::newVersionAvailable)
72             return Ids::notification;
73 
74         jassertfalse;
75         return {};
76     }
77 
getTitleForMessage(const Identifier & message)78     inline String getTitleForMessage (const Identifier& message)
79     {
80         if (message == Ids::incompatibleLicense)        return "Incompatible License and Splash Screen Setting";
81         if (message == Ids::cppStandard)                return "C++ Standard";
82         if (message == Ids::moduleNotFound)             return "Module Not Found";
83         if (message == Ids::jucePath)                   return "JUCE Path";
84         if (message == Ids::jucerFileModified)          return "Project File Modified";
85         if (message == Ids::missingModuleDependencies)  return "Missing Module Dependencies";
86         if (message == Ids::oldProjucer)                return "Projucer Out of Date";
87         if (message == Ids::newVersionAvailable)        return "New Version Available";
88         if (message == Ids::cLion)                      return "Deprecated Exporter";
89 
90         jassertfalse;
91         return {};
92     }
93 
getDescriptionForMessage(const Identifier & message)94     inline String getDescriptionForMessage (const Identifier& message)
95     {
96         if (message == Ids::incompatibleLicense)        return "Save and export is disabled.";
97         if (message == Ids::cppStandard)                return "Module(s) have a higher C++ standard requirement than the project.";
98         if (message == Ids::moduleNotFound)             return "Module(s) could not be found at the specified paths.";
99         if (message == Ids::jucePath)                   return "The path to your JUCE folder is incorrect.";
100         if (message == Ids::jucerFileModified)          return "The .jucer file has been modified since the last save.";
101         if (message == Ids::missingModuleDependencies)  return "Module(s) have missing dependencies.";
102         if (message == Ids::oldProjucer)                return "The version of the Projucer you are using is out of date.";
103         if (message == Ids::newVersionAvailable)        return "A new version of JUCE is available to download.";
104         if (message == Ids::cLion)                      return "The CLion exporter is deprecated. Use JUCE's CMake support instead.";
105 
106         jassertfalse;
107         return {};
108     }
109 
110     using MessageAction = std::pair<String, std::function<void()>>;
111 }
112 
113 //==============================================================================
114 class Project  : public FileBasedDocument,
115                  private ValueTree::Listener,
116                  private LicenseController::LicenseStateListener,
117                  private ChangeListener,
118                  private AvailableModulesList::Listener
119 {
120 public:
121     //==============================================================================
122     Project (const File&);
123     ~Project() override;
124 
125     //==============================================================================
126     String getDocumentTitle() override;
127     Result loadDocument (const File& file) override;
128     Result saveDocument (const File& file) override;
129 
130     Result saveProject (ProjectExporter* exporterToSave = nullptr);
131     Result saveResourcesOnly();
132     Result openProjectInIDE (ProjectExporter& exporterToOpen, bool saveFirst);
133 
134     File getLastDocumentOpened() override;
135     void setLastDocumentOpened (const File& file) override;
136 
137     void setTitle (const String& newTitle);
138 
139     //==============================================================================
getProjectFolder()140     File getProjectFolder() const                               { return getFile().getParentDirectory(); }
getGeneratedCodeFolder()141     File getGeneratedCodeFolder() const                         { return getFile().getSiblingFile ("JuceLibraryCode"); }
getSourceFilesFolder()142     File getSourceFilesFolder() const                           { return getProjectFolder().getChildFile ("Source"); }
getLocalModulesFolder()143     File getLocalModulesFolder() const                          { return getGeneratedCodeFolder().getChildFile ("modules"); }
getLocalModuleFolder(const String & moduleID)144     File getLocalModuleFolder (const String& moduleID) const    { return getLocalModulesFolder().getChildFile (moduleID); }
getAppIncludeFile()145     File getAppIncludeFile() const                              { return getGeneratedCodeFolder().getChildFile (getJuceSourceHFilename()); }
146 
147     File getBinaryDataCppFile (int index) const;
getBinaryDataHeaderFile()148     File getBinaryDataHeaderFile() const                        { return getBinaryDataCppFile (0).withFileExtension (".h"); }
149 
getAppConfigFilename()150     static String getAppConfigFilename()                        { return "AppConfig.h"; }
getPluginDefinesFilename()151     static String getPluginDefinesFilename()                    { return "JucePluginDefines.h"; }
getJuceSourceHFilename()152     static String getJuceSourceHFilename()                      { return "JuceHeader.h"; }
153 
154     //==============================================================================
155     template <class FileType>
shouldBeAddedToBinaryResourcesByDefault(const FileType & file)156     bool shouldBeAddedToBinaryResourcesByDefault (const FileType& file)
157     {
158         return ! file.hasFileExtension (sourceOrHeaderFileExtensions);
159     }
160 
161     File resolveFilename (String filename) const;
162     String getRelativePathForFile (const File& file) const;
163 
164     //==============================================================================
165     // Creates editors for the project settings
166     void createPropertyEditors (PropertyListBuilder&);
167 
168     //==============================================================================
getProjectRoot()169     ValueTree getProjectRoot() const                     { return projectRoot; }
getProjectValue(const Identifier & name)170     Value getProjectValue (const Identifier& name)       { return projectRoot.getPropertyAsValue (name, getUndoManagerFor (projectRoot)); }
getProjectVar(const Identifier & name)171     var   getProjectVar   (const Identifier& name) const { return projectRoot.getProperty        (name); }
172 
173     const build_tools::ProjectType& getProjectType() const;
getProjectTypeString()174     String getProjectTypeString() const                  { return projectTypeValue.get(); }
setProjectType(const String & newProjectType)175     void setProjectType (const String& newProjectType)   { projectTypeValue = newProjectType; }
176 
getProjectNameString()177     String getProjectNameString() const                  { return projectNameValue.get(); }
getProjectFilenameRootString()178     String getProjectFilenameRootString()                { return File::createLegalFileName (getDocumentTitle()); }
getProjectUIDString()179     String getProjectUIDString() const                   { return projectUIDValue.get(); }
180 
getProjectLineFeed()181     String getProjectLineFeed() const                    { return projectLineFeedValue.get(); }
182 
getVersionString()183     String getVersionString() const                      { return versionValue.get(); }
getVersionAsHex()184     String getVersionAsHex() const                       { return build_tools::getVersionAsHex (getVersionString()); }
getVersionAsHexInteger()185     int getVersionAsHexInteger() const                   { return build_tools::getVersionAsHexInteger (getVersionString()); }
setProjectVersion(const String & newVersion)186     void setProjectVersion (const String& newVersion)    { versionValue = newVersion; }
187 
getBundleIdentifierString()188     String getBundleIdentifierString() const             { return bundleIdentifierValue.get(); }
189     String getDefaultBundleIdentifierString() const;
getDefaultAAXIdentifierString()190     String getDefaultAAXIdentifierString() const         { return getDefaultBundleIdentifierString(); }
191     String getDefaultPluginManufacturerString() const;
192 
getCompanyNameString()193     String getCompanyNameString() const                  { return companyNameValue.get(); }
getCompanyCopyrightString()194     String getCompanyCopyrightString() const             { return companyCopyrightValue.get(); }
getCompanyWebsiteString()195     String getCompanyWebsiteString() const               { return companyWebsiteValue.get(); }
getCompanyEmailString()196     String getCompanyEmailString() const                 { return companyEmailValue.get(); }
197 
getHeaderSearchPathsString()198     String getHeaderSearchPathsString() const            { return headerSearchPathsValue.get(); }
199 
getPreprocessorDefs()200     StringPairArray getPreprocessorDefs() const          { return parsedPreprocessorDefs; }
201 
getMaxBinaryFileSize()202     int getMaxBinaryFileSize() const                     { return maxBinaryFileSizeValue.get(); }
shouldIncludeBinaryInJuceHeader()203     bool shouldIncludeBinaryInJuceHeader() const         { return includeBinaryDataInJuceHeaderValue.get(); }
getBinaryDataNamespaceString()204     String getBinaryDataNamespaceString() const          { return binaryDataNamespaceValue.get(); }
205 
shouldDisplaySplashScreen()206     bool shouldDisplaySplashScreen() const               { return displaySplashScreenValue.get(); }
getSplashScreenColourString()207     String getSplashScreenColourString() const           { return splashScreenColourValue.get(); }
208 
getCppStandardStrings()209     static StringArray getCppStandardStrings()           { return { "C++11", "C++14", "C++17", "Use Latest" }; }
getCppStandardVars()210     static Array<var> getCppStandardVars()               { return { "11",    "14",    "17",    "latest" }; }
211 
getCppStandardString()212     String getCppStandardString() const                  { return cppStandardValue.get(); }
213 
214     StringArray getCompilerFlagSchemes() const;
215     void addCompilerFlagScheme (const String&);
216     void removeCompilerFlagScheme (const String&);
217 
getPostExportShellCommandPosixString()218     String getPostExportShellCommandPosixString() const  { return postExportShellCommandPosixValue.get(); }
getPostExportShellCommandWinString()219     String getPostExportShellCommandWinString() const    { return postExportShellCommandWinValue.get(); }
220 
shouldUseAppConfig()221     bool shouldUseAppConfig() const                      { return useAppConfigValue.get(); }
shouldAddUsingNamespaceToJuceHeader()222     bool shouldAddUsingNamespaceToJuceHeader() const     { return addUsingNamespaceToJuceHeader.get(); }
223 
224     //==============================================================================
getPluginNameString()225     String getPluginNameString() const                { return pluginNameValue.get(); }
getPluginDescriptionString()226     String getPluginDescriptionString() const         { return pluginDescriptionValue.get();}
getPluginManufacturerString()227     String getPluginManufacturerString() const        { return pluginManufacturerValue.get(); }
getPluginManufacturerCodeString()228     String getPluginManufacturerCodeString() const    { return pluginManufacturerCodeValue.get(); }
getPluginCodeString()229     String getPluginCodeString() const                { return pluginCodeValue.get(); }
getPluginChannelConfigsString()230     String getPluginChannelConfigsString() const      { return pluginChannelConfigsValue.get(); }
getAAXIdentifierString()231     String getAAXIdentifierString() const             { return pluginAAXIdentifierValue.get(); }
getPluginAUExportPrefixString()232     String getPluginAUExportPrefixString() const      { return pluginAUExportPrefixValue.get(); }
getVSTNumMIDIInputsString()233     String getVSTNumMIDIInputsString() const          { return pluginVSTNumMidiInputsValue.get(); }
getVSTNumMIDIOutputsString()234     String getVSTNumMIDIOutputsString() const         { return pluginVSTNumMidiOutputsValue.get(); }
235 
checkMultiChoiceVar(const ValueWithDefault & valueToCheck,Identifier idToCheck)236     static bool checkMultiChoiceVar (const ValueWithDefault& valueToCheck, Identifier idToCheck) noexcept
237     {
238         if (! valueToCheck.get().isArray())
239             return false;
240 
241         auto v = valueToCheck.get();
242 
243         if (auto* varArray = v.getArray())
244             return varArray->contains (idToCheck.toString());
245 
246         return false;
247     }
248 
isAudioPluginProject()249     bool isAudioPluginProject() const                 { return getProjectType().isAudioPlugin(); }
250 
shouldBuildVST()251     bool shouldBuildVST() const                       { return isAudioPluginProject() && checkMultiChoiceVar (pluginFormatsValue, Ids::buildVST); }
shouldBuildVST3()252     bool shouldBuildVST3() const                      { return isAudioPluginProject() && checkMultiChoiceVar (pluginFormatsValue, Ids::buildVST3); }
shouldBuildAU()253     bool shouldBuildAU() const                        { return isAudioPluginProject() && checkMultiChoiceVar (pluginFormatsValue, Ids::buildAU); }
shouldBuildAUv3()254     bool shouldBuildAUv3() const                      { return isAudioPluginProject() && checkMultiChoiceVar (pluginFormatsValue, Ids::buildAUv3); }
shouldBuildRTAS()255     bool shouldBuildRTAS() const                      { return isAudioPluginProject() && checkMultiChoiceVar (pluginFormatsValue, Ids::buildRTAS); }
shouldBuildAAX()256     bool shouldBuildAAX() const                       { return isAudioPluginProject() && checkMultiChoiceVar (pluginFormatsValue, Ids::buildAAX); }
shouldBuildStandalonePlugin()257     bool shouldBuildStandalonePlugin() const          { return isAudioPluginProject() && checkMultiChoiceVar (pluginFormatsValue, Ids::buildStandalone); }
shouldBuildUnityPlugin()258     bool shouldBuildUnityPlugin() const               { return isAudioPluginProject() && checkMultiChoiceVar (pluginFormatsValue, Ids::buildUnity); }
shouldEnableIAA()259     bool shouldEnableIAA() const                      { return isAudioPluginProject() && checkMultiChoiceVar (pluginFormatsValue, Ids::enableIAA); }
260 
isPluginSynth()261     bool isPluginSynth() const                        { return checkMultiChoiceVar (pluginCharacteristicsValue, Ids::pluginIsSynth); }
pluginWantsMidiInput()262     bool pluginWantsMidiInput() const                 { return checkMultiChoiceVar (pluginCharacteristicsValue, Ids::pluginWantsMidiIn); }
pluginProducesMidiOutput()263     bool pluginProducesMidiOutput() const             { return checkMultiChoiceVar (pluginCharacteristicsValue, Ids::pluginProducesMidiOut); }
isPluginMidiEffect()264     bool isPluginMidiEffect() const                   { return checkMultiChoiceVar (pluginCharacteristicsValue, Ids::pluginIsMidiEffectPlugin); }
pluginEditorNeedsKeyFocus()265     bool pluginEditorNeedsKeyFocus() const            { return checkMultiChoiceVar (pluginCharacteristicsValue, Ids::pluginEditorRequiresKeys); }
isPluginRTASBypassDisabled()266     bool isPluginRTASBypassDisabled() const           { return checkMultiChoiceVar (pluginCharacteristicsValue, Ids::pluginRTASDisableBypass); }
isPluginRTASMultiMonoDisabled()267     bool isPluginRTASMultiMonoDisabled() const        { return checkMultiChoiceVar (pluginCharacteristicsValue, Ids::pluginRTASDisableMultiMono); }
isPluginAAXBypassDisabled()268     bool isPluginAAXBypassDisabled() const            { return checkMultiChoiceVar (pluginCharacteristicsValue, Ids::pluginAAXDisableBypass); }
isPluginAAXMultiMonoDisabled()269     bool isPluginAAXMultiMonoDisabled() const         { return checkMultiChoiceVar (pluginCharacteristicsValue, Ids::pluginAAXDisableMultiMono); }
270 
271     static StringArray getAllAUMainTypeStrings() noexcept;
272     static Array<var> getAllAUMainTypeVars() noexcept;
273     Array<var> getDefaultAUMainTypes() const noexcept;
274 
275     static StringArray getAllVSTCategoryStrings() noexcept;
276     Array<var> getDefaultVSTCategories() const noexcept;
277 
278     static StringArray getAllVST3CategoryStrings() noexcept;
279     Array<var> getDefaultVST3Categories() const noexcept;
280 
281     static StringArray getAllAAXCategoryStrings() noexcept;
282     static Array<var> getAllAAXCategoryVars() noexcept;
283     Array<var> getDefaultAAXCategories() const noexcept;
284 
285     static StringArray getAllRTASCategoryStrings() noexcept;
286     static Array<var> getAllRTASCategoryVars() noexcept;
287     Array<var> getDefaultRTASCategories() const noexcept;
288 
289     String getAUMainTypeString() const noexcept;
290     bool isAUSandBoxSafe() const noexcept;
291     String getVSTCategoryString() const noexcept;
292     String getVST3CategoryString() const noexcept;
293     int getAAXCategory() const noexcept;
294     int getRTASCategory() const noexcept;
295 
296     String getIAATypeCode() const;
297     String getIAAPluginName() const;
298 
getUnityScriptName()299     String getUnityScriptName() const    { return addUnityPluginPrefixIfNecessary (getProjectNameString()) + "_UnityScript.cs"; }
addUnityPluginPrefixIfNecessary(const String & name)300     static String addUnityPluginPrefixIfNecessary (const String& name)
301     {
302         if (! name.startsWithIgnoreCase ("audioplugin"))
303             return "audioplugin_" + name;
304 
305         return name;
306     }
307 
308     //==============================================================================
309     bool isAUPluginHost();
310     bool isVSTPluginHost();
311     bool isVST3PluginHost();
312 
313     //==============================================================================
314     bool shouldBuildTargetType (build_tools::ProjectType::Target::Type targetType) const noexcept;
315     static build_tools::ProjectType::Target::Type getTargetTypeFromFilePath (const File& file, bool returnSharedTargetIfNoValidSuffix);
316 
317     //==============================================================================
318     void updateDeprecatedProjectSettingsInteractively();
319 
320     StringPairArray getAppConfigDefs();
321     StringPairArray getAudioPluginFlags() const;
322 
323     //==============================================================================
324     class Item
325     {
326     public:
327         //==============================================================================
328         Item (Project& project, const ValueTree& itemNode, bool isModuleCode);
329         Item (const Item& other);
330 
331         static Item createGroup (Project& project, const String& name, const String& uid, bool isModuleCode);
332         void initialiseMissingProperties();
333 
334         //==============================================================================
isValid()335         bool isValid() const                            { return state.isValid(); }
336         bool operator== (const Item& other) const       { return state == other.state && &project == &other.project; }
337         bool operator!= (const Item& other) const       { return ! operator== (other); }
338 
339         //==============================================================================
340         bool isFile() const;
341         bool isGroup() const;
342         bool isMainGroup() const;
343         bool isImageFile() const;
344         bool isSourceFile() const;
345 
346         String getID() const;
347         void setID (const String& newID);
348         Item findItemWithID (const String& targetId) const; // (recursive search)
349 
350         String getImageFileID() const;
351         std::unique_ptr<Drawable> loadAsImageFile() const;
352 
353         //==============================================================================
354         Value getNameValue();
355         String getName() const;
356         File getFile() const;
357         String getFilePath() const;
358         void setFile (const File& file);
359         void setFile (const build_tools::RelativePath& file);
360         File determineGroupFolder() const;
361         bool renameFile (const File& newFile);
362 
363         bool shouldBeAddedToTargetProject() const;
364         bool shouldBeAddedToTargetExporter (const ProjectExporter&) const;
365         bool shouldBeCompiled() const;
366         Value getShouldCompileValue();
367 
368         bool shouldBeAddedToBinaryResources() const;
369         Value getShouldAddToBinaryResourcesValue();
370 
371         bool shouldBeAddedToXcodeResources() const;
372         Value getShouldAddToXcodeResourcesValue();
373 
374         Value getShouldInhibitWarningsValue();
375         bool shouldInhibitWarnings() const;
376 
377         bool isModuleCode() const;
378 
379         Value getShouldSkipPCHValue();
380         bool shouldSkipPCH() const;
381 
382         Value getCompilerFlagSchemeValue();
383         String getCompilerFlagSchemeString() const;
384 
385         void setCompilerFlagScheme (const String&);
386         void clearCurrentCompilerFlagScheme();
387 
388         //==============================================================================
389         bool canContain (const Item& child) const;
getNumChildren()390         int getNumChildren() const                      { return state.getNumChildren(); }
getChild(int index)391         Item getChild (int index) const                 { return Item (project, state.getChild (index), belongsToModule); }
392 
393         Item addNewSubGroup (const String& name, int insertIndex);
394         Item getOrCreateSubGroup (const String& name);
395         void addChild (const Item& newChild, int insertIndex);
396         bool addFileAtIndex (const File& file, int insertIndex, bool shouldCompile);
397         bool addFileRetainingSortOrder (const File& file, bool shouldCompile);
398         void addFileUnchecked (const File& file, int insertIndex, bool shouldCompile);
399         bool addRelativeFile (const build_tools::RelativePath& file, int insertIndex, bool shouldCompile);
400         void removeItemFromProject();
401         void sortAlphabetically (bool keepGroupsAtStart, bool recursive);
402         Item findItemForFile (const File& file) const;
403         bool containsChildForFile (const build_tools::RelativePath& file) const;
404 
405         Item getParent() const;
406         Item createCopy();
407 
getUndoManager()408         UndoManager* getUndoManager() const              { return project.getUndoManagerFor (state); }
409 
410         Icon getIcon (bool isOpen = false) const;
411         bool isIconCrossedOut() const;
412 
413         bool needsSaving() const noexcept;
414 
415         Project& project;
416         ValueTree state;
417 
418     private:
419         Item& operator= (const Item&);
420         bool belongsToModule;
421     };
422 
423     Item getMainGroup();
424 
425     void findAllImageItems (OwnedArray<Item>& items);
426 
427     //==============================================================================
428     ValueTree getExporters();
429     int getNumExporters();
430     std::unique_ptr<ProjectExporter> createExporter (int index);
431     void addNewExporter (const Identifier& exporterIdentifier);
432     void createExporterForCurrentPlatform();
433 
434     struct ExporterIterator
435     {
436         ExporterIterator (Project& project);
437         ~ExporterIterator();
438 
439         bool next();
440 
441         ProjectExporter& operator*() const       { return *exporter; }
442         ProjectExporter* operator->() const      { return exporter.get(); }
443 
444         std::unique_ptr<ProjectExporter> exporter;
445         int index;
446 
447     private:
448         Project& project;
449         JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ExporterIterator)
450     };
451 
452     //==============================================================================
453     struct ConfigFlag
454     {
455         String symbol, description, sourceModuleID;
456         ValueWithDefault value;
457     };
458 
459     ValueWithDefault getConfigFlag (const String& name);
460     bool isConfigFlagEnabled (const String& name, bool defaultIsEnabled = false) const;
461 
462     //==============================================================================
463     EnabledModulesList& getEnabledModules();
464 
getExporterPathsModulesList()465     AvailableModulesList& getExporterPathsModulesList()  { return exporterPathsModulesList; }
466     void rescanExporterPathModules (bool async = false);
467 
468     std::pair<String, File> getModuleWithID (const String&);
469 
470     //==============================================================================
471     PropertiesFile& getStoredProperties() const;
472 
473     //==============================================================================
getUndoManagerFor(const ValueTree &)474     UndoManager* getUndoManagerFor (const ValueTree&) const             { return nullptr; }
getUndoManager()475     UndoManager* getUndoManager() const                                 { return nullptr; }
476 
477     //==============================================================================
478     static const char* projectFileExtension;
479 
480     //==============================================================================
481     bool updateCachedFileState();
getCachedFileStateContent()482     String getCachedFileStateContent() const noexcept  { return cachedFileState.second; }
483 
484     String serialiseProjectXml (std::unique_ptr<XmlElement>) const;
485 
486     //==============================================================================
487     String getUniqueTargetFolderSuffixForExporter (const Identifier& exporterIdentifier, const String& baseTargetFolder);
488 
489     //==============================================================================
isCurrentlySaving()490     bool isCurrentlySaving() const noexcept              { return isSaving; }
491 
isTemporaryProject()492     bool isTemporaryProject() const noexcept             { return tempDirectory != File(); }
getTemporaryDirectory()493     File getTemporaryDirectory() const noexcept          { return tempDirectory; }
494     void setTemporaryDirectory (const File&) noexcept;
495 
496     //==============================================================================
getCompileEngineSettings()497     CompileEngineSettings& getCompileEngineSettings()    { return *compileEngineSettings; }
498 
499     //==============================================================================
getProjectMessages()500     ValueTree getProjectMessages() const  { return projectMessages; }
501 
502     void addProjectMessage (const Identifier& messageToAdd, std::vector<ProjectMessages::MessageAction>&& messageActions);
503     void removeProjectMessage (const Identifier& messageToRemove);
504 
505     std::vector<ProjectMessages::MessageAction> getMessageActions (const Identifier& message);
506 
507     //==============================================================================
508     bool hasIncompatibleLicenseTypeAndSplashScreenSetting() const;
509     bool isFileModificationCheckPending() const;
510     bool isSaveAndExportDisabled() const;
511 
512 private:
513     //==============================================================================
514     void valueTreePropertyChanged (ValueTree&, const Identifier&) override;
515     void valueTreeChildAdded (ValueTree&, ValueTree&) override;
516     void valueTreeChildRemoved (ValueTree&, ValueTree&, int) override;
517     void valueTreeChildOrderChanged (ValueTree&, int, int) override;
518 
519     //==============================================================================
520     struct ProjectFileModificationPoller  : private Timer
521     {
522         ProjectFileModificationPoller (Project& p);
isCheckPendingProjectFileModificationPoller523         bool isCheckPending() const noexcept  { return pending; }
524 
525     private:
526         void timerCallback() override;
527         void reset();
528 
529         void resaveProject();
530         void reloadProjectFromDisk();
531 
532         Project& project;
533         bool pending = false;
534     };
535 
536     //==============================================================================
537     ValueTree projectRoot  { Ids::JUCERPROJECT };
538 
539     ValueWithDefault projectNameValue, projectUIDValue, projectLineFeedValue, projectTypeValue, versionValue, bundleIdentifierValue, companyNameValue,
540                      companyCopyrightValue, companyWebsiteValue, companyEmailValue, displaySplashScreenValue, splashScreenColourValue, cppStandardValue,
541                      headerSearchPathsValue, preprocessorDefsValue, userNotesValue, maxBinaryFileSizeValue, includeBinaryDataInJuceHeaderValue, binaryDataNamespaceValue,
542                      compilerFlagSchemesValue, postExportShellCommandPosixValue, postExportShellCommandWinValue, useAppConfigValue, addUsingNamespaceToJuceHeader;
543 
544     ValueWithDefault pluginFormatsValue, pluginNameValue, pluginDescriptionValue, pluginManufacturerValue, pluginManufacturerCodeValue,
545                      pluginCodeValue, pluginChannelConfigsValue, pluginCharacteristicsValue, pluginAUExportPrefixValue, pluginAAXIdentifierValue,
546                      pluginAUMainTypeValue, pluginAUSandboxSafeValue, pluginRTASCategoryValue, pluginVSTCategoryValue, pluginVST3CategoryValue, pluginAAXCategoryValue,
547                      pluginVSTNumMidiInputsValue, pluginVSTNumMidiOutputsValue;
548 
549     //==============================================================================
550     std::unique_ptr<CompileEngineSettings> compileEngineSettings;
551     std::unique_ptr<EnabledModulesList> enabledModulesList;
552 
553     AvailableModulesList exporterPathsModulesList;
554 
555     //==============================================================================
556     void updateDeprecatedProjectSettings();
557 
558     //==============================================================================
559     bool shouldWriteLegacyPluginFormatSettings = false;
560     bool shouldWriteLegacyPluginCharacteristicsSettings = false;
561 
562     static Array<Identifier> getLegacyPluginFormatIdentifiers() noexcept;
563     static Array<Identifier> getLegacyPluginCharacteristicsIdentifiers() noexcept;
564 
565     void writeLegacyPluginFormatSettings();
566     void writeLegacyPluginCharacteristicsSettings();
567 
568     void coalescePluginFormatValues();
569     void coalescePluginCharacteristicsValues();
570     void updatePluginCategories();
571 
572     //==============================================================================
573     File tempDirectory;
574     std::pair<Time, String> cachedFileState;
575 
576     void saveAndMoveTemporaryProject (bool openInIDE);
577 
578     //==============================================================================
579     friend class Item;
580     bool isSaving = false;
581     StringPairArray parsedPreprocessorDefs;
582 
583     //==============================================================================
584     void initialiseProjectValues();
585     void initialiseMainGroup();
586     void initialiseAudioPluginValues();
587 
588     bool setCppVersionFromOldExporterSettings();
589 
590     void createAudioPluginPropertyEditors (PropertyListBuilder& props);
591 
592     //==============================================================================
593     void updateTitleDependencies();
594     void updateCompanyNameDependencies();
595     void updateProjectSettings();
596     ValueTree getConfigurations() const;
597     ValueTree getConfigNode();
598 
599     void updateOldStyleConfigList();
600     void moveOldPropertyFromProjectToAllExporters (Identifier name);
601     void removeDefunctExporters();
602     void updateOldModulePaths();
603 
604     //==============================================================================
605     void licenseStateChanged() override;
606     void changeListenerCallback (ChangeBroadcaster*) override;
607     void availableModulesChanged (AvailableModulesList*) override;
608 
609     void updateLicenseWarning();
610     void updateJUCEPathWarning();
611 
612     void updateModuleWarnings();
613     void updateExporterWarnings();
614     void updateCppStandardWarning (bool showWarning);
615     void updateMissingModuleDependenciesWarning (bool showWarning);
616     void updateOldProjucerWarning (bool showWarning);
617     void updateCLionWarning (bool showWarning);
618     void updateModuleNotFoundWarning (bool showWarning);
619 
620     ValueTree projectMessages { ProjectMessages::Ids::projectMessages, {},
621                                 { { ProjectMessages::Ids::notification, {} }, { ProjectMessages::Ids::warning, {} } } };
622     std::map<Identifier, std::vector<ProjectMessages::MessageAction>> messageActions;
623 
624     ProjectFileModificationPoller fileModificationPoller { *this };
625 
626     //==============================================================================
627     JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Project)
628 };
629