| @@ -59,6 +59,12 @@ public: | |||
| else if (os == TargetOS::windows) osSelector.setSelectedId (2); | |||
| else if (os == TargetOS::linux) osSelector.setSelectedId (3); | |||
| addChildComponent (rescanJUCEPathButton); | |||
| rescanJUCEPathButton.onClick = [] { ProjucerApplication::getApp().rescanJUCEPathModules(); }; | |||
| addChildComponent (rescanUserPathButton); | |||
| rescanUserPathButton.onClick = [] { ProjucerApplication::getApp().rescanUserPathModules(); }; | |||
| updateFilePathPropertyComponents(); | |||
| } | |||
| @@ -85,6 +91,7 @@ public: | |||
| int labelIndex = 0; | |||
| bool isFirst = true; | |||
| bool showRescanButtons = (rescanJUCEPathButton.isVisible() && rescanUserPathButton.isVisible()); | |||
| for (auto* pathComp : pathPropertyComponents) | |||
| { | |||
| @@ -99,7 +106,20 @@ public: | |||
| if (isFirst) | |||
| b.removeFromTop (20); | |||
| pathComp->setBounds (b.removeFromTop (pathComp->getPreferredHeight())); | |||
| auto compBounds = b.removeFromTop (pathComp->getPreferredHeight()); | |||
| if (showRescanButtons) | |||
| { | |||
| auto propName = pathComp->getName(); | |||
| if (propName == "JUCE Modules") | |||
| rescanJUCEPathButton.setBounds (compBounds.removeFromRight (75).reduced (5, 0)); | |||
| else if (propName == "User Modules") | |||
| rescanUserPathButton.setBounds (compBounds.removeFromRight (75).reduced (5, 0)); | |||
| } | |||
| pathComp->setBounds (compBounds); | |||
| b.removeFromTop (5); | |||
| } | |||
| @@ -124,6 +144,8 @@ public: | |||
| private: | |||
| OwnedArray<Label> pathPropertyLabels; | |||
| OwnedArray<PropertyComponent> pathPropertyComponents; | |||
| TextButton rescanJUCEPathButton { "Re-scan" }, | |||
| rescanUserPathButton { "Re-scan" }; | |||
| ComboBox osSelector; | |||
| InfoButton info; | |||
| @@ -228,6 +250,9 @@ private: | |||
| addAndMakeVisible (pathPropertyComponents.add (new FilePathPropertyComponent (settings.getStoredPath (Ids::androidStudioExePath), | |||
| "Android Studio " + exeLabel, false))); | |||
| rescanJUCEPathButton.setVisible (true); | |||
| rescanUserPathButton.setVisible (true); | |||
| } | |||
| else | |||
| { | |||
| @@ -267,6 +292,9 @@ private: | |||
| addAndMakeVisible (pathPropertyComponents.add (new TextPropertyComponent (settings.getFallbackPathForOS (Ids::vst3Path, selectedOS), | |||
| "Custom VST3 SDK", maxChars, false))); | |||
| rescanJUCEPathButton.setVisible (false); | |||
| rescanUserPathButton.setVisible (false); | |||
| } | |||
| resized(); | |||
| @@ -81,11 +81,11 @@ void ProjucerApplication::initialise (const String& commandLine) | |||
| + "MHz Cores: " + String (SystemStats::getNumCpus()) | |||
| + " " + String (SystemStats::getMemorySizeInMegabytes()) + "MB"); | |||
| initialiseBasics(); | |||
| isRunningCommandLine = commandLine.isNotEmpty() | |||
| && ! commandLine.startsWith ("-NSDocumentRevisionsDebugMode"); | |||
| initialiseBasics(); | |||
| licenseController.reset (new LicenseController); | |||
| licenseController->addLicenseStatusChangedCallback (this); | |||
| @@ -136,6 +136,9 @@ void ProjucerApplication::initialiseBasics() | |||
| ImageCache::setCacheTimeout (30 * 1000); | |||
| icons.reset (new Icons()); | |||
| tooltipWindow.setMillisecondsBeforeTipAppears (1200); | |||
| rescanJUCEPathModules(); | |||
| rescanUserPathModules(); | |||
| } | |||
| bool ProjucerApplication::initialiseLogger (const char* filePrefix) | |||
| @@ -1415,6 +1418,37 @@ void ProjucerApplication::showSetJUCEPathAlert() | |||
| } | |||
| void ProjucerApplication::rescanJUCEPathModules() | |||
| { | |||
| File jucePath (getAppSettings().getStoredPath (Ids::defaultJuceModulePath).toString()); | |||
| if (isRunningCommandLine) | |||
| jucePathModuleList.scanPaths ({ jucePath }); | |||
| else | |||
| jucePathModuleList.scanPathsAsync ({ jucePath }); | |||
| } | |||
| static Array<File> getSanitisedUserModulePaths() | |||
| { | |||
| Array<File> paths; | |||
| for (auto p : StringArray::fromTokens (getAppSettings().getStoredPath (Ids::defaultUserModulePath).toString(), ";", {})) | |||
| { | |||
| p = p.replace ("~", File::getSpecialLocation (File::userHomeDirectory).getFullPathName()); | |||
| paths.add (File::createFileWithoutCheckingPath (p.trim())); | |||
| } | |||
| return paths; | |||
| } | |||
| void ProjucerApplication::rescanUserPathModules() | |||
| { | |||
| if (isRunningCommandLine) | |||
| userPathsModuleList.scanPaths (getSanitisedUserModulePaths()); | |||
| else | |||
| userPathsModuleList.scanPathsAsync (getSanitisedUserModulePaths()); | |||
| } | |||
| void ProjucerApplication::selectEditorColourSchemeWithName (const String& schemeName) | |||
| { | |||
| auto& appearanceSettings = getAppSettings().appearance; | |||
| @@ -139,6 +139,13 @@ public: | |||
| //============================================================================== | |||
| void setAnalyticsEnabled (bool); | |||
| //============================================================================== | |||
| void rescanJUCEPathModules(); | |||
| void rescanUserPathModules(); | |||
| AvailableModuleList& getJUCEPathModuleList() { return jucePathModuleList; } | |||
| AvailableModuleList& getUserPathsModuleList() { return userPathsModuleList; } | |||
| //============================================================================== | |||
| ProjucerLookAndFeel lookAndFeel; | |||
| @@ -204,6 +211,8 @@ private: | |||
| void showSetJUCEPathAlert(); | |||
| std::unique_ptr<AlertWindow> pathAlert; | |||
| AvailableModuleList jucePathModuleList, userPathsModuleList; | |||
| //============================================================================== | |||
| void setColourScheme (int index, bool saveSetting); | |||
| @@ -206,7 +206,7 @@ namespace | |||
| << "Name: " << proj.project->getProjectNameString() << std::endl | |||
| << "UID: " << proj.project->getProjectUIDString() << std::endl; | |||
| EnabledModuleList& modules = proj.project->getModules(); | |||
| EnabledModuleList& modules = proj.project->getEnabledModules(); | |||
| if (int numModules = modules.getNumModules()) | |||
| { | |||
| @@ -370,13 +370,13 @@ private: | |||
| scanProjectItem (proj.getMainGroup(), compileUnits, userFiles); | |||
| { | |||
| auto isVSTHost = project.getModules().isModuleEnabled ("juce_audio_processors") | |||
| auto isVSTHost = project.getEnabledModules().isModuleEnabled ("juce_audio_processors") | |||
| && (project.isConfigFlagEnabled ("JUCE_PLUGINHOST_VST3") || project.isConfigFlagEnabled ("JUCE_PLUGINHOST_VST")); | |||
| auto isPluginProject = proj.getProjectType().isAudioPlugin(); | |||
| OwnedArray<LibraryModule> modules; | |||
| proj.getModules().createRequiredModules (modules); | |||
| proj.getEnabledModules().createRequiredModules (modules); | |||
| for (Project::ExporterIterator exporter (proj); exporter.next();) | |||
| { | |||
| @@ -384,7 +384,7 @@ private: | |||
| { | |||
| for (auto* m : modules) | |||
| { | |||
| auto localModuleFolder = proj.getModules().shouldCopyModuleFilesLocally (m->moduleInfo.getID()).getValue() | |||
| auto localModuleFolder = proj.getEnabledModules().shouldCopyModuleFilesLocally (m->moduleInfo.getID()).getValue() | |||
| ? proj.getLocalModuleFolder (m->moduleInfo.getID()) | |||
| : m->moduleInfo.getFolder(); | |||
| @@ -435,7 +435,7 @@ private: | |||
| static bool areAnyModulesMissing (Project& project) | |||
| { | |||
| OwnedArray<LibraryModule> modules; | |||
| project.getModules().createRequiredModules (modules); | |||
| project.getEnabledModules().createRequiredModules (modules); | |||
| for (auto* module : modules) | |||
| if (! module->getFolder().isDirectory()) | |||
| @@ -458,7 +458,7 @@ private: | |||
| StringArray paths; | |||
| paths.addArray (getSearchPathsFromString (project.getCompileEngineSettings().getSystemHeaderPathString())); | |||
| auto isVSTHost = project.getModules().isModuleEnabled ("juce_audio_processors") | |||
| auto isVSTHost = project.getEnabledModules().isModuleEnabled ("juce_audio_processors") | |||
| && (project.isConfigFlagEnabled ("JUCE_PLUGINHOST_VST3") | |||
| || project.isConfigFlagEnabled ("JUCE_PLUGINHOST_VST")); | |||
| @@ -468,7 +468,7 @@ private: | |||
| paths.add (customVst3Path); | |||
| OwnedArray<LibraryModule> modules; | |||
| project.getModules().createRequiredModules (modules); | |||
| project.getEnabledModules().createRequiredModules (modules); | |||
| for (auto* module : modules) | |||
| { | |||
| @@ -34,10 +34,10 @@ public: | |||
| ModuleItem (Project& p, const String& modID) | |||
| : project (p), moduleID (modID) | |||
| { | |||
| missingDependencies = project.getModules().getExtraDependenciesNeeded (moduleID).size() > 0; | |||
| cppStandardHigherThanProject = project.getModules().doesModuleHaveHigherCppStandardThanProject (moduleID); | |||
| missingDependencies = project.getEnabledModules().getExtraDependenciesNeeded (moduleID).size() > 0; | |||
| cppStandardHigherThanProject = project.getEnabledModules().doesModuleHaveHigherCppStandardThanProject (moduleID); | |||
| moduleInfo = project.getModules().getModuleInfo (moduleID); | |||
| moduleInfo = project.getEnabledModules().getModuleInfo (moduleID); | |||
| } | |||
| bool canBeSelected() const override { return true; } | |||
| @@ -57,7 +57,7 @@ public: | |||
| void deleteItem() override | |||
| { | |||
| closeSettingsPage(); | |||
| project.getModules().removeModule (moduleID); | |||
| project.getEnabledModules().removeModule (moduleID); | |||
| } | |||
| Icon getIcon() const override | |||
| @@ -101,7 +101,7 @@ public: | |||
| bool checkCppStandard() | |||
| { | |||
| auto oldVal = cppStandardHigherThanProject; | |||
| cppStandardHigherThanProject = project.getModules().doesModuleHaveHigherCppStandardThanProject (moduleID); | |||
| cppStandardHigherThanProject = project.getEnabledModules().doesModuleHaveHigherCppStandardThanProject (moduleID); | |||
| if (oldVal != cppStandardHigherThanProject) | |||
| return true; | |||
| @@ -137,7 +137,7 @@ private: | |||
| { | |||
| public: | |||
| ModuleSettingsPanel (Project& p, const String& modID, TreeView* tree) | |||
| : group (p.getModules().getModuleInfo (modID).getID(), | |||
| : group (p.getEnabledModules().getModuleInfo (modID).getID(), | |||
| Icon (getIcons().singleModule, Colours::transparentBlack)), | |||
| project (p), | |||
| modulesTree (tree), | |||
| @@ -149,7 +149,7 @@ private: | |||
| void refresh() | |||
| { | |||
| auto& modules = project.getModules(); | |||
| auto& modules = project.getEnabledModules(); | |||
| setEnabled (modules.isModuleEnabled (moduleID)); | |||
| @@ -284,7 +284,7 @@ private: | |||
| void refresh() override | |||
| { | |||
| info = project.getModules().getModuleInfo (moduleID); | |||
| info = project.getEnabledModules().getModuleInfo (moduleID); | |||
| repaint(); | |||
| } | |||
| @@ -337,7 +337,7 @@ private: | |||
| MissingDependenciesComponent (Project& p, const String& modID) | |||
| : PropertyComponent ("Dependencies", 100), | |||
| project (p), moduleID (modID), | |||
| missingDependencies (project.getModules().getExtraDependenciesNeeded (modID)) | |||
| missingDependencies (project.getEnabledModules().getExtraDependenciesNeeded (modID)) | |||
| { | |||
| addAndMakeVisible (fixButton); | |||
| fixButton.setColour (TextButton::buttonColourId, Colours::red); | |||
| @@ -359,28 +359,14 @@ private: | |||
| void fixDependencies() | |||
| { | |||
| ModuleList list; | |||
| list.scanGlobalJuceModulePath(); | |||
| if (! tryToFix (list)) | |||
| if (! tryToFix()) | |||
| { | |||
| list.scanGlobalUserModulePath(); | |||
| AlertWindow::showMessageBoxAsync (AlertWindow::WarningIcon, | |||
| "Adding Missing Dependencies", | |||
| "Couldn't locate some of these modules - you'll need to find their " | |||
| "folders manually and add them to the list."); | |||
| if (! tryToFix (list)) | |||
| { | |||
| list.scanProjectExporterModulePaths (project); | |||
| if (! tryToFix (list)) | |||
| { | |||
| AlertWindow::showMessageBoxAsync (AlertWindow::WarningIcon, | |||
| "Adding Missing Dependencies", | |||
| "Couldn't locate some of these modules - you'll need to find their " | |||
| "folders manually and add them to the list."); | |||
| return; | |||
| } | |||
| } | |||
| return; | |||
| } | |||
| refreshAndReselectItem(); | |||
| @@ -397,18 +383,21 @@ private: | |||
| StringArray missingDependencies; | |||
| TextButton fixButton { "Add Required Modules" }; | |||
| bool tryToFix (ModuleList& list) | |||
| bool tryToFix() | |||
| { | |||
| auto& modules = project.getModules(); | |||
| auto copyLocally = modules.areMostModulesCopiedLocally(); | |||
| auto useGlobalPath = modules.areMostModulesUsingGlobalPath(); | |||
| auto& enabledModules = project.getEnabledModules(); | |||
| auto copyLocally = enabledModules.areMostModulesCopiedLocally(); | |||
| auto useGlobalPath = enabledModules.areMostModulesUsingGlobalPath(); | |||
| StringArray missing; | |||
| for (auto missingModule : missingDependencies) | |||
| { | |||
| if (auto* info = list.getModuleWithID (missingModule)) | |||
| modules.addModule (info->moduleFolder, copyLocally, useGlobalPath, false); | |||
| auto mod = project.getModuleWithID (missingModule); | |||
| if (mod.second != File()) | |||
| enabledModules.addModule (mod.second, copyLocally, useGlobalPath, false); | |||
| else | |||
| missing.add (missingModule); | |||
| } | |||
| @@ -477,22 +466,28 @@ private: | |||
| //============================================================================== | |||
| class EnabledModulesItem : public ProjectTreeItemBase, | |||
| private Value::Listener, | |||
| private Timer | |||
| private AvailableModuleList::Listener | |||
| { | |||
| public: | |||
| EnabledModulesItem (Project& p) | |||
| : project (p), | |||
| moduleListTree (p.getModules().state) | |||
| moduleListTree (p.getEnabledModules().state) | |||
| { | |||
| moduleListTree.addListener (this); | |||
| projectCppStandardValue.referTo (project.getProjectValue (Ids::cppLanguageStandard)); | |||
| defaultJuceModulePathValue.referTo (getAppSettings().getStoredPath (Ids::defaultJuceModulePath)); | |||
| defaultUserModulePathValue.referTo (getAppSettings().getStoredPath (Ids::defaultUserModulePath)); | |||
| projectCppStandardValue.addListener (this); | |||
| defaultJuceModulePathValue.addListener (this); | |||
| defaultUserModulePathValue.addListener (this); | |||
| ProjucerApplication::getApp().getJUCEPathModuleList().addListener (this); | |||
| ProjucerApplication::getApp().getUserPathsModuleList().addListener (this); | |||
| project.getExporterPathsModuleList().addListener (this); | |||
| } | |||
| ~EnabledModulesItem() | |||
| { | |||
| ProjucerApplication::getApp().getJUCEPathModuleList().removeListener (this); | |||
| ProjucerApplication::getApp().getUserPathsModuleList().removeListener (this); | |||
| project.getExporterPathsModuleList().removeListener (this); | |||
| } | |||
| int getItemHeight() const override { return 22; } | |||
| @@ -542,51 +537,63 @@ public: | |||
| } | |||
| for (int i = 0; i < modules.size(); ++i) | |||
| project.getModules().addModule (modules.getReference(i).moduleFolder, | |||
| project.getModules().areMostModulesCopiedLocally(), | |||
| project.getModules().areMostModulesUsingGlobalPath(), | |||
| project.getEnabledModules().addModule (modules.getReference(i).moduleFolder, | |||
| project.getEnabledModules().areMostModulesCopiedLocally(), | |||
| project.getEnabledModules().areMostModulesUsingGlobalPath(), | |||
| true); | |||
| } | |||
| void addSubItems() override | |||
| { | |||
| for (int i = 0; i < project.getModules().getNumModules(); ++i) | |||
| addSubItem (new ModuleItem (project, project.getModules().getModuleID (i))); | |||
| for (int i = 0; i < project.getEnabledModules().getNumModules(); ++i) | |||
| addSubItem (new ModuleItem (project, project.getEnabledModules().getModuleID (i))); | |||
| } | |||
| void showPopupMenu() override | |||
| { | |||
| auto& modules = project.getModules(); | |||
| PopupMenu knownModules, jucePathModules, userPathModules, exporterPathsModules; | |||
| auto& enabledModules = project.getEnabledModules(); | |||
| PopupMenu allModules; | |||
| ModuleList list; | |||
| int index = 100; | |||
| list.scanGlobalJuceModulePath(); | |||
| // JUCE path | |||
| PopupMenu jucePathModules; | |||
| int index = 100; | |||
| for (auto m : list.getIDs()) | |||
| jucePathModules.addItem (index++, m, ! modules.isModuleEnabled (m)); | |||
| for (auto& mod : ProjucerApplication::getApp().getJUCEPathModuleList().getAllModules()) | |||
| jucePathModules.addItem (index++, mod.first, ! enabledModules.isModuleEnabled (mod.first)); | |||
| knownModules.addSubMenu ("Global JUCE modules path", jucePathModules); | |||
| jucePathModules.addSeparator(); | |||
| jucePathModules.addItem (-1, "Re-scan path"); | |||
| list.scanGlobalUserModulePath(); | |||
| allModules.addSubMenu ("Global JUCE modules path", jucePathModules); | |||
| // User path | |||
| index = 200; | |||
| for (auto m : list.getIDs()) | |||
| userPathModules.addItem (index++, m, ! modules.isModuleEnabled (m)); | |||
| PopupMenu userPathModules; | |||
| knownModules.addSubMenu ("Global user modules path", userPathModules); | |||
| for (auto& mod : ProjucerApplication::getApp().getUserPathsModuleList().getAllModules()) | |||
| userPathModules.addItem (index++, mod.first, ! enabledModules.isModuleEnabled (mod.first)); | |||
| list.scanProjectExporterModulePaths (project); | |||
| userPathModules.addSeparator(); | |||
| userPathModules.addItem (-2, "Re-scan path"); | |||
| allModules.addSubMenu ("Global user modules path", userPathModules); | |||
| // Exporter path | |||
| index = 300; | |||
| for (auto m : list.getIDs()) | |||
| exporterPathsModules.addItem (index++, m, ! modules.isModuleEnabled (m)); | |||
| PopupMenu exporterPathModules; | |||
| for (auto& mod : project.getExporterPathsModuleList().getAllModules()) | |||
| exporterPathModules.addItem (index++, mod.first, ! enabledModules.isModuleEnabled (mod.first)); | |||
| knownModules.addSubMenu ("Exporter paths", exporterPathsModules); | |||
| exporterPathModules.addSeparator(); | |||
| exporterPathModules.addItem (-3, "Re-scan path"); | |||
| allModules.addSubMenu ("Exporter paths", exporterPathModules); | |||
| PopupMenu menu; | |||
| menu.addSubMenu ("Add a module", knownModules); | |||
| menu.addSubMenu ("Add a module", allModules); | |||
| menu.addSeparator(); | |||
| menu.addItem (1001, "Add a module from a specified folder..."); | |||
| @@ -595,35 +602,39 @@ public: | |||
| void handlePopupMenuResult (int resultCode) override | |||
| { | |||
| auto& modules = project.getModules(); | |||
| if (resultCode == 1001) | |||
| { | |||
| modules.addModuleFromUserSelectedFile(); | |||
| project.getEnabledModules().addModuleFromUserSelectedFile(); | |||
| } | |||
| else if (resultCode < 0) | |||
| { | |||
| if (resultCode == -1) ProjucerApplication::getApp().rescanJUCEPathModules(); | |||
| else if (resultCode == -2) ProjucerApplication::getApp().rescanUserPathModules(); | |||
| else if (resultCode == -3) project.rescanExporterPathModules(); | |||
| } | |||
| else if (resultCode > 0) | |||
| { | |||
| ModuleList list; | |||
| std::vector<ModuleIDAndFolder> list; | |||
| int offset = -1; | |||
| if (resultCode < 200) | |||
| { | |||
| list.scanGlobalJuceModulePath(); | |||
| list = ProjucerApplication::getApp().getJUCEPathModuleList().getAllModules(); | |||
| offset = 100; | |||
| } | |||
| else if (resultCode < 300) | |||
| { | |||
| list.scanGlobalUserModulePath(); | |||
| list = ProjucerApplication::getApp().getUserPathsModuleList().getAllModules(); | |||
| offset = 200; | |||
| } | |||
| else if (resultCode < 400) | |||
| { | |||
| list.scanProjectExporterModulePaths (project); | |||
| list = project.getExporterPathsModuleList().getAllModules(); | |||
| offset = 300; | |||
| } | |||
| if (offset != -1) | |||
| modules.addModuleInteractive (list.getIDs() [resultCode - offset]); | |||
| project.getEnabledModules().addModuleInteractive (list[(size_t) (resultCode - offset)].first); | |||
| } | |||
| } | |||
| @@ -641,7 +652,7 @@ public: | |||
| private: | |||
| Project& project; | |||
| ValueTree moduleListTree; | |||
| Value projectCppStandardValue, defaultJuceModulePathValue, defaultUserModulePathValue; | |||
| Value projectCppStandardValue; | |||
| //============================================================================== | |||
| void valueChanged (Value& v) override | |||
| @@ -660,25 +671,26 @@ private: | |||
| } | |||
| } | |||
| } | |||
| else if (v == defaultJuceModulePathValue || v == defaultUserModulePathValue) | |||
| { | |||
| auto juceModulePathChanged = (v == defaultJuceModulePathValue); | |||
| } | |||
| for (int i = 0; i < getNumSubItems(); ++i) | |||
| if (auto* moduleItem = dynamic_cast<ModuleItem*> (getSubItem (i))) | |||
| moduleItem->refreshModuleInfoIfCurrentlyShowing (juceModulePathChanged); | |||
| void removeDuplicateModules() | |||
| { | |||
| auto jucePathModuleList = ProjucerApplication::getApp().getJUCEPathModuleList().getAllModules(); | |||
| // coalesce changes using a timer in case the value is changing rapidly | |||
| startTimer (750); | |||
| } | |||
| auto& userPathModules = ProjucerApplication::getApp().getUserPathsModuleList(); | |||
| userPathModules.removeDuplicates (jucePathModuleList); | |||
| auto& exporterPathModules = project.getExporterPathsModuleList(); | |||
| exporterPathModules.removeDuplicates (jucePathModuleList); | |||
| exporterPathModules.removeDuplicates (userPathModules.getAllModules()); | |||
| } | |||
| //============================================================================== | |||
| void timerCallback() override | |||
| void availableModulesChanged() override | |||
| { | |||
| stopTimer(); | |||
| removeDuplicateModules(); | |||
| refreshSubItems(); | |||
| } | |||
| //============================================================================== | |||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (EnabledModulesItem) | |||
| }; | |||
| @@ -35,7 +35,7 @@ class ModulesInformationComponent : public Component, | |||
| public: | |||
| ModulesInformationComponent (Project& p) | |||
| : project (p), | |||
| modulesValueTree (p.getModules().state) | |||
| modulesValueTree (p.getEnabledModules().state) | |||
| { | |||
| listHeader = new ListBoxHeader ( { "Module", "Version", "Make Local Copy", "Paths" }, | |||
| { 0.25f, 0.2f, 0.2f, 0.35f } ); | |||
| @@ -106,7 +106,7 @@ public: | |||
| int getNumRows() override | |||
| { | |||
| return project.getModules().getNumModules(); | |||
| return project.getEnabledModules().getNumModules(); | |||
| } | |||
| void paintListBoxItem (int rowNumber, Graphics& g, int width, int height, bool rowIsSelected) override | |||
| @@ -123,26 +123,26 @@ public: | |||
| g.setColour (rowIsSelected ? findColour (defaultHighlightedTextColourId) : findColour (widgetTextColourId)); | |||
| //====================================================================== | |||
| auto moduleID = project.getModules().getModuleID (rowNumber); | |||
| auto moduleID = project.getEnabledModules().getModuleID (rowNumber); | |||
| g.drawFittedText (moduleID, bounds.removeFromLeft (roundToInt (listHeader->getProportionAtIndex (0) * width)), Justification::centredLeft, 1); | |||
| //====================================================================== | |||
| auto version = project.getModules().getModuleInfo (moduleID).getVersion(); | |||
| auto version = project.getEnabledModules().getModuleInfo (moduleID).getVersion(); | |||
| if (version.isEmpty()) | |||
| version = "?"; | |||
| g.drawFittedText (version, bounds.removeFromLeft (roundToInt (listHeader->getProportionAtIndex (1) * width)), Justification::centredLeft, 1); | |||
| //====================================================================== | |||
| auto copyLocally = project.getModules().shouldCopyModuleFilesLocally (moduleID).getValue() ? "Yes" : "No"; | |||
| auto copyLocally = project.getEnabledModules().shouldCopyModuleFilesLocally (moduleID).getValue() ? "Yes" : "No"; | |||
| g.drawFittedText (copyLocally, bounds.removeFromLeft (roundToInt (listHeader->getProportionAtIndex (2) * width)), Justification::centredLeft, 1); | |||
| //====================================================================== | |||
| String pathText; | |||
| if (project.getModules().shouldUseGlobalPath (moduleID)) | |||
| if (project.getEnabledModules().shouldUseGlobalPath (moduleID)) | |||
| { | |||
| pathText = "Global"; | |||
| } | |||
| @@ -161,7 +161,7 @@ public: | |||
| void listBoxItemDoubleClicked (int row, const MouseEvent&) override | |||
| { | |||
| auto moduleID = project.getModules().getModuleID (row); | |||
| auto moduleID = project.getEnabledModules().getModuleID (row); | |||
| if (moduleID.isNotEmpty()) | |||
| if (auto* pcc = findParentComponentOfClass<ProjectContentComponent>()) | |||
| @@ -170,7 +170,7 @@ public: | |||
| void deleteKeyPressed (int row) override | |||
| { | |||
| project.getModules().removeModule (project.getModules().getModuleID (row)); | |||
| project.getEnabledModules().removeModule (project.getEnabledModules().getModuleID (row)); | |||
| } | |||
| void lookAndFeelChanged() override | |||
| @@ -224,7 +224,7 @@ private: | |||
| auto res = m.showAt (&setCopyModeButton); | |||
| if (res != 0) | |||
| project.getModules().setLocalCopyModeForAllModules (res == 1); | |||
| project.getEnabledModules().setLocalCopyModeForAllModules (res == 1); | |||
| } | |||
| void showGlobalPathsMenu() | |||
| @@ -243,7 +243,7 @@ private: | |||
| { | |||
| auto enableGlobalPaths = (res % 2 == 1); | |||
| auto& moduleList = project.getModules(); | |||
| auto& moduleList = project.getEnabledModules(); | |||
| if (res < 3) | |||
| { | |||
| @@ -271,7 +271,7 @@ private: | |||
| pastePathsID | |||
| }; | |||
| auto& moduleList = project.getModules(); | |||
| auto& moduleList = project.getEnabledModules(); | |||
| auto moduleToCopy = moduleList.getModuleID (list.getSelectedRow()); | |||
| if (moduleToCopy.isNotEmpty()) | |||
| @@ -25,10 +25,9 @@ | |||
| */ | |||
| #include "../Application/jucer_Headers.h" | |||
| #include "jucer_Module.h" | |||
| #include "../ProjectSaving/jucer_ProjectSaver.h" | |||
| #include "../ProjectSaving/jucer_ProjectExport_Xcode.h" | |||
| #include "../Application/jucer_ProjucerAnalytics.h" | |||
| #include "../Application/jucer_Application.h" | |||
| //============================================================================== | |||
| static var parseModuleDesc (const StringArray& lines) | |||
| @@ -113,195 +112,154 @@ StringArray ModuleDescription::getDependencies() const | |||
| } | |||
| //============================================================================== | |||
| ModuleList::ModuleList() | |||
| { | |||
| } | |||
| ModuleList::ModuleList (const ModuleList& other) | |||
| { | |||
| operator= (other); | |||
| } | |||
| ModuleList& ModuleList::operator= (const ModuleList& other) | |||
| { | |||
| modules.clear(); | |||
| modules.addCopiesOf (other.modules); | |||
| return *this; | |||
| } | |||
| const ModuleDescription* ModuleList::getModuleWithID (const String& moduleID) const | |||
| { | |||
| for (auto* m : modules) | |||
| if (m->getID() == moduleID) | |||
| return m; | |||
| return nullptr; | |||
| } | |||
| void ModuleList::sort() | |||
| { | |||
| std::sort (modules.begin(), modules.end(), [] (const ModuleDescription* m1, const ModuleDescription* m2) | |||
| { | |||
| return m1->getID().compareIgnoreCase (m2->getID()) < 0; | |||
| }); | |||
| } | |||
| StringArray ModuleList::getIDs() const | |||
| { | |||
| StringArray results; | |||
| for (auto* m : modules) | |||
| results.add (m->getID()); | |||
| results.sort (true); | |||
| return results; | |||
| } | |||
| bool ModuleList::tryToAddModuleFromFolder (const File& path) | |||
| static bool tryToAddModuleFromFolder (const File& path, ModuleIDAndFolderList& list) | |||
| { | |||
| ModuleDescription m (path); | |||
| if (m.isValid()) | |||
| { | |||
| getPreviousModuleDirectories().addIfNotAlreadyThere (path.getParentDirectory()); | |||
| modules.add (new ModuleDescription (m)); | |||
| list.push_back ({ m.getID(), path }); | |||
| return true; | |||
| } | |||
| return false; | |||
| } | |||
| void ModuleList::addAllModulesInFolder (const File& path) | |||
| static bool addAllModulesInSubfoldersRecursively (const File& path, int depth, ModuleIDAndFolderList& list) | |||
| { | |||
| if (! tryToAddModuleFromFolder (path)) | |||
| if (depth > 0) | |||
| { | |||
| for (DirectoryIterator iter (path, false, "*", File::findDirectories); iter.next();) | |||
| tryToAddModuleFromFolder (iter.getFile().getLinkedTarget()); | |||
| } | |||
| } | |||
| { | |||
| if (auto* job = ThreadPoolJob::getCurrentThreadPoolJob()) | |||
| if (job->shouldExit()) | |||
| return false; | |||
| Array<File>& ModuleList::getPreviousModuleDirectories() | |||
| { | |||
| static Array<File> previous; | |||
| return previous; | |||
| } | |||
| auto childPath = iter.getFile().getLinkedTarget(); | |||
| File ModuleList::tryToFindModulePathFromPrevious (const String& id) | |||
| { | |||
| for (auto& f : getPreviousModuleDirectories()) | |||
| { | |||
| auto modulePath = f.getChildFile (id); | |||
| if (ModuleDescription (modulePath).isValid()) | |||
| return modulePath; | |||
| if (! tryToAddModuleFromFolder (childPath, list)) | |||
| if (! addAllModulesInSubfoldersRecursively (childPath, depth - 1, list)) | |||
| return false; | |||
| } | |||
| } | |||
| return {}; | |||
| return true; | |||
| } | |||
| //============================================================================== | |||
| static File getModuleFolderFromPathIfItExists (const String& path, const String& moduleID) | |||
| static bool addAllModulesInFolder (const File& path, ModuleIDAndFolderList& list) | |||
| { | |||
| if (path.isNotEmpty()) | |||
| { | |||
| ModuleList list; | |||
| list.addAllModulesInFolder (path); | |||
| bool exitedBeforeCompletion = false; | |||
| if (auto* desc = list.getModuleWithID (moduleID)) | |||
| return desc->getFolder(); | |||
| if (! tryToAddModuleFromFolder (path, list)) | |||
| { | |||
| int subfolders = 3; | |||
| exitedBeforeCompletion = addAllModulesInSubfoldersRecursively (path, subfolders, list); | |||
| } | |||
| return {}; | |||
| return exitedBeforeCompletion; | |||
| } | |||
| static Array<File> getAllPossibleModulePathsFromExporters (Project& project, bool onlyThisOS) | |||
| static void sort (ModuleIDAndFolderList& listToSort) | |||
| { | |||
| StringArray paths; | |||
| std::sort (listToSort.begin(), listToSort.end(), [] (const ModuleIDAndFolder& m1, const ModuleIDAndFolder& m2) | |||
| { | |||
| return m1.first.compareIgnoreCase (m2.first) < 0; | |||
| }); | |||
| } | |||
| for (Project::ExporterIterator exporter (project); exporter.next();) | |||
| //============================================================================== | |||
| struct ModuleScannerJob : public ThreadPoolJob | |||
| { | |||
| ModuleScannerJob (const Array<File>& paths, std::function<void (const ModuleIDAndFolderList&)>&& callback) | |||
| : ThreadPoolJob ("ModuleScannerJob"), | |||
| pathsToScan (paths), | |||
| completionCallback (std::move (callback)) | |||
| { | |||
| if (onlyThisOS && ! exporter->mayCompileOnCurrentOS()) | |||
| continue; | |||
| auto& modules = project.getModules(); | |||
| auto n = modules.getNumModules(); | |||
| for (int i = 0; i < n; ++i) | |||
| { | |||
| auto id = modules.getModuleID (i); | |||
| } | |||
| if (modules.shouldUseGlobalPath (id)) | |||
| continue; | |||
| JobStatus runJob() override | |||
| { | |||
| ModuleIDAndFolderList list; | |||
| auto path = exporter->getPathForModuleString (id); | |||
| for (auto& p : pathsToScan) | |||
| if (! addAllModulesInFolder (p, list)) | |||
| return jobNeedsRunningAgain; | |||
| if (path.isNotEmpty()) | |||
| paths.addIfNotAlreadyThere (path); | |||
| } | |||
| sort (list); | |||
| completionCallback (list); | |||
| auto oldPath = exporter->getLegacyModulePath(); | |||
| if (oldPath.isNotEmpty()) | |||
| paths.addIfNotAlreadyThere (oldPath); | |||
| return jobHasFinished; | |||
| } | |||
| Array<File> files; | |||
| Array<File> pathsToScan; | |||
| std::function<void (const ModuleIDAndFolderList&)> completionCallback; | |||
| }; | |||
| for (auto& path : paths) | |||
| { | |||
| auto f = project.resolveFilename (path); | |||
| AvailableModuleList::AvailableModuleList() | |||
| { | |||
| } | |||
| if (f.isDirectory()) | |||
| { | |||
| files.addIfNotAlreadyThere (f); | |||
| ThreadPoolJob* AvailableModuleList::createScannerJob (const Array<File>& paths) | |||
| { | |||
| return new ModuleScannerJob (paths, [this] (ModuleIDAndFolderList scannedModuleList) | |||
| { | |||
| { | |||
| const ScopedLock swapLock (lock); | |||
| moduleList.swap (scannedModuleList); | |||
| } | |||
| if (f.getChildFile ("modules").isDirectory()) | |||
| files.addIfNotAlreadyThere (f.getChildFile ("modules")); | |||
| } | |||
| } | |||
| MessageManager::callAsync ([this] { listeners.call ([] (Listener& l) { l.availableModulesChanged(); }); }); | |||
| }); | |||
| } | |||
| return files; | |||
| void AvailableModuleList::removePendingAndAddJob (ThreadPoolJob* jobToAdd) | |||
| { | |||
| scanPool.removeAllJobs (false, 100); | |||
| scanPool.addJob (jobToAdd, true); | |||
| } | |||
| void ModuleList::scanProjectExporterModulePaths (Project& project) | |||
| void AvailableModuleList::scanPaths (const Array<File>& paths) | |||
| { | |||
| modules.clear(); | |||
| auto* job = createScannerJob (paths); | |||
| for (auto& m : getAllPossibleModulePathsFromExporters (project, false)) | |||
| addAllModulesInFolder (m); | |||
| removePendingAndAddJob (job); | |||
| scanPool.waitForJobToFinish (job, -1); | |||
| } | |||
| sort(); | |||
| void AvailableModuleList::scanPathsAsync (const Array<File>& paths) | |||
| { | |||
| removePendingAndAddJob (createScannerJob (paths)); | |||
| } | |||
| void ModuleList::scanGlobalJuceModulePath() | |||
| ModuleIDAndFolderList AvailableModuleList::getAllModules() const | |||
| { | |||
| modules.clear(); | |||
| const ScopedLock readLock (lock); | |||
| auto& settings = getAppSettings(); | |||
| return moduleList; | |||
| } | |||
| auto path = settings.getStoredPath (Ids::defaultJuceModulePath).toString(); | |||
| ModuleIDAndFolder AvailableModuleList::getModuleWithID (const String& id) const | |||
| { | |||
| const ScopedLock readLock (lock); | |||
| if (path.isNotEmpty()) | |||
| addAllModulesInFolder ({ path }); | |||
| for (auto& mod : moduleList) | |||
| if (mod.first == id) | |||
| return mod; | |||
| sort(); | |||
| return {}; | |||
| } | |||
| void ModuleList::scanGlobalUserModulePath() | |||
| void AvailableModuleList::removeDuplicates (const ModuleIDAndFolderList& other) | |||
| { | |||
| modules.clear(); | |||
| const ScopedLock readLock (lock); | |||
| auto paths = StringArray::fromTokens (getAppSettings().getStoredPath (Ids::defaultUserModulePath).toString(), ";", {}); | |||
| for (auto p : paths) | |||
| for (auto& m : other) | |||
| { | |||
| p = p.replace ("~", File::getSpecialLocation (File::userHomeDirectory).getFullPathName()); | |||
| auto pos = std::find (moduleList.begin(), moduleList.end(), m); | |||
| auto f = File::createFileWithoutCheckingPath (p.trim()); | |||
| if (f.exists()) | |||
| addAllModulesInFolder (f); | |||
| if (pos != moduleList.end()) | |||
| moduleList.erase (pos); | |||
| } | |||
| sort(); | |||
| } | |||
| //============================================================================== | |||
| @@ -314,7 +272,7 @@ LibraryModule::LibraryModule (const ModuleDescription& d) | |||
| void LibraryModule::writeIncludes (ProjectSaver& projectSaver, OutputStream& out) | |||
| { | |||
| auto& project = projectSaver.project; | |||
| auto& modules = project.getModules(); | |||
| auto& modules = project.getEnabledModules(); | |||
| auto id = getID(); | |||
| @@ -382,7 +340,7 @@ void LibraryModule::addSettingsForModuleToExporter (ProjectExporter& exporter, P | |||
| { | |||
| Array<File> compiled; | |||
| auto& modules = project.getModules(); | |||
| auto& modules = project.getEnabledModules(); | |||
| auto id = getID(); | |||
| auto localModuleFolder = modules.shouldCopyModuleFilesLocally (id).getValue() ? project.getLocalModuleFolder (id) | |||
| @@ -649,7 +607,7 @@ EnabledModuleList::EnabledModuleList (Project& p, const ValueTree& s) | |||
| ModuleDescription EnabledModuleList::getModuleInfo (const String& moduleID) | |||
| { | |||
| return ModuleDescription (findFolderForModule (moduleID)); | |||
| return ModuleDescription (project.getModuleWithID (moduleID).second); | |||
| } | |||
| bool EnabledModuleList::isModuleEnabled (const String& moduleID) const | |||
| @@ -681,48 +639,6 @@ Value EnabledModuleList::shouldShowAllModuleFilesInProject (const String& module | |||
| .getPropertyAsValue (Ids::showAllCode, getUndoManager()); | |||
| } | |||
| File EnabledModuleList::findFolderForModule (const String& moduleID) | |||
| { | |||
| if (shouldUseGlobalPath (moduleID)) | |||
| { | |||
| if (isJUCEModule (moduleID)) | |||
| return File (getAppSettings().getStoredPath (Ids::defaultJuceModulePath).toString()).getChildFile (moduleID); | |||
| { | |||
| auto previous = ModuleList::tryToFindModulePathFromPrevious (moduleID); | |||
| if (previous != File()) | |||
| return previous; | |||
| } | |||
| ModuleList list; | |||
| list.scanGlobalUserModulePath(); | |||
| if (auto* desc = list.getModuleWithID (moduleID)) | |||
| return desc->getFolder(); | |||
| } | |||
| else | |||
| { | |||
| for (auto p : getAllPossibleModulePathsFromExporters (project, true)) | |||
| { | |||
| auto f = getModuleFolderFromPathIfItExists (p.getFullPathName(), moduleID); | |||
| if (f != File()) | |||
| return f; | |||
| } | |||
| for (auto p : getAllPossibleModulePathsFromExporters (project, false)) | |||
| { | |||
| auto f = getModuleFolderFromPathIfItExists (p.getFullPathName(), moduleID); | |||
| if (f != File()) | |||
| return f; | |||
| } | |||
| } | |||
| return {}; | |||
| } | |||
| struct ModuleTreeSorter | |||
| { | |||
| static int compareElements (const ValueTree& m1, const ValueTree& m2) | |||
| @@ -754,7 +670,7 @@ void EnabledModuleList::addModule (const File& moduleFolder, bool copyLocally, b | |||
| if (! isModuleEnabled (moduleID)) | |||
| { | |||
| ValueTree module (Ids::MODULE); | |||
| module.setProperty (Ids::ID, moduleID, nullptr); | |||
| module.setProperty (Ids::ID, moduleID, getUndoManager()); | |||
| state.appendChild (module, getUndoManager()); | |||
| sortAlphabetically(); | |||
| @@ -769,6 +685,9 @@ void EnabledModuleList::addModule (const File& moduleFolder, bool copyLocally, b | |||
| for (Project::ExporterIterator exporter (project); exporter.next();) | |||
| exporter->getPathForModuleValue (moduleID) = path.toUnixStyle(); | |||
| if (! useGlobalPath) | |||
| project.rescanExporterPathModules (false); | |||
| if (sendAnalyticsEvent) | |||
| { | |||
| StringPairArray data; | |||
| @@ -808,7 +727,7 @@ StringArray EnabledModuleList::getAllModules() const | |||
| static void getDependencies (Project& project, const String& moduleID, StringArray& dependencies) | |||
| { | |||
| auto info = project.getModules().getModuleInfo (moduleID); | |||
| auto info = project.getEnabledModules().getModuleInfo (moduleID); | |||
| for (auto uid : info.getDependencies()) | |||
| { | |||
| @@ -877,7 +796,7 @@ bool EnabledModuleList::areMostModulesCopiedLocally() const | |||
| void EnabledModuleList::setLocalCopyModeForAllModules (bool copyLocally) | |||
| { | |||
| for (auto i = getNumModules(); --i >= 0;) | |||
| shouldCopyModuleFilesLocally (project.getModules().getModuleID (i)) = copyLocally; | |||
| shouldCopyModuleFilesLocally (project.getEnabledModules().getModuleID (i)) = copyLocally; | |||
| } | |||
| File EnabledModuleList::findDefaultModulesFolder (Project& project) | |||
| @@ -887,12 +806,9 @@ File EnabledModuleList::findDefaultModulesFolder (Project& project) | |||
| if (globalPath != File()) | |||
| return globalPath; | |||
| ModuleList available; | |||
| available.scanProjectExporterModulePaths (project); | |||
| for (auto i = available.modules.size(); --i >= 0;) | |||
| for (auto& exporterPathModule : project.getExporterPathsModuleList().getAllModules()) | |||
| { | |||
| auto f = available.modules.getUnchecked(i)->getFolder(); | |||
| auto f = exporterPathModule.second; | |||
| if (f.isDirectory()) | |||
| return f.getParentDirectory(); | |||
| @@ -916,27 +832,15 @@ void EnabledModuleList::addModuleFromUserSelectedFile() | |||
| void EnabledModuleList::addModuleInteractive (const String& moduleID) | |||
| { | |||
| ModuleList list; | |||
| auto f = project.getModuleWithID (moduleID).second; | |||
| list.scanGlobalJuceModulePath(); | |||
| if (auto* info = list.getModuleWithID (moduleID)) | |||
| if (f != File()) | |||
| { | |||
| addModule (info->moduleFolder, areMostModulesCopiedLocally(), areMostModulesUsingGlobalPath(), true); | |||
| addModule (f, areMostModulesCopiedLocally(), areMostModulesUsingGlobalPath(), true); | |||
| return; | |||
| } | |||
| list.scanGlobalUserModulePath(); | |||
| if (auto* info = list.getModuleWithID (moduleID)) | |||
| { | |||
| addModule (info->moduleFolder, areMostModulesCopiedLocally(), areMostModulesUsingGlobalPath(), true); | |||
| return; | |||
| } | |||
| list.scanProjectExporterModulePaths (project); | |||
| if (auto* info = list.getModuleWithID (moduleID)) | |||
| addModule (info->moduleFolder, areMostModulesCopiedLocally(), false, true); | |||
| else | |||
| addModuleFromUserSelectedFile(); | |||
| addModuleFromUserSelectedFile(); | |||
| } | |||
| void EnabledModuleList::addModuleOfferingToCopy (const File& f, bool isFromUserSpecifiedFolder) | |||
| @@ -64,31 +64,6 @@ struct ModuleDescription | |||
| URL url; | |||
| }; | |||
| //============================================================================== | |||
| struct ModuleList | |||
| { | |||
| ModuleList(); | |||
| ModuleList (const ModuleList&); | |||
| ModuleList& operator= (const ModuleList&); | |||
| const ModuleDescription* getModuleWithID (const String& moduleID) const; | |||
| StringArray getIDs() const; | |||
| void sort(); | |||
| bool tryToAddModuleFromFolder (const File&); | |||
| void addAllModulesInFolder (const File&); | |||
| static Array<File>& getPreviousModuleDirectories(); | |||
| static File tryToFindModulePathFromPrevious (const String&); | |||
| void scanProjectExporterModulePaths (Project&); | |||
| void scanGlobalJuceModulePath(); | |||
| void scanGlobalUserModulePath(); | |||
| OwnedArray<ModuleDescription> modules; | |||
| }; | |||
| //============================================================================== | |||
| class LibraryModule | |||
| { | |||
| @@ -135,6 +110,47 @@ private: | |||
| void addBrowseableCode (ProjectExporter&, const Array<File>& compiled, const File& localModuleFolder) const; | |||
| }; | |||
| //============================================================================== | |||
| using ModuleIDAndFolder = std::pair<String, File>; | |||
| using ModuleIDAndFolderList = std::vector<ModuleIDAndFolder>; | |||
| class AvailableModuleList | |||
| { | |||
| public: | |||
| AvailableModuleList(); | |||
| void scanPaths (const Array<File>&); | |||
| void scanPathsAsync (const Array<File>&); | |||
| ModuleIDAndFolderList getAllModules() const; | |||
| ModuleIDAndFolder getModuleWithID (const String&) const; | |||
| void removeDuplicates (const ModuleIDAndFolderList& other); | |||
| //============================================================================== | |||
| struct Listener | |||
| { | |||
| virtual ~Listener() {} | |||
| virtual void availableModulesChanged() = 0; | |||
| }; | |||
| void addListener (Listener* listenerToAdd) { listeners.add (listenerToAdd); } | |||
| void removeListener (Listener* listenerToRemove) { listeners.remove (listenerToRemove); } | |||
| private: | |||
| ThreadPoolJob* createScannerJob (const Array<File>&); | |||
| void removePendingAndAddJob (ThreadPoolJob*); | |||
| ThreadPool scanPool { 1 }; | |||
| ModuleIDAndFolderList moduleList; | |||
| ListenerList<Listener> listeners; | |||
| CriticalSection lock; | |||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AvailableModuleList) | |||
| }; | |||
| //============================================================================== | |||
| class EnabledModuleList | |||
| { | |||
| @@ -182,7 +198,5 @@ public: | |||
| private: | |||
| UndoManager* getUndoManager() const { return project.getUndoManagerFor (state); } | |||
| File findFolderForModule (const String& moduleID); | |||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (EnabledModuleList) | |||
| }; | |||
| @@ -64,7 +64,7 @@ Project::Project (const File& f) | |||
| parsedPreprocessorDefs = parsePreprocessorDefs (preprocessorDefsValue.get()); | |||
| getModules().sortAlphabetically(); | |||
| getEnabledModules().sortAlphabetically(); | |||
| projectRoot.addListener (this); | |||
| @@ -488,45 +488,33 @@ static int getBuiltJuceVersion() | |||
| + JUCE_BUILDNUMBER; | |||
| } | |||
| static bool isAnyModuleNewerThanProjucer (const OwnedArray<ModuleDescription>& modules) | |||
| static bool isModuleNewerThanProjucer (const ModuleDescription& module) | |||
| { | |||
| for (auto i = modules.size(); --i >= 0;) | |||
| { | |||
| auto* m = modules.getUnchecked(i); | |||
| if (m->getID().startsWith ("juce_") | |||
| && getJuceVersion (m->getVersion()) > getBuiltJuceVersion()) | |||
| return true; | |||
| } | |||
| if (module.getID().startsWith ("juce_") | |||
| && getJuceVersion (module.getVersion()) > getBuiltJuceVersion()) | |||
| return true; | |||
| return false; | |||
| } | |||
| void Project::warnAboutOldProjucerVersion() | |||
| { | |||
| ModuleList available; | |||
| available.scanGlobalJuceModulePath(); | |||
| if (! isAnyModuleNewerThanProjucer (available.modules)) | |||
| available.scanGlobalUserModulePath(); | |||
| if (! isAnyModuleNewerThanProjucer (available.modules)) | |||
| available.scanProjectExporterModulePaths (*this); | |||
| if (! isAnyModuleNewerThanProjucer (available.modules)) | |||
| return; | |||
| // Projucer is out of date! | |||
| if (ProjucerApplication::getApp().isRunningCommandLine) | |||
| std::cout << "WARNING! This version of the Projucer is out-of-date!" << std::endl; | |||
| else | |||
| AlertWindow::showMessageBoxAsync (AlertWindow::WarningIcon, | |||
| "Projucer", | |||
| "This version of the Projucer is out-of-date!" | |||
| "\n\n" | |||
| "Always make sure that you're running the very latest version, " | |||
| "preferably compiled directly from the JUCE repository that you're working with!"); | |||
| for (auto& juceModule : ProjucerApplication::getApp().getJUCEPathModuleList().getAllModules()) | |||
| { | |||
| if (isModuleNewerThanProjucer ({ juceModule.second })) | |||
| { | |||
| // Projucer is out of date! | |||
| if (ProjucerApplication::getApp().isRunningCommandLine) | |||
| std::cout << "WARNING! This version of the Projucer is out-of-date!" << std::endl; | |||
| else | |||
| AlertWindow::showMessageBoxAsync (AlertWindow::WarningIcon, | |||
| "Projucer", | |||
| "This version of the Projucer is out-of-date!" | |||
| "\n\n" | |||
| "Always make sure that you're running the very latest version, " | |||
| "preferably compiled directly from the JUCE repository that you're working with!"); | |||
| } | |||
| } | |||
| } | |||
| //============================================================================== | |||
| @@ -564,7 +552,7 @@ Result Project::loadDocument (const File& file) | |||
| registerRecentFile (file); | |||
| enabledModulesList.reset(); | |||
| enabledModuleList.reset(); | |||
| projectRoot = newTree; | |||
| initialiseProjectValues(); | |||
| @@ -586,6 +574,9 @@ Result Project::loadDocument (const File& file) | |||
| compileEngineSettings.reset (new CompileEngineSettings (projectRoot)); | |||
| exporterPathsModuleList.reset (new AvailableModuleList()); | |||
| rescanExporterPathModules (! ProjucerApplication::getApp().isRunningCommandLine); | |||
| return Result::ok(); | |||
| } | |||
| @@ -1744,17 +1735,17 @@ String Project::getIAAPluginName() | |||
| //============================================================================== | |||
| bool Project::isAUPluginHost() | |||
| { | |||
| return getModules().isModuleEnabled ("juce_audio_processors") && isConfigFlagEnabled ("JUCE_PLUGINHOST_AU"); | |||
| return getEnabledModules().isModuleEnabled ("juce_audio_processors") && isConfigFlagEnabled ("JUCE_PLUGINHOST_AU"); | |||
| } | |||
| bool Project::isVSTPluginHost() | |||
| { | |||
| return getModules().isModuleEnabled ("juce_audio_processors") && isConfigFlagEnabled ("JUCE_PLUGINHOST_VST"); | |||
| return getEnabledModules().isModuleEnabled ("juce_audio_processors") && isConfigFlagEnabled ("JUCE_PLUGINHOST_VST"); | |||
| } | |||
| bool Project::isVST3PluginHost() | |||
| { | |||
| return getModules().isModuleEnabled ("juce_audio_processors") && isConfigFlagEnabled ("JUCE_PLUGINHOST_VST3"); | |||
| return getEnabledModules().isModuleEnabled ("juce_audio_processors") && isConfigFlagEnabled ("JUCE_PLUGINHOST_VST3"); | |||
| } | |||
| //============================================================================== | |||
| @@ -1875,12 +1866,83 @@ Array<var> Project::getDefaultRTASCategories() const noexcept | |||
| } | |||
| //============================================================================== | |||
| EnabledModuleList& Project::getModules() | |||
| EnabledModuleList& Project::getEnabledModules() | |||
| { | |||
| if (enabledModuleList == nullptr) | |||
| enabledModuleList.reset (new EnabledModuleList (*this, projectRoot.getOrCreateChildWithName (Ids::MODULES, nullptr))); | |||
| return *enabledModuleList; | |||
| } | |||
| static Array<File> getAllPossibleModulePathsFromExporters (Project& project) | |||
| { | |||
| StringArray paths; | |||
| for (Project::ExporterIterator exporter (project); exporter.next();) | |||
| { | |||
| auto& modules = project.getEnabledModules(); | |||
| auto n = modules.getNumModules(); | |||
| for (int i = 0; i < n; ++i) | |||
| { | |||
| auto id = modules.getModuleID (i); | |||
| if (modules.shouldUseGlobalPath (id)) | |||
| continue; | |||
| auto path = exporter->getPathForModuleString (id); | |||
| if (path.isNotEmpty()) | |||
| paths.addIfNotAlreadyThere (path); | |||
| } | |||
| auto oldPath = exporter->getLegacyModulePath(); | |||
| if (oldPath.isNotEmpty()) | |||
| paths.addIfNotAlreadyThere (oldPath); | |||
| } | |||
| Array<File> files; | |||
| for (auto& path : paths) | |||
| { | |||
| auto f = project.resolveFilename (path); | |||
| if (f.isDirectory()) | |||
| { | |||
| files.addIfNotAlreadyThere (f); | |||
| if (f.getChildFile ("modules").isDirectory()) | |||
| files.addIfNotAlreadyThere (f.getChildFile ("modules")); | |||
| } | |||
| } | |||
| return files; | |||
| } | |||
| AvailableModuleList& Project::getExporterPathsModuleList() | |||
| { | |||
| return *exporterPathsModuleList; | |||
| } | |||
| void Project::rescanExporterPathModules (bool async) | |||
| { | |||
| if (async) | |||
| exporterPathsModuleList->scanPathsAsync (getAllPossibleModulePathsFromExporters (*this)); | |||
| else | |||
| exporterPathsModuleList->scanPaths (getAllPossibleModulePathsFromExporters (*this)); | |||
| } | |||
| ModuleIDAndFolder Project::getModuleWithID (const String& id) | |||
| { | |||
| if (enabledModulesList == nullptr) | |||
| enabledModulesList.reset (new EnabledModuleList (*this, projectRoot.getOrCreateChildWithName (Ids::MODULES, nullptr))); | |||
| const auto& list = (isJUCEModule (id) ? ProjucerApplication::getApp().getJUCEPathModuleList().getAllModules() | |||
| : ProjucerApplication::getApp().getUserPathsModuleList().getAllModules()); | |||
| for (auto& m : list) | |||
| if (m.first == id) | |||
| return m; | |||
| return *enabledModulesList; | |||
| return exporterPathsModuleList->getModuleWithID (id); | |||
| } | |||
| //============================================================================== | |||
| @@ -31,6 +31,7 @@ | |||
| class ProjectExporter; | |||
| class LibraryModule; | |||
| class EnabledModuleList; | |||
| class AvailableModuleList; | |||
| class ProjectContentComponent; | |||
| class CompileEngineSettings; | |||
| @@ -351,7 +352,12 @@ public: | |||
| bool isConfigFlagEnabled (const String& name, bool defaultIsEnabled = false) const; | |||
| //============================================================================== | |||
| EnabledModuleList& getModules(); | |||
| EnabledModuleList& getEnabledModules(); | |||
| AvailableModuleList& getExporterPathsModuleList(); | |||
| void rescanExporterPathModules (bool async = false); | |||
| std::pair<String, File> getModuleWithID (const String&); | |||
| //============================================================================== | |||
| String getFileTemplate (const String& templateName); | |||
| @@ -412,6 +418,8 @@ private: | |||
| //============================================================================== | |||
| std::unique_ptr<CompileEngineSettings> compileEngineSettings; | |||
| std::unique_ptr<EnabledModuleList> enabledModuleList; | |||
| std::unique_ptr<AvailableModuleList> exporterPathsModuleList; | |||
| //============================================================================== | |||
| bool shouldWriteLegacyPluginFormatSettings = false; | |||
| @@ -440,7 +448,6 @@ private: | |||
| //============================================================================== | |||
| friend class Item; | |||
| std::unique_ptr<EnabledModuleList> enabledModulesList; | |||
| bool isSaving = false; | |||
| Time modificationTime; | |||
| StringPairArray parsedPreprocessorDefs; | |||
| @@ -1788,7 +1788,7 @@ private: | |||
| void createOpenGlFeatureElement (XmlElement& manifest) const | |||
| { | |||
| if (project.getModules().isModuleEnabled ("juce_opengl")) | |||
| if (project.getEnabledModules().isModuleEnabled ("juce_opengl")) | |||
| { | |||
| XmlElement* glVersion = nullptr; | |||
| @@ -312,7 +312,7 @@ private: | |||
| static String guiExtrasModule ("juce_gui_extra"); | |||
| if (project.getModules().isModuleEnabled (guiExtrasModule) | |||
| if (project.getEnabledModules().isModuleEnabled (guiExtrasModule) | |||
| && project.isConfigFlagEnabled ("JUCE_WEB_BROWSER", true)) | |||
| { | |||
| result.add ("webkit2gtk-4.0"); | |||
| @@ -608,7 +608,7 @@ private: | |||
| { | |||
| static String guiExtrasModule ("juce_gui_extra"); | |||
| return (project.getModules().isModuleEnabled (guiExtrasModule) | |||
| return (project.getEnabledModules().isModuleEnabled (guiExtrasModule) | |||
| && project.isConfigFlagEnabled ("JUCE_WEB_BROWSER", true)); | |||
| } | |||
| @@ -616,7 +616,7 @@ private: | |||
| { | |||
| static String juceCoreModule ("juce_core"); | |||
| return (project.getModules().isModuleEnabled (juceCoreModule) | |||
| return (project.getEnabledModules().isModuleEnabled (juceCoreModule) | |||
| && project.isConfigFlagEnabled ("JUCE_LOAD_CURL_SYMBOLS_LAZILY", false)); | |||
| } | |||
| @@ -1506,7 +1506,7 @@ public: | |||
| paths.addArray (config.getHeaderSearchPaths()); | |||
| paths.addArray (getTargetExtraHeaderSearchPaths()); | |||
| if (owner.project.getModules().isModuleEnabled ("juce_audio_plugin_client")) | |||
| if (owner.project.getEnabledModules().isModuleEnabled ("juce_audio_plugin_client")) | |||
| { | |||
| // Needed to compile .r files | |||
| paths.add (owner.getModuleFolderRelativeToProject ("juce_audio_plugin_client") | |||
| @@ -544,7 +544,7 @@ String ProjectExporter::getPathForModuleString (const String& moduleID) const | |||
| auto exporterPath = settings.getChildWithName (Ids::MODULEPATHS) | |||
| .getChildWithProperty (Ids::ID, moduleID) [Ids::path].toString(); | |||
| if (exporterPath.isEmpty() || project.getModules().shouldUseGlobalPath (moduleID)) | |||
| if (exporterPath.isEmpty() || project.getEnabledModules().shouldUseGlobalPath (moduleID)) | |||
| { | |||
| auto id = isJUCEModule (moduleID) ? Ids::defaultJuceModulePath | |||
| : Ids::defaultUserModulePath; | |||
| @@ -573,17 +573,17 @@ TargetOS::OS ProjectExporter::getTargetOSForExporter() const | |||
| { | |||
| auto targetOS = TargetOS::unknown; | |||
| if (isWindows()) targetOS = TargetOS::windows; | |||
| else if (isOSX() || isiOS()) targetOS = TargetOS::osx; | |||
| else if (isLinux()) targetOS = TargetOS::linux; | |||
| else if (isAndroid()) targetOS = TargetOS::getThisOS(); | |||
| if (isWindows()) targetOS = TargetOS::windows; | |||
| else if (isOSX() || isiOS()) targetOS = TargetOS::osx; | |||
| else if (isLinux()) targetOS = TargetOS::linux; | |||
| else if (isAndroid() || isCLion()) targetOS = TargetOS::getThisOS(); | |||
| return targetOS; | |||
| } | |||
| RelativePath ProjectExporter::getModuleFolderRelativeToProject (const String& moduleID) const | |||
| { | |||
| if (project.getModules().shouldCopyModuleFilesLocally (moduleID).getValue()) | |||
| if (project.getEnabledModules().shouldCopyModuleFilesLocally (moduleID).getValue()) | |||
| return RelativePath (project.getRelativePathForFile (project.getLocalModuleFolder (moduleID)), | |||
| RelativePath::projectFolder); | |||
| @@ -602,7 +602,7 @@ String ProjectExporter::getLegacyModulePath() const | |||
| RelativePath ProjectExporter::getLegacyModulePath (const String& moduleID) const | |||
| { | |||
| if (project.getModules().state.getChildWithProperty (Ids::ID, moduleID) ["useLocalCopy"]) | |||
| if (project.getEnabledModules().state.getChildWithProperty (Ids::ID, moduleID) ["useLocalCopy"]) | |||
| return RelativePath (project.getRelativePathForFile (project.getGeneratedCodeFolder() | |||
| .getChildFile ("modules") | |||
| .getChildFile (moduleID)), RelativePath::projectFolder); | |||
| @@ -625,9 +625,9 @@ void ProjectExporter::updateOldModulePaths() | |||
| if (oldPath.isNotEmpty()) | |||
| { | |||
| for (int i = project.getModules().getNumModules(); --i >= 0;) | |||
| for (int i = project.getEnabledModules().getNumModules(); --i >= 0;) | |||
| { | |||
| auto modID = project.getModules().getModuleID(i); | |||
| auto modID = project.getEnabledModules().getModuleID(i); | |||
| getPathForModuleValue (modID) = getLegacyModulePath (modID).getParentDirectory().toUnixStyle(); | |||
| } | |||
| @@ -650,9 +650,9 @@ void ProjectExporter::createDefaultModulePaths() | |||
| { | |||
| if (areCompatibleExporters (*this, *exporter)) | |||
| { | |||
| for (int i = project.getModules().getNumModules(); --i >= 0;) | |||
| for (int i = project.getEnabledModules().getNumModules(); --i >= 0;) | |||
| { | |||
| auto modID = project.getModules().getModuleID (i); | |||
| auto modID = project.getEnabledModules().getModuleID (i); | |||
| getPathForModuleValue (modID) = exporter->getPathForModuleValue (modID).getValue(); | |||
| } | |||
| @@ -664,9 +664,9 @@ void ProjectExporter::createDefaultModulePaths() | |||
| { | |||
| if (exporter->canLaunchProject()) | |||
| { | |||
| for (int i = project.getModules().getNumModules(); --i >= 0;) | |||
| for (int i = project.getEnabledModules().getNumModules(); --i >= 0;) | |||
| { | |||
| auto modID = project.getModules().getModuleID (i); | |||
| auto modID = project.getEnabledModules().getModuleID (i); | |||
| getPathForModuleValue (modID) = exporter->getPathForModuleValue (modID).getValue(); | |||
| } | |||
| @@ -674,9 +674,9 @@ void ProjectExporter::createDefaultModulePaths() | |||
| } | |||
| } | |||
| for (int i = project.getModules().getNumModules(); --i >= 0;) | |||
| for (int i = project.getEnabledModules().getNumModules(); --i >= 0;) | |||
| { | |||
| auto modID = project.getModules().getModuleID (i); | |||
| auto modID = project.getEnabledModules().getModuleID (i); | |||
| getPathForModuleValue (modID) = "../../juce"; | |||
| } | |||
| } | |||
| @@ -82,7 +82,7 @@ public: | |||
| project.setFile (projectFile); | |||
| OwnedArray<LibraryModule> modules; | |||
| project.getModules().createRequiredModules (modules); | |||
| project.getEnabledModules().createRequiredModules (modules); | |||
| checkModuleValidity (modules); | |||
| @@ -148,7 +148,7 @@ public: | |||
| Result saveContentNeededForLiveBuild() | |||
| { | |||
| OwnedArray<LibraryModule> modules; | |||
| project.getModules().createRequiredModules (modules); | |||
| project.getEnabledModules().createRequiredModules (modules); | |||
| checkModuleValidity (modules); | |||
| @@ -390,7 +390,7 @@ private: | |||
| return; | |||
| } | |||
| if (project.getModules().getExtraDependenciesNeeded (module->getID()).size() > 0) | |||
| if (project.getEnabledModules().getExtraDependenciesNeeded (module->getID()).size() > 0) | |||
| { | |||
| addError ("At least one of your modules has missing dependencies!\n" | |||
| "Please go to the settings page of the highlighted modules and add the required dependencies."); | |||
| @@ -183,7 +183,7 @@ struct TranslationHelpers | |||
| scanFilesForTranslations (strings, project.getMainGroup()); | |||
| OwnedArray<LibraryModule> modules; | |||
| project.getModules().createRequiredModules (modules); | |||
| project.getEnabledModules().createRequiredModules (modules); | |||
| for (int j = 0; j < modules.size(); ++j) | |||
| { | |||
| @@ -182,14 +182,14 @@ struct NewProjectWizard | |||
| void addDefaultModules (Project& project, bool useGlobalPath) | |||
| { | |||
| StringArray mods (getDefaultModules()); | |||
| auto defaultModules = getDefaultModules(); | |||
| ModuleList list; | |||
| list.addAllModulesInFolder (modulesFolder); | |||
| AvailableModuleList list; | |||
| list.scanPaths ({ modulesFolder }); | |||
| for (int i = 0; i < mods.size(); ++i) | |||
| if (const ModuleDescription* info = list.getModuleWithID (mods[i])) | |||
| project.getModules().addModule (info->moduleFolder, false, useGlobalPath, false); | |||
| for (auto& mod : list.getAllModules()) | |||
| if (defaultModules.contains (mod.first)) | |||
| project.getEnabledModules().addModule (mod.second, false, useGlobalPath, false); | |||
| } | |||
| void addExporters (Project& project, WizardComp& wizardComp) | |||