From ef2d1fa9fd01ff1f3c4e72356a761c576162259c Mon Sep 17 00:00:00 2001 From: ed Date: Thu, 15 Mar 2018 11:15:11 +0000 Subject: [PATCH] Projucer: Various fixes and improvements to PIPGenerator --- .../jucer_GlobalPathsWindowComponent.h | 57 ++++++++++++++++++- .../Source/Application/jucer_Application.cpp | 45 +++++++++++++-- .../Source/Application/jucer_Application.h | 5 +- .../Source/Application/jucer_CommandLine.cpp | 9 +-- .../Application/jucer_ProjucerAnalytics.cpp | 3 + .../Projucer/Source/Project/jucer_Project.cpp | 28 ++++++++- .../Projucer/Source/Project/jucer_Project.h | 1 + .../Source/Settings/jucer_StoredSettings.cpp | 45 ++++++++++++++- .../Source/Settings/jucer_StoredSettings.h | 7 ++- .../Utility/Helpers/jucer_MiscUtilities.cpp | 9 ++- .../Source/Utility/Helpers/jucer_PresetIDs.h | 1 + .../Utility/PIPs/jucer_PIPGenerator.cpp | 12 ++-- 12 files changed, 194 insertions(+), 28 deletions(-) diff --git a/extras/Projucer/Source/Application/Windows/jucer_GlobalPathsWindowComponent.h b/extras/Projucer/Source/Application/Windows/jucer_GlobalPathsWindowComponent.h index 8514dd7a05..a6c9a7c511 100644 --- a/extras/Projucer/Source/Application/Windows/jucer_GlobalPathsWindowComponent.h +++ b/extras/Projucer/Source/Application/Windows/jucer_GlobalPathsWindowComponent.h @@ -28,7 +28,8 @@ //============================================================================== -class GlobalPathsWindowComponent : public Component +class GlobalPathsWindowComponent : public Component, + private Timer { public: GlobalPathsWindowComponent() @@ -66,6 +67,12 @@ public: g.fillAll (findColour (backgroundColourId)); } + void paintOverChildren (Graphics& g) override + { + g.setColour (findColour (defaultHighlightColourId).withAlpha (flashAlpha)); + g.fillRect (boundsToHighlight); + } + void resized() override { auto b = getLocalBounds().reduced (10); @@ -77,6 +84,8 @@ public: info.setBounds (osSelector.getBounds().withWidth (osSelector.getHeight()).translated ((osSelector.getWidth() + 5), 0).reduced (2)); int labelIndex = 0; + bool isFirst = true; + for (auto* pathComp : pathPropertyComponents) { if (pathComp == nullptr) @@ -87,9 +96,28 @@ public: } else { + if (isFirst) + b.removeFromTop (20); + pathComp->setBounds (b.removeFromTop (pathComp->getPreferredHeight())); b.removeFromTop (5); } + + isFirst = false; + } + } + + void highlightJUCEPath() + { + if (! isTimerRunning() && isSelectedOSThisOS()) + { + if (auto* jucePathComp = pathPropertyComponents.getFirst()) + boundsToHighlight = jucePathComp->getBounds(); + + flashAlpha = 0.0f; + hasFlashed = false; + + startTimer (25); } } @@ -100,6 +128,30 @@ private: ComboBox osSelector; InfoButton info; + Rectangle boundsToHighlight; + float flashAlpha = 0.0f; + bool hasFlashed = false; + + //============================================================================== + void timerCallback() override + { + flashAlpha += (hasFlashed ? -0.05f : 0.05f); + + if (flashAlpha > 0.75f) + { + hasFlashed = true; + } + else if (flashAlpha < 0.0f) + { + flashAlpha = 0.0f; + boundsToHighlight = {}; + + stopTimer(); + } + + repaint(); + } + //============================================================================== bool isSelectedOSThisOS() { return TargetOS::getThisOS() == getSelectedOS(); } @@ -126,6 +178,9 @@ private: if (isSelectedOSThisOS()) { + addAndMakeVisible (pathPropertyComponents.add (new FilePathPropertyComponent (settings.getStoredPath (Ids::jucePath), + "Path to JUCE", true))); + pathPropertyComponents.add (nullptr); addAndMakeVisible (pathPropertyComponents.add (new FilePathPropertyComponent (settings.getStoredPath (Ids::defaultJuceModulePath), diff --git a/extras/Projucer/Source/Application/jucer_Application.cpp b/extras/Projucer/Source/Application/jucer_Application.cpp index ed1e614e90..59a0e9b39a 100644 --- a/extras/Projucer/Source/Application/jucer_Application.cpp +++ b/extras/Projucer/Source/Application/jucer_Application.cpp @@ -177,6 +177,9 @@ void ProjucerApplication::handleAsyncUpdate() setAnalyticsEnabled (licenseController->getState().applicationUsageDataState == LicenseState::ApplicationUsageData::enabled); Analytics::getInstance()->logEvent ("Startup", {}, ProjucerAnalyticsEvent::appEvent); } + + if (! isRunningCommandLine && settings->shouldAskUserToSetJUCEPath()) + showSetJUCEPathAlert(); } void ProjucerApplication::initialiseWindows (const String& commandLine) @@ -345,7 +348,8 @@ enum activeDocumentsBaseID = 400, colourSchemeBaseID = 1000, codeEditorColourSchemeBaseID = 1500, - examplesBaseID = 2000, + showPathsID = 1999, + examplesBaseID = 2000 }; MenuBarModel* ProjucerApplication::getMenuModel() @@ -592,7 +596,7 @@ void ProjucerApplication::createExamplesPopupMenu (PopupMenu& menu) noexcept if (numExamples == 0) { - menu.addCommandItem (commandManager, CommandIDs::showGlobalPathsWindow, "Set path to JUCE..."); + menu.addItem (showPathsID, "Set path to JUCE..."); } else { @@ -615,7 +619,7 @@ Array ProjucerApplication::getSortedExampleDirectories() const noexcept { auto exampleDirectory = iter.getFile(); - if (exampleDirectory.getFileName() != "DemoRunner" && exampleDirectory.getFileName() != "Resources") + if (exampleDirectory.getFileName() != "DemoRunner" && exampleDirectory.getFileName() != "Assets") exampleDirectories.add (exampleDirectory); } @@ -860,6 +864,10 @@ void ProjucerApplication::handleMainMenuCommand (int menuItemID) { showEditorColourSchemeWindow(); } + else if (menuItemID == showPathsID) + { + showPathsWindow (true); + } else if (menuItemID >= examplesBaseID && menuItemID < (examplesBaseID + numExamples)) { findAndLaunchExample (menuItemID - examplesBaseID); @@ -1028,7 +1036,7 @@ bool ProjucerApplication::perform (const InvocationInfo& info) case CommandIDs::clearRecentFiles: clearRecentFiles(); break; case CommandIDs::showUTF8Tool: showUTF8ToolWindow(); break; case CommandIDs::showSVGPathTool: showSVGPathDataToolWindow(); break; - case CommandIDs::showGlobalPathsWindow: showPathsWindow(); break; + case CommandIDs::showGlobalPathsWindow: showPathsWindow (false); break; case CommandIDs::showAboutWindow: showAboutWindow(); break; case CommandIDs::showAppUsageWindow: showApplicationUsageDataAgreementPopup(); break; case CommandIDs::showForum: launchForumBrowser(); break; @@ -1166,7 +1174,7 @@ void ProjucerApplication::dismissApplicationUsageDataAgreementPopup() applicationUsageDataWindow.reset(); } -void ProjucerApplication::showPathsWindow() +void ProjucerApplication::showPathsWindow (bool highlightJUCEPath) { if (pathsWindow != nullptr) pathsWindow->toFront (true); @@ -1174,7 +1182,11 @@ void ProjucerApplication::showPathsWindow() new FloatingToolWindow ("Global Paths", "pathsWindowPos", new GlobalPathsWindowComponent(), pathsWindow, false, - 600, 550, 600, 550, 600, 550); + 600, 650, 600, 650, 600, 650); + + if (highlightJUCEPath) + if (auto* pathsComp = dynamic_cast (pathsWindow->getChildComponent (0))) + pathsComp->highlightJUCEPath(); } void ProjucerApplication::showEditorColourSchemeWindow() @@ -1345,6 +1357,27 @@ void ProjucerApplication::setupAnalytics() Analytics::getInstance()->setUserProperties (userData); } +void ProjucerApplication::showSetJUCEPathAlert() +{ + auto& lf = Desktop::getInstance().getDefaultLookAndFeel(); + pathAlert = lf.createAlertWindow ("Set JUCE Path", "Your global JUCE path is invalid. This path is used to access the JUCE examples and demo project - " + "would you like to set it now?", + "Set path", "Cancel", "Don't ask again", + AlertWindow::WarningIcon, 3, + mainWindowList.getFrontmostWindow (false)); + + pathAlert->enterModalState (true, ModalCallbackFunction::create ([this] (int retVal) + { + pathAlert.reset (nullptr); + + if (retVal == 1) + showPathsWindow (true); + else if (retVal == 0) + settings->setDontAskAboutJUCEPathAgain(); + })); + +} + void ProjucerApplication::selectEditorColourSchemeWithName (const String& schemeName) { auto& appearanceSettings = getAppSettings().appearance; diff --git a/extras/Projucer/Source/Application/jucer_Application.h b/extras/Projucer/Source/Application/jucer_Application.h index 7df6fad6bb..a9ec37b808 100644 --- a/extras/Projucer/Source/Application/jucer_Application.h +++ b/extras/Projucer/Source/Application/jucer_Application.h @@ -108,7 +108,7 @@ public: void showApplicationUsageDataAgreementPopup(); void dismissApplicationUsageDataAgreementPopup(); - void showPathsWindow(); + void showPathsWindow (bool highlightJUCEPath = false); void showEditorColourSchemeWindow(); void launchForumBrowser(); @@ -194,6 +194,9 @@ private: void resetAnalytics() noexcept; void setupAnalytics(); + void showSetJUCEPathAlert(); + ScopedPointer pathAlert; + //============================================================================== void setColourScheme (int index, bool saveSetting); diff --git a/extras/Projucer/Source/Application/jucer_CommandLine.cpp b/extras/Projucer/Source/Application/jucer_CommandLine.cpp index 74e71dcdbb..08796204d0 100644 --- a/extras/Projucer/Source/Application/jucer_CommandLine.cpp +++ b/extras/Projucer/Source/Application/jucer_CommandLine.cpp @@ -717,10 +717,6 @@ namespace #endif auto settingsFile = userAppData.getChildFile ("Projucer").getChildFile ("Projucer.settings"); - - if (! settingsFile.existsAsFile()) - throw CommandLineError ("Expected settings file at " + settingsFile.getFullPathName() + " not found!"); - ScopedPointer xml (XmlDocument::parse (settingsFile)); auto settingsTree = ValueTree::fromXml (*xml); @@ -743,15 +739,14 @@ namespace if (! childToSet.isValid()) throw CommandLineError ("Failed to set the requested setting!"); - childToSet.setProperty (args[2], args[3], nullptr); + childToSet.setProperty (args[2], File::getCurrentWorkingDirectory().getChildFile (args[3]).getFullPathName(), nullptr); settingsFile.replaceWithText (settingsTree.toXmlString()); } static void createProjectFromPIP (const StringArray& args) { - if (args.size() < 3) - throw CommandLineError ("Not enough arguments. Usage: --create-project-from-pip path/to/PIP path/to/output."); + checkArgumentCount (args, 3); auto pipFile = File::getCurrentWorkingDirectory().getChildFile (args[1].unquoted()); if (! pipFile.existsAsFile()) diff --git a/extras/Projucer/Source/Application/jucer_ProjucerAnalytics.cpp b/extras/Projucer/Source/Application/jucer_ProjucerAnalytics.cpp index 4ae958f761..681a79e5ea 100644 --- a/extras/Projucer/Source/Application/jucer_ProjucerAnalytics.cpp +++ b/extras/Projucer/Source/Application/jucer_ProjucerAnalytics.cpp @@ -37,6 +37,9 @@ ProjucerAnalyticsDestination::ProjucerAnalyticsDestination() } auto dataDir = File::getSpecialLocation (File::userApplicationDataDirectory) + #if JUCE_MAC + .getChildFile ("Application Support") + #endif .getChildFile ("Projucer") .getChildFile ("Analytics"); diff --git a/extras/Projucer/Source/Project/jucer_Project.cpp b/extras/Projucer/Source/Project/jucer_Project.cpp index b9ef72f66e..ef50af9250 100644 --- a/extras/Projucer/Source/Project/jucer_Project.cpp +++ b/extras/Projucer/Source/Project/jucer_Project.cpp @@ -491,13 +491,39 @@ void Project::moveTemporaryDirectory (const File& newParentDirectory) auto newDirectory = newParentDirectory.getChildFile (tempDirectory.getFileName()); auto oldJucerFileName = getFile().getFileName(); + saveProjectRootToFile(); + tempDirectory.copyDirectoryTo (newDirectory); tempDirectory.deleteRecursively(); tempDirectory = File(); // reload project from new location if (auto* window = ProjucerApplication::getApp().mainWindowList.getMainWindowForFile (getFile())) - window->moveProject (newDirectory.getChildFile (oldJucerFileName)); + { + Component::SafePointer safeWindow (window); + + MessageManager::callAsync ([safeWindow, newDirectory, oldJucerFileName] + { + if (safeWindow != nullptr) + safeWindow.getComponent()->moveProject (newDirectory.getChildFile (oldJucerFileName)); + }); + } +} + +bool Project::saveProjectRootToFile() +{ + ScopedPointer xml (projectRoot.createXml()); + + if (xml == nullptr) + { + jassertfalse; + return false; + } + + MemoryOutputStream mo; + xml->writeToStream (mo, {}); + + return FileHelpers::overwriteFileWithNewDataIfDifferent (getFile(), mo); } //============================================================================== diff --git a/extras/Projucer/Source/Project/jucer_Project.h b/extras/Projucer/Source/Project/jucer_Project.h index caddd7c238..d5086a9ce6 100644 --- a/extras/Projucer/Source/Project/jucer_Project.h +++ b/extras/Projucer/Source/Project/jucer_Project.h @@ -367,6 +367,7 @@ private: void askUserWhereToSaveProject(); void moveTemporaryDirectory (const File&); + bool saveProjectRootToFile(); //============================================================================== bool hasSentGUIBuilderAnalyticsEvent = false; diff --git a/extras/Projucer/Source/Settings/jucer_StoredSettings.cpp b/extras/Projucer/Source/Settings/jucer_StoredSettings.cpp index 6d4c9a2f8c..adc74d7f04 100644 --- a/extras/Projucer/Source/Settings/jucer_StoredSettings.cpp +++ b/extras/Projucer/Source/Settings/jucer_StoredSettings.cpp @@ -47,6 +47,8 @@ StoredSettings::StoredSettings() { updateOldProjectSettingsFiles(); reload(); + checkJUCEPaths(); + projectDefaults.addListener (this); fallbackPaths.addListener (this); } @@ -195,6 +197,34 @@ void StoredSettings::updateOldProjectSettingsFiles() } } +void StoredSettings::checkJUCEPaths() +{ + auto moduleFolder = projectDefaults.getProperty (Ids::defaultJuceModulePath).toString(); + auto juceFolder = projectDefaults.getProperty (Ids::jucePath).toString(); + + auto validModuleFolder = isGlobalPathValid ({}, Ids::defaultJuceModulePath, moduleFolder); + auto validJuceFolder = isGlobalPathValid ({}, Ids::jucePath, juceFolder); + + if (validModuleFolder && ! validJuceFolder) + projectDefaults.getPropertyAsValue (Ids::jucePath, nullptr) = File (moduleFolder).getParentDirectory().getFullPathName(); + else if (! validModuleFolder && validJuceFolder) + projectDefaults.getPropertyAsValue (Ids::defaultJuceModulePath, nullptr) = File (juceFolder).getChildFile ("modules").getFullPathName(); +} + +bool StoredSettings::shouldAskUserToSetJUCEPath() noexcept +{ + if (! isGlobalPathValid ({}, Ids::jucePath, projectDefaults.getProperty (Ids::jucePath).toString()) + && getGlobalProperties().getValue ("dontAskAboutJUCEPath", {}).isEmpty()) + return true; + + return false; +} + +void StoredSettings::setDontAskAboutJUCEPathAgain() noexcept +{ + getGlobalProperties().setValue ("dontAskAboutJUCEPath", 1); +} + //============================================================================== void StoredSettings::loadSwatchColours() { @@ -271,7 +301,12 @@ Value StoredSettings::getFallbackPathForOS (const Identifier& key, DependencyPat if (v.toString().isEmpty()) { - if (key == Ids::defaultJuceModulePath) + if (key == Ids::jucePath) + { + v = (os == TargetOS::windows ? "C:\\JUCE" + : "~/JUCE"); + } + else if (key == Ids::defaultJuceModulePath) { v = (os == TargetOS::windows ? "C:\\JUCE\\modules" : "~/JUCE/modules"); @@ -334,13 +369,13 @@ Value StoredSettings::getFallbackPathForOS (const Identifier& key, DependencyPat return v; } -static bool doesSDKPathContainFile (const File& relativeTo, const String& path, const String& fileToCheckFor) +static bool doesSDKPathContainFile (const File& relativeTo, const String& path, const String& fileToCheckFor) noexcept { auto actualPath = path.replace ("${user.home}", File::getSpecialLocation (File::userHomeDirectory).getFullPathName()); return relativeTo.getChildFile (actualPath + "/" + fileToCheckFor).exists(); } -bool StoredSettings::isGlobalPathValid (const File& relativeTo, const Identifier& key, const String& path) +bool StoredSettings::isGlobalPathValid (const File& relativeTo, const Identifier& key, const String& path) const noexcept { String fileToCheckFor; @@ -390,6 +425,10 @@ bool StoredSettings::isGlobalPathValid (const File& relativeTo, const Identifier fileToCheckFor = "../clion.sh"; #endif } + else if (key == Ids::jucePath) + { + fileToCheckFor = "ChangeList.txt"; + } else { // didn't recognise the key provided! diff --git a/extras/Projucer/Source/Settings/jucer_StoredSettings.h b/extras/Projucer/Source/Settings/jucer_StoredSettings.h index 3f20928e53..af34a63084 100644 --- a/extras/Projucer/Source/Settings/jucer_StoredSettings.h +++ b/extras/Projucer/Source/Settings/jucer_StoredSettings.h @@ -70,7 +70,11 @@ public: Value getStoredPath (const Identifier& key); Value getFallbackPathForOS (const Identifier& key, DependencyPathOS); - bool isGlobalPathValid (const File& relativeTo, const Identifier& key, const String& path); + bool isGlobalPathValid (const File& relativeTo, const Identifier& key, const String& path) const noexcept; + + //============================================================================== + bool shouldAskUserToSetJUCEPath() noexcept; + void setDontAskAboutJUCEPathAgain() noexcept; private: OwnedArray propertyFiles; @@ -96,6 +100,7 @@ private: void saveSwatchColours(); void updateOldProjectSettingsFiles(); + void checkJUCEPaths(); //============================================================================== void valueTreePropertyChanged (ValueTree& vt, const Identifier&) override { changed (vt == projectDefaults); } diff --git a/extras/Projucer/Source/Utility/Helpers/jucer_MiscUtilities.cpp b/extras/Projucer/Source/Utility/Helpers/jucer_MiscUtilities.cpp index 225e604096..73c14677e8 100644 --- a/extras/Projucer/Source/Utility/Helpers/jucer_MiscUtilities.cpp +++ b/extras/Projucer/Source/Utility/Helpers/jucer_MiscUtilities.cpp @@ -405,7 +405,12 @@ bool isPIPFile (const File& file) noexcept File getJUCEExamplesDirectoryPathFromGlobal() noexcept { - return File (getAppSettings().getStoredPath (Ids::defaultJuceModulePath).toString()).getSiblingFile ("examples"); + auto globalPath = getAppSettings().getStoredPath (Ids::jucePath).toString(); + + if (globalPath.isNotEmpty()) + return File (getAppSettings().getStoredPath (Ids::jucePath).toString()).getChildFile ("examples"); + + return {}; } bool isValidJUCEExamplesDirectory (const File& directory) noexcept @@ -413,5 +418,5 @@ bool isValidJUCEExamplesDirectory (const File& directory) noexcept if (! directory.exists() || ! directory.isDirectory() || ! directory.containsSubDirectories()) return false; - return directory.getChildFile ("Resources").getChildFile ("juce_icon.png").existsAsFile(); + return directory.getChildFile ("Assets").getChildFile ("juce_icon.png").existsAsFile(); } diff --git a/extras/Projucer/Source/Utility/Helpers/jucer_PresetIDs.h b/extras/Projucer/Source/Utility/Helpers/jucer_PresetIDs.h index d3444f8e65..deeb2012e1 100644 --- a/extras/Projucer/Source/Utility/Helpers/jucer_PresetIDs.h +++ b/extras/Projucer/Source/Utility/Helpers/jucer_PresetIDs.h @@ -65,6 +65,7 @@ namespace Ids DECLARE_ID (osxFallback); DECLARE_ID (windowsFallback); DECLARE_ID (linuxFallback); + DECLARE_ID (jucePath); DECLARE_ID (defaultJuceModulePath); DECLARE_ID (defaultUserModulePath); DECLARE_ID (vst3Folder); diff --git a/extras/Projucer/Source/Utility/PIPs/jucer_PIPGenerator.cpp b/extras/Projucer/Source/Utility/PIPs/jucer_PIPGenerator.cpp index 797375e951..2d45b1297b 100644 --- a/extras/Projucer/Source/Utility/PIPs/jucer_PIPGenerator.cpp +++ b/extras/Projucer/Source/Utility/PIPs/jucer_PIPGenerator.cpp @@ -256,15 +256,15 @@ void PIPGenerator::createFiles (ValueTree& jucerTree) if (relativeFiles.size() > 0) { - ValueTree resources (Ids::GROUP); - resources.setProperty (Ids::ID, createAlphaNumericUID(), nullptr); - resources.setProperty (Ids::name, "Resources", nullptr); + ValueTree assets (Ids::GROUP); + assets.setProperty (Ids::ID, createAlphaNumericUID(), nullptr); + assets.setProperty (Ids::name, "Assets", nullptr); for (auto& f : relativeFiles) if (copyRelativeFileToLocalSourceDirectory (f)) - addFileToTree (resources, f.getFileName(), f.getFileExtension() == ".cpp", "Source/" + f.getFileName()); + addFileToTree (assets, f.getFileName(), f.getFileExtension() == ".cpp", "Source/" + f.getFileName()); - mainGroup.addChild (resources, -1, nullptr); + mainGroup.addChild (assets, -1, nullptr); } } @@ -400,7 +400,7 @@ Result PIPGenerator::setProjectSettings (ValueTree& jucerTree) if (isValidJUCEExamplesDirectory (examplesDirectory)) { defines += ((defines.isEmpty() ? "" : " ") + String ("PIP_JUCE_EXAMPLES_DIRECTORY=") - + examplesDirectory.getFullPathName()); + + Base64::toBase64 (examplesDirectory.getFullPathName())); } else {