diff --git a/extras/Projucer/Builds/LinuxMakefile/Makefile b/extras/Projucer/Builds/LinuxMakefile/Makefile index a282a48029..2026e23824 100644 --- a/extras/Projucer/Builds/LinuxMakefile/Makefile +++ b/extras/Projucer/Builds/LinuxMakefile/Makefile @@ -71,7 +71,6 @@ OBJECTS_APP := \ $(JUCE_OBJDIR)/jucer_CommandLine_f35de107.o \ $(JUCE_OBJDIR)/jucer_DocumentEditorComponent_695dff1d.o \ $(JUCE_OBJDIR)/jucer_DownloadCompileEngineThread_8a38703f.o \ - $(JUCE_OBJDIR)/jucer_GlobalPreferences_b0f1bd3d.o \ $(JUCE_OBJDIR)/jucer_Main_f8488f5b.o \ $(JUCE_OBJDIR)/jucer_MainWindow_1e163aeb.o \ $(JUCE_OBJDIR)/jucer_OpenDocumentManager_4c72d210.o \ @@ -126,6 +125,8 @@ OBJECTS_APP := \ .PHONY: clean all +all : $(JUCE_OUTDIR)/$(JUCE_TARGET_APP) + $(JUCE_OUTDIR)/$(JUCE_TARGET_APP) : check-pkg-config $(OBJECTS_APP) $(RESOURCES) @echo Linking "Projucer - App" -$(V_AT)mkdir -p $(JUCE_BINDIR) @@ -158,11 +159,6 @@ $(JUCE_OBJDIR)/jucer_DownloadCompileEngineThread_8a38703f.o: ../../Source/Applic @echo "Compiling jucer_DownloadCompileEngineThread.cpp" $(V_AT)$(CXX) $(JUCE_CXXFLAGS) $(JUCE_CPPFLAGS_APP) $(JUCE_CFLAGS_APP) -o "$@" -c "$<" -$(JUCE_OBJDIR)/jucer_GlobalPreferences_b0f1bd3d.o: ../../Source/Application/jucer_GlobalPreferences.cpp - -$(V_AT)mkdir -p $(JUCE_OBJDIR) - @echo "Compiling jucer_GlobalPreferences.cpp" - $(V_AT)$(CXX) $(JUCE_CXXFLAGS) $(JUCE_CPPFLAGS_APP) $(JUCE_CFLAGS_APP) -o "$@" -c "$<" - $(JUCE_OBJDIR)/jucer_Main_f8488f5b.o: ../../Source/Application/jucer_Main.cpp -$(V_AT)mkdir -p $(JUCE_OBJDIR) @echo "Compiling jucer_Main.cpp" diff --git a/extras/Projucer/Builds/MacOSX/Projucer.xcodeproj/project.pbxproj b/extras/Projucer/Builds/MacOSX/Projucer.xcodeproj/project.pbxproj index e8970d656f..82ced36bc0 100644 --- a/extras/Projucer/Builds/MacOSX/Projucer.xcodeproj/project.pbxproj +++ b/extras/Projucer/Builds/MacOSX/Projucer.xcodeproj/project.pbxproj @@ -29,7 +29,6 @@ 954A036F5DDB375DB23FFB3E = {isa = PBXBuildFile; fileRef = 0400CB0E056A1D840304D2DE; }; 3EB3D569250C4BA4CA9AF578 = {isa = PBXBuildFile; fileRef = C7608A3967D9AB9481848F2B; }; 636D21BF846031A6A1A7476A = {isa = PBXBuildFile; fileRef = 11EB44786085029106099D01; }; - 3C3B0ED6C43FDA3AF76DEE2E = {isa = PBXBuildFile; fileRef = 84DE44680C9D37CDDCD127FF; }; 95B44E6C74B1DED31DBE37EB = {isa = PBXBuildFile; fileRef = 8C52A3DDA62A746AA7A68535; }; AA9D0B8E23F3D87A23DE9F8A = {isa = PBXBuildFile; fileRef = 9069981E414A631B036CC9AC; }; 244BA1BDA5FAA465EA3F9C6D = {isa = PBXBuildFile; fileRef = 2247EE920DF0610CAF9F4513; }; @@ -115,7 +114,6 @@ 1B9B5A37F079FE3B3CF8FAB6 = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; name = "include_juce_graphics.mm"; path = "../../JuceLibraryCode/include_juce_graphics.mm"; sourceTree = "SOURCE_ROOT"; }; 1C216FE9B7A5209C5CCF2517 = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = "jucer_PaintElement.cpp"; path = "../../Source/ComponentEditor/paintelements/jucer_PaintElement.cpp"; sourceTree = "SOURCE_ROOT"; }; 1C73D7591E63E8018E279716 = {isa = PBXFileReference; lastKnownFileType = file.svg; name = "export_android.svg"; path = "../../Source/BinaryData/export_android.svg"; sourceTree = "SOURCE_ROOT"; }; - 1C81C5501BE7F2C912250711 = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "jucer_GlobalPreferences.h"; path = "../../Source/Application/jucer_GlobalPreferences.h"; sourceTree = "SOURCE_ROOT"; }; 1D3D6A19A60F0B03DE2F1C14 = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "jucer_PaintElementPath.h"; path = "../../Source/ComponentEditor/paintelements/jucer_PaintElementPath.h"; sourceTree = "SOURCE_ROOT"; }; 1D99EA99F946D665FE583414 = {isa = PBXFileReference; lastKnownFileType = file.svg; name = "wizard_Highlight.svg"; path = "../../Source/BinaryData/wizard_Highlight.svg"; sourceTree = "SOURCE_ROOT"; }; 1DE5BBC777FB64798D823002 = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; name = "include_juce_data_structures.mm"; path = "../../JuceLibraryCode/include_juce_data_structures.mm"; sourceTree = "SOURCE_ROOT"; }; @@ -165,6 +163,7 @@ 472F9A90F685220D730EBF6C = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = BinaryData.cpp; path = ../../JuceLibraryCode/BinaryData.cpp; sourceTree = "SOURCE_ROOT"; }; 47B49049B85EED74D29C9906 = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "jucer_ProjectTree_File.h"; path = "../../Source/Project/jucer_ProjectTree_File.h"; sourceTree = "SOURCE_ROOT"; }; 47DD50A5A9091F9900E0EAD9 = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = "jucer_JucerTreeViewBase.cpp"; path = "../../Source/Utility/jucer_JucerTreeViewBase.cpp"; sourceTree = "SOURCE_ROOT"; }; + 4856DA6706D794E2D89DAB63 = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "jucer_GlobalSearchPathsWindowComponent.h"; path = "../../Source/Utility/jucer_GlobalSearchPathsWindowComponent.h"; sourceTree = "SOURCE_ROOT"; }; 4A035FB6A8D93BF154C08C3F = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "projucer_BuildTabStatusComp.h"; path = "../../Source/LiveBuildEngine/projucer_BuildTabStatusComp.h"; sourceTree = "SOURCE_ROOT"; }; 4A41FD3066D0979DB48691E5 = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "jucer_MiscUtilities.h"; path = "../../Source/Utility/jucer_MiscUtilities.h"; sourceTree = "SOURCE_ROOT"; }; 4A4EBDAD8D098F72CE053235 = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "jucer_ProjectWizard_AudioPlugin.h"; path = "../../Source/Wizards/jucer_ProjectWizard_AudioPlugin.h"; sourceTree = "SOURCE_ROOT"; }; @@ -233,7 +232,6 @@ 8138A55052E9FC27284B74DD = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "jucer_FontPropertyComponent.h"; path = "../../Source/ComponentEditor/properties/jucer_FontPropertyComponent.h"; sourceTree = "SOURCE_ROOT"; }; 820291543BF93243B718F0EE = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "jucer_JucerTreeViewBase.h"; path = "../../Source/Utility/jucer_JucerTreeViewBase.h"; sourceTree = "SOURCE_ROOT"; }; 842427CFE565F3FCE5B99174 = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = DiscRecording.framework; path = System/Library/Frameworks/DiscRecording.framework; sourceTree = SDKROOT; }; - 84DE44680C9D37CDDCD127FF = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = "jucer_GlobalPreferences.cpp"; path = "../../Source/Application/jucer_GlobalPreferences.cpp"; sourceTree = "SOURCE_ROOT"; }; 86E468DE6556BB2AD76A3D80 = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = "jucer_ProjectContentComponent.cpp"; path = "../../Source/Project/jucer_ProjectContentComponent.cpp"; sourceTree = "SOURCE_ROOT"; }; 86E8A40E5A83781A8478454D = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = "jucer_MainTemplate_Window.cpp"; path = "../../Source/BinaryData/jucer_MainTemplate_Window.cpp"; sourceTree = "SOURCE_ROOT"; }; 8702F43110E4CCA5E5F827F5 = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = AppConfig.h; path = ../../JuceLibraryCode/AppConfig.h; sourceTree = "SOURCE_ROOT"; }; @@ -314,6 +312,7 @@ C00793A0D4A59BDADC62EEF7 = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "projucer_CompileEngineClient.h"; path = "../../Source/LiveBuildEngine/projucer_CompileEngineClient.h"; sourceTree = "SOURCE_ROOT"; }; C094F3B6A65A79A6DF87C9C2 = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "jucer_PaintElementGroup.h"; path = "../../Source/ComponentEditor/paintelements/jucer_PaintElementGroup.h"; sourceTree = "SOURCE_ROOT"; }; C09BBB58CA45B66D693E8C31 = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "jucer_TemplateThumbnailsComponent.h"; path = "../../Source/Wizards/jucer_TemplateThumbnailsComponent.h"; sourceTree = "SOURCE_ROOT"; }; + C15220EDA1A8315DA3255E87 = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "jucer_EditorColourSchemeWindowComponent.h"; path = "../../Source/Utility/jucer_EditorColourSchemeWindowComponent.h"; sourceTree = "SOURCE_ROOT"; }; C187718F7B9EBA88584B43F3 = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = "jucer_PaintRoutine.cpp"; path = "../../Source/ComponentEditor/jucer_PaintRoutine.cpp"; sourceTree = "SOURCE_ROOT"; }; C22791DB75870C4F102AA8A3 = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "jucer_SlidingPanelComponent.h"; path = "../../Source/Utility/jucer_SlidingPanelComponent.h"; sourceTree = "SOURCE_ROOT"; }; C2990A8D054BC230E7C637C3 = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "jucer_NewProjectWizardClasses.h"; path = "../../Source/Wizards/jucer_NewProjectWizardClasses.h"; sourceTree = "SOURCE_ROOT"; }; @@ -396,8 +395,6 @@ 11EB44786085029106099D01, B7307A82D9EB1EDBA91EE43D, D526C38D581425949BA0E4AC, - 84DE44680C9D37CDDCD127FF, - 1C81C5501BE7F2C912250711, F03E2BDD36E6F4F53AB767A8, 8C52A3DDA62A746AA7A68535, 9069981E414A631B036CC9AC, @@ -569,10 +566,12 @@ 914ADDB50ED7365F08BA91F9, DF78EF6242D82F912534A277, 188D03A4247F4BC0539F5C49, + C15220EDA1A8315DA3255E87, 1729AEDC34001C31B8CC357C, 553725A0E3A391651ED1731E, 35A36102EAD2D2620EE99E7E, E382C78A1D837DD98916E86A, + 4856DA6706D794E2D89DAB63, CF21D9DB3AEC0A4DCAB36A99, 515FF6E74826E3E3F7273621, 47DD50A5A9091F9900E0EAD9, @@ -839,7 +838,6 @@ 954A036F5DDB375DB23FFB3E, 3EB3D569250C4BA4CA9AF578, 636D21BF846031A6A1A7476A, - 3C3B0ED6C43FDA3AF76DEE2E, 95B44E6C74B1DED31DBE37EB, AA9D0B8E23F3D87A23DE9F8A, 244BA1BDA5FAA465EA3F9C6D, diff --git a/extras/Projucer/Builds/VisualStudio2013/Projucer_App.vcxproj b/extras/Projucer/Builds/VisualStudio2013/Projucer_App.vcxproj index a483f0f93d..84805596e6 100644 --- a/extras/Projucer/Builds/VisualStudio2013/Projucer_App.vcxproj +++ b/extras/Projucer/Builds/VisualStudio2013/Projucer_App.vcxproj @@ -16,6 +16,7 @@ {E4CFCE31-1AF5-C360-751D-9682E333BE4D} v120 + 10.0.14393.0 false v120 v120 + 10.0.14393.0 @@ -32,6 +34,7 @@ true v120 v120 + 10.0.14393.0 @@ -42,6 +45,7 @@ v120 + 10.0.14393.0 <_ProjectFileVersion>10.0.30319.1 @@ -55,6 +59,7 @@ Projucer true v120 + 10.0.14393.0 @@ -146,7 +151,6 @@ - @@ -1376,7 +1380,6 @@ - @@ -1496,9 +1499,11 @@ + + diff --git a/extras/Projucer/Builds/VisualStudio2013/Projucer_App.vcxproj.filters b/extras/Projucer/Builds/VisualStudio2013/Projucer_App.vcxproj.filters index 93c1efae21..23c4138f6e 100644 --- a/extras/Projucer/Builds/VisualStudio2013/Projucer_App.vcxproj.filters +++ b/extras/Projucer/Builds/VisualStudio2013/Projucer_App.vcxproj.filters @@ -286,9 +286,6 @@ Projucer\Application - - Projucer\Application - Projucer\Application @@ -1719,9 +1716,6 @@ Projucer\Application - - Projucer\Application - Projucer\Application @@ -2079,6 +2073,9 @@ Projucer\Utility + + Projucer\Utility + Projucer\Utility @@ -2088,6 +2085,9 @@ Projucer\Utility + + Projucer\Utility + Projucer\Utility diff --git a/extras/Projucer/Builds/VisualStudio2015/Projucer_App.vcxproj b/extras/Projucer/Builds/VisualStudio2015/Projucer_App.vcxproj index 27eeee228e..6ab5cda091 100644 --- a/extras/Projucer/Builds/VisualStudio2015/Projucer_App.vcxproj +++ b/extras/Projucer/Builds/VisualStudio2015/Projucer_App.vcxproj @@ -16,6 +16,7 @@ {E4CFCE31-1AF5-C360-751D-9682E333BE4D} v140 + 10.0.14393.0 false v140 v140 + 10.0.14393.0 @@ -32,6 +34,7 @@ true v140 v140 + 10.0.14393.0 @@ -42,6 +45,7 @@ v140 + 10.0.14393.0 <_ProjectFileVersion>10.0.30319.1 @@ -55,6 +59,7 @@ Projucer true v140 + 10.0.14393.0 @@ -146,7 +151,6 @@ - @@ -1376,7 +1380,6 @@ - @@ -1496,9 +1499,11 @@ + + diff --git a/extras/Projucer/Builds/VisualStudio2015/Projucer_App.vcxproj.filters b/extras/Projucer/Builds/VisualStudio2015/Projucer_App.vcxproj.filters index 3dc4f36a97..f0e3759050 100644 --- a/extras/Projucer/Builds/VisualStudio2015/Projucer_App.vcxproj.filters +++ b/extras/Projucer/Builds/VisualStudio2015/Projucer_App.vcxproj.filters @@ -286,9 +286,6 @@ Projucer\Application - - Projucer\Application - Projucer\Application @@ -1719,9 +1716,6 @@ Projucer\Application - - Projucer\Application - Projucer\Application @@ -2079,6 +2073,9 @@ Projucer\Utility + + Projucer\Utility + Projucer\Utility @@ -2088,6 +2085,9 @@ Projucer\Utility + + Projucer\Utility + Projucer\Utility diff --git a/extras/Projucer/Builds/VisualStudio2017/Projucer_App.vcxproj b/extras/Projucer/Builds/VisualStudio2017/Projucer_App.vcxproj index a375503921..2d061f1949 100644 --- a/extras/Projucer/Builds/VisualStudio2017/Projucer_App.vcxproj +++ b/extras/Projucer/Builds/VisualStudio2017/Projucer_App.vcxproj @@ -16,6 +16,7 @@ {E4CFCE31-1AF5-C360-751D-9682E333BE4D} v141 + 10.0.14393.0 false v141 v141 + 10.0.14393.0 @@ -32,6 +34,7 @@ true v141 v141 + 10.0.14393.0 @@ -42,6 +45,7 @@ v141 + 10.0.14393.0 <_ProjectFileVersion>10.0.30319.1 @@ -55,6 +59,7 @@ Projucer true v141 + 10.0.14393.0 @@ -146,7 +151,6 @@ - @@ -1376,7 +1380,6 @@ - @@ -1496,9 +1499,11 @@ + + diff --git a/extras/Projucer/Builds/VisualStudio2017/Projucer_App.vcxproj.filters b/extras/Projucer/Builds/VisualStudio2017/Projucer_App.vcxproj.filters index 3f85ff3d3e..351952b98a 100644 --- a/extras/Projucer/Builds/VisualStudio2017/Projucer_App.vcxproj.filters +++ b/extras/Projucer/Builds/VisualStudio2017/Projucer_App.vcxproj.filters @@ -286,9 +286,6 @@ Projucer\Application - - Projucer\Application - Projucer\Application @@ -1719,9 +1716,6 @@ Projucer\Application - - Projucer\Application - Projucer\Application @@ -2079,6 +2073,9 @@ Projucer\Utility + + Projucer\Utility + Projucer\Utility @@ -2088,6 +2085,9 @@ Projucer\Utility + + Projucer\Utility + Projucer\Utility diff --git a/extras/Projucer/JuceLibraryCode/AppConfig.h b/extras/Projucer/JuceLibraryCode/AppConfig.h index 6c4c3718ac..13c2efc550 100644 --- a/extras/Projucer/JuceLibraryCode/AppConfig.h +++ b/extras/Projucer/JuceLibraryCode/AppConfig.h @@ -74,7 +74,7 @@ // juce_core flags: #ifndef JUCE_FORCE_DEBUG - //#define JUCE_FORCE_DEBUG + //#define JUCE_FORCE_DEBUG 1 #endif #ifndef JUCE_LOG_ASSERTIONS @@ -82,15 +82,15 @@ #endif #ifndef JUCE_CHECK_MEMORY_LEAKS - //#define JUCE_CHECK_MEMORY_LEAKS + //#define JUCE_CHECK_MEMORY_LEAKS 1 #endif #ifndef JUCE_DONT_AUTOLINK_TO_WIN32_LIBRARIES - //#define JUCE_DONT_AUTOLINK_TO_WIN32_LIBRARIES + //#define JUCE_DONT_AUTOLINK_TO_WIN32_LIBRARIES 1 #endif #ifndef JUCE_INCLUDE_ZLIB_CODE - //#define JUCE_INCLUDE_ZLIB_CODE + //#define JUCE_INCLUDE_ZLIB_CODE 1 #endif #ifndef JUCE_USE_CURL @@ -98,59 +98,59 @@ #endif #ifndef JUCE_CATCH_UNHANDLED_EXCEPTIONS - //#define JUCE_CATCH_UNHANDLED_EXCEPTIONS + //#define JUCE_CATCH_UNHANDLED_EXCEPTIONS 1 #endif #ifndef JUCE_ALLOW_STATIC_NULL_VARIABLES - //#define JUCE_ALLOW_STATIC_NULL_VARIABLES + //#define JUCE_ALLOW_STATIC_NULL_VARIABLES 1 #endif //============================================================================== // juce_events flags: #ifndef JUCE_EXECUTE_APP_SUSPEND_ON_IOS_BACKGROUND_TASK - //#define JUCE_EXECUTE_APP_SUSPEND_ON_IOS_BACKGROUND_TASK + //#define JUCE_EXECUTE_APP_SUSPEND_ON_IOS_BACKGROUND_TASK 1 #endif //============================================================================== // juce_graphics flags: #ifndef JUCE_USE_COREIMAGE_LOADER - //#define JUCE_USE_COREIMAGE_LOADER + //#define JUCE_USE_COREIMAGE_LOADER 1 #endif #ifndef JUCE_USE_DIRECTWRITE - //#define JUCE_USE_DIRECTWRITE + //#define JUCE_USE_DIRECTWRITE 1 #endif //============================================================================== // juce_gui_basics flags: #ifndef JUCE_ENABLE_REPAINT_DEBUGGING - //#define JUCE_ENABLE_REPAINT_DEBUGGING + //#define JUCE_ENABLE_REPAINT_DEBUGGING 1 #endif #ifndef JUCE_USE_XSHM - //#define JUCE_USE_XSHM + //#define JUCE_USE_XSHM 1 #endif #ifndef JUCE_USE_XRENDER - //#define JUCE_USE_XRENDER + //#define JUCE_USE_XRENDER 1 #endif #ifndef JUCE_USE_XCURSOR - //#define JUCE_USE_XCURSOR + //#define JUCE_USE_XCURSOR 1 #endif //============================================================================== // juce_gui_extra flags: #ifndef JUCE_WEB_BROWSER - //#define JUCE_WEB_BROWSER + //#define JUCE_WEB_BROWSER 1 #endif #ifndef JUCE_ENABLE_LIVE_CONSTANT_EDITOR - //#define JUCE_ENABLE_LIVE_CONSTANT_EDITOR + //#define JUCE_ENABLE_LIVE_CONSTANT_EDITOR 1 #endif //============================================================================== #ifndef JUCE_STANDALONE_APPLICATION diff --git a/extras/Projucer/Projucer.jucer b/extras/Projucer/Projucer.jucer index 4db0c29131..3e22b2be99 100644 --- a/extras/Projucer/Projucer.jucer +++ b/extras/Projucer/Projucer.jucer @@ -136,10 +136,6 @@ resource="0" file="Source/Application/jucer_DownloadCompileEngineThread.h"/> - - + + & ownerPointer, - bool showCodeEditorTab = false); - static const char* getSchemeFileSuffix() { return ".scheme"; } static const char* getSchemeFileWildCard() { return "*.scheme"; } diff --git a/extras/Projucer/Source/Application/jucer_Application.cpp b/extras/Projucer/Source/Application/jucer_Application.cpp index 75b195c78d..f4874dad8b 100644 --- a/extras/Projucer/Source/Application/jucer_Application.cpp +++ b/extras/Projucer/Source/Application/jucer_Application.cpp @@ -196,11 +196,11 @@ void ProjucerApplication::shutdown() } versionChecker = nullptr; - appearanceEditorWindow = nullptr; - globalPreferencesWindow = nullptr; utf8Window = nullptr; svgPathWindow = nullptr; aboutWindow = nullptr; + pathsWindow = nullptr; + editorColourSchemeWindow = nullptr; if (licenseController != nullptr) { @@ -386,7 +386,7 @@ void ProjucerApplication::createFileMenu (PopupMenu& menu) #if ! JUCE_MAC menu.addCommandItem (commandManager, CommandIDs::showAboutWindow); menu.addCommandItem (commandManager, CommandIDs::showAppUsageWindow); - menu.addCommandItem (commandManager, CommandIDs::showGlobalPreferences); + menu.addCommandItem (commandManager, CommandIDs::showGlobalPathsWindow); menu.addSeparator(); menu.addCommandItem (commandManager, StandardApplicationCommandIDs::quit); #endif @@ -463,7 +463,7 @@ void ProjucerApplication::createColourSchemeItems (PopupMenu& menu) for (auto s : schemes) { editorColourSchemes.addItem (codeEditorColourSchemeBaseID + i, s, - globalPreferencesWindow == nullptr, + editorColourSchemeWindow == nullptr, selectedEditorColourSchemeIndex == i); ++i; } @@ -472,7 +472,7 @@ void ProjucerApplication::createColourSchemeItems (PopupMenu& menu) editorColourSchemes.addSeparator(); editorColourSchemes.addItem (codeEditorColourSchemeBaseID + numEditorColourSchemes, - "Create...", globalPreferencesWindow == nullptr); + "Create...", editorColourSchemeWindow == nullptr); menu.addSubMenu ("Editor Colour Scheme", editorColourSchemes); } @@ -511,7 +511,7 @@ void ProjucerApplication::createExtraAppleMenuItems (PopupMenu& menu) menu.addCommandItem (commandManager, CommandIDs::showAboutWindow); menu.addCommandItem (commandManager, CommandIDs::showAppUsageWindow); menu.addSeparator(); - menu.addCommandItem (commandManager, CommandIDs::showGlobalPreferences); + menu.addCommandItem (commandManager, CommandIDs::showGlobalPathsWindow); } void ProjucerApplication::handleMainMenuCommand (int menuItemID) @@ -539,7 +539,7 @@ void ProjucerApplication::handleMainMenuCommand (int menuItemID) } else if (menuItemID == (codeEditorColourSchemeBaseID + numEditorColourSchemes)) { - AppearanceSettings::showGlobalPreferences (globalPreferencesWindow, true); + showEditorColourSchemeWindow(); } else { @@ -556,7 +556,7 @@ void ProjucerApplication::getAllCommands (Array & commands) CommandIDs::open, CommandIDs::closeAllDocuments, CommandIDs::saveAll, - CommandIDs::showGlobalPreferences, + CommandIDs::showGlobalPathsWindow, CommandIDs::showUTF8Tool, CommandIDs::showSVGPathTool, CommandIDs::showAboutWindow, @@ -580,9 +580,10 @@ void ProjucerApplication::getCommandInfo (CommandID commandID, ApplicationComman result.defaultKeypresses.add (KeyPress ('o', ModifierKeys::commandModifier, 0)); break; - case CommandIDs::showGlobalPreferences: - result.setInfo ("Preferences...", "Shows the preferences window.", CommandCategories::general, 0); - result.defaultKeypresses.add (KeyPress (',', ModifierKeys::commandModifier, 0)); + case CommandIDs::showGlobalPathsWindow: + result.setInfo ("Global Search Paths...", + "Shows the window to change the global search paths.", + CommandCategories::general, 0); break; case CommandIDs::closeAllDocuments: @@ -646,7 +647,7 @@ bool ProjucerApplication::perform (const InvocationInfo& info) case CommandIDs::closeAllDocuments: closeAllDocuments (true); break; case CommandIDs::showUTF8Tool: showUTF8ToolWindow(); break; case CommandIDs::showSVGPathTool: showSVGPathDataToolWindow(); break; - case CommandIDs::showGlobalPreferences: AppearanceSettings::showGlobalPreferences (globalPreferencesWindow); break; + case CommandIDs::showGlobalPathsWindow: showPathsWindow(); break; case CommandIDs::showAboutWindow: showAboutWindow(); break; case CommandIDs::showAppUsageWindow: showApplicationUsageDataAgreementPopup(); break; case CommandIDs::loginLogout: doLogout(); break; @@ -742,6 +743,32 @@ void ProjucerApplication::dismissApplicationUsageDataAgreementPopup() applicationUsageDataWindow = nullptr; } +void ProjucerApplication::showPathsWindow() +{ + if (pathsWindow != nullptr) + pathsWindow->toFront (true); + else + new FloatingToolWindow ("Global Search Paths", + "pathsWindowPos", + new GlobalSearchPathsWindowComponent(), pathsWindow, false, + 600, 500, 600, 500, 600, 500); +} + +void ProjucerApplication::showEditorColourSchemeWindow() +{ + if (editorColourSchemeWindow != nullptr) + editorColourSchemeWindow->toFront (true); + else + { + new FloatingToolWindow ("Editor Colour Scheme", + "editorColourSchemeWindowPos", + new EditorColourSchemeWindowComponent(), + editorColourSchemeWindow, + false, + 500, 500, 500, 500, 500, 500); + } +} + //============================================================================== struct FileWithTime { @@ -846,9 +873,10 @@ void ProjucerApplication::setColourScheme (int index, bool saveSetting) if (utf8Window != nullptr) utf8Window->sendLookAndFeelChange(); if (svgPathWindow != nullptr) svgPathWindow->sendLookAndFeelChange(); - if (globalPreferencesWindow != nullptr) globalPreferencesWindow->sendLookAndFeelChange(); if (aboutWindow != nullptr) aboutWindow->sendLookAndFeelChange(); if (applicationUsageDataWindow != nullptr) applicationUsageDataWindow->sendLookAndFeelChange(); + if (pathsWindow != nullptr) pathsWindow->sendLookAndFeelChange(); + if (editorColourSchemeWindow != nullptr) editorColourSchemeWindow->sendLookAndFeelChange(); auto* mcm = ModalComponentManager::getInstance(); for (auto i = 0; i < mcm->getNumModalComponents(); ++i) diff --git a/extras/Projucer/Source/Application/jucer_Application.h b/extras/Projucer/Source/Application/jucer_Application.h index 6559c88279..559edaf85a 100644 --- a/extras/Projucer/Source/Application/jucer_Application.h +++ b/extras/Projucer/Source/Application/jucer_Application.h @@ -102,6 +102,9 @@ public: void showApplicationUsageDataAgreementPopup(); void dismissApplicationUsageDataAgreementPopup(); + void showPathsWindow(); + void showEditorColourSchemeWindow(); + void updateAllBuildTabs(); LatestVersionChecker* createVersionChecker() const; @@ -129,8 +132,8 @@ public: OpenDocumentManager openDocumentManager; ScopedPointer commandManager; - ScopedPointer appearanceEditorWindow, globalPreferencesWindow, utf8Window, - svgPathWindow, aboutWindow, applicationUsageDataWindow; + ScopedPointer utf8Window, svgPathWindow, aboutWindow, applicationUsageDataWindow, + pathsWindow, editorColourSchemeWindow; ScopedPointer logger; diff --git a/extras/Projucer/Source/Application/jucer_AutoUpdater.cpp b/extras/Projucer/Source/Application/jucer_AutoUpdater.cpp index 1b4e9e4076..6e55fb0174 100644 --- a/extras/Projucer/Source/Application/jucer_AutoUpdater.cpp +++ b/extras/Projucer/Source/Application/jucer_AutoUpdater.cpp @@ -745,7 +745,7 @@ void LatestVersionChecker::modalStateFinished (int result, void LatestVersionChecker::askUserForLocationToDownload (URL& newVersionToDownload, const String& extraHeaders) { - File targetFolder (findDefaultModulesFolder()); + File targetFolder (EnabledModuleList::findGlobalModulesFolder()); if (isJuceModulesFolder (targetFolder)) targetFolder = targetFolder.getParentDirectory(); diff --git a/extras/Projucer/Source/Application/jucer_CommandIDs.h b/extras/Projucer/Source/Application/jucer_CommandIDs.h index 3046977511..175ac31701 100644 --- a/extras/Projucer/Source/Application/jucer_CommandIDs.h +++ b/extras/Projucer/Source/Application/jucer_CommandIDs.h @@ -45,7 +45,7 @@ namespace CommandIDs createNewExporter = 0x300015, showUTF8Tool = 0x300020, - showGlobalPreferences = 0x300021, + showGlobalPathsWindow = 0x300021, showTranslationTool = 0x300022, showSVGPathTool = 0x300023, showAboutWindow = 0x300024, diff --git a/extras/Projucer/Source/Application/jucer_GlobalPreferences.cpp b/extras/Projucer/Source/Application/jucer_GlobalPreferences.cpp deleted file mode 100644 index f7adcf1c76..0000000000 --- a/extras/Projucer/Source/Application/jucer_GlobalPreferences.cpp +++ /dev/null @@ -1,465 +0,0 @@ -/* - ============================================================================== - - This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. - - JUCE is an open source library subject to commercial or open-source - licensing. - - By using JUCE, you agree to the terms of both the JUCE 5 End-User License - Agreement and JUCE 5 Privacy Policy (both updated and effective as of the - 27th April 2017). - - End User License Agreement: www.juce.com/juce-5-licence - Privacy Policy: www.juce.com/juce-5-privacy-policy - - Or: You may also use this code under the terms of the GPL v3 (see - www.gnu.org/licenses). - - JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER - EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE - DISCLAIMED. - - ============================================================================== -*/ - -#include "../jucer_Headers.h" -#include "jucer_GlobalPreferences.h" -#include "../Utility/jucer_FloatingToolWindow.h" -#include "../Utility/jucer_ColourPropertyComponent.h" -#include "jucer_Application.h" - -//============================================================================== -PathSettingsTab::PathSettingsTab (DependencyPathOS os) -{ - const int maxChars = 1024; - - auto& settings = getAppSettings(); - - vst3PathComponent = pathComponents.add (new TextPropertyComponent (settings.getGlobalPath (Ids::vst3Path, os), "VST3 SDK", maxChars, false)); - - #if ! JUCE_LINUX - rtasPathComponent = pathComponents.add (new TextPropertyComponent (settings.getGlobalPath (Ids::rtasPath, os), "RTAS SDK", maxChars, false)); - aaxPathComponent = pathComponents.add (new TextPropertyComponent (settings.getGlobalPath (Ids::aaxPath, os), "AAX SDK", maxChars, false)); - #endif - - androidSdkPathComponent = pathComponents.add (new TextPropertyComponent (settings.getGlobalPath (Ids::androidSDKPath, os), "Android SDK", maxChars, false)); - androidNdkPathComponent = pathComponents.add (new TextPropertyComponent (settings.getGlobalPath (Ids::androidNDKPath, os), "Android NDK", maxChars, false)); - - for (auto component : pathComponents) - { - addAndMakeVisible (component); - component->addListener (this); - textPropertyComponentChanged (component); - } -} - -PathSettingsTab::~PathSettingsTab() -{ -} - -void PathSettingsTab::textPropertyComponentChanged (TextPropertyComponent* textPropertyComponent) -{ - auto keyName = getKeyForPropertyComponent (textPropertyComponent); - - auto textColour = getAppSettings().isGlobalPathValid (File::getCurrentWorkingDirectory(), keyName, textPropertyComponent->getText()) - ? findColour (widgetTextColourId) - : Colours::red; - - textPropertyComponent->setColour (TextPropertyComponent::textColourId, textColour); -} - -Identifier PathSettingsTab::getKeyForPropertyComponent (TextPropertyComponent* component) const -{ - if (component == vst3PathComponent) return Ids::vst3Path; - if (component == rtasPathComponent) return Ids::rtasPath; - if (component == aaxPathComponent) return Ids::aaxPath; - if (component == androidSdkPathComponent) return Ids::androidSDKPath; - if (component == androidNdkPathComponent) return Ids::androidNDKPath; - - // this property component does not have a key associated to it! - jassertfalse; - return {}; -} - -Component* PathSettingsTab::getContent() -{ - return this; -} - -String PathSettingsTab::getName() const noexcept -{ - return "Paths"; -} - -void PathSettingsTab::resized() -{ - const int componentHeight = 25; - - for (auto component : pathComponents) - { - const auto elementNumber = pathComponents.indexOf (component); - component->setBounds (10, componentHeight * elementNumber, getWidth() - 20, componentHeight); - } -} - -void PathSettingsTab::lookAndFeelChanged() -{ - for (auto* comp : pathComponents) - textPropertyComponentChanged (comp); -} - -//============================================================================== -struct AppearanceEditor -{ - struct FontScanPanel : public Component, - private Timer - { - FontScanPanel() - { - fontsToScan = Font::findAllTypefaceNames(); - startTimer (1); - } - - void paint (Graphics& g) override - { - g.fillAll (findColour (backgroundColourId)); - - g.setFont (14.0f); - g.setColour (findColour (defaultTextColourId)); - g.drawFittedText ("Scanning for fonts..", getLocalBounds(), Justification::centred, 2); - - const auto size = 30; - getLookAndFeel().drawSpinningWaitAnimation (g, Colours::white, (getWidth() - size) / 2, getHeight() / 2 - 50, size, size); - } - - void timerCallback() override - { - repaint(); - - if (fontsToScan.size() == 0) - { - getAppSettings().monospacedFontNames = fontsFound; - - if (auto* tab = findParentComponentOfClass()) - tab->changeContent (new EditorPanel()); - } - else - { - if (isMonospacedTypeface (fontsToScan[0])) - fontsFound.add (fontsToScan[0]); - - fontsToScan.remove (0); - } - } - - // A rather hacky trick to select only the fixed-pitch fonts.. - // This is unfortunately a bit slow, but will work on all platforms. - static bool isMonospacedTypeface (const String& name) - { - const Font font (name, 20.0f, Font::plain); - - const auto width = font.getStringWidth ("...."); - - return width == font.getStringWidth ("WWWW") - && width == font.getStringWidth ("0000") - && width == font.getStringWidth ("1111") - && width == font.getStringWidth ("iiii"); - } - - StringArray fontsToScan, fontsFound; - }; - - //============================================================================== - struct EditorPanel : public Component, - private ButtonListener - { - EditorPanel() - : loadButton ("Load Scheme..."), - saveButton ("Save Scheme...") - { - rebuildProperties(); - addAndMakeVisible (panel); - - addAndMakeVisible (loadButton); - addAndMakeVisible (saveButton); - - loadButton.addListener (this); - saveButton.addListener (this); - - lookAndFeelChanged(); - - saveSchemeState(); - } - - ~EditorPanel() - { - if (hasSchemeBeenModifiedSinceSave()) - saveScheme (true); - } - - void rebuildProperties() - { - auto& scheme = getAppSettings().appearance; - - Array props; - auto fontValue = scheme.getCodeFontValue(); - props.add (FontNameValueSource::createProperty ("Code Editor Font", fontValue)); - props.add (FontSizeValueSource::createProperty ("Font Size", fontValue)); - - const auto colourNames = scheme.getColourNames(); - - for (int i = 0; i < colourNames.size(); ++i) - props.add (new ColourPropertyComponent (nullptr, colourNames[i], - scheme.getColourValue (colourNames[i]), - Colours::white, false)); - - panel.clear(); - panel.addProperties (props); - } - - void resized() override - { - auto r = getLocalBounds(); - panel.setBounds (r.removeFromTop (getHeight() - 28).reduced (10, 2)); - loadButton.setBounds (r.removeFromLeft (getWidth() / 2).reduced (10, 1)); - saveButton.setBounds (r.reduced (10, 1)); - } - - private: - PropertyPanel panel; - TextButton loadButton, saveButton; - - Font codeFont; - Array colourValues; - - void buttonClicked (Button* b) override - { - if (b == &loadButton) - loadScheme(); - else - saveScheme (false); - } - - void saveScheme (bool isExit) - { - FileChooser fc ("Select a file in which to save this colour-scheme...", - getAppSettings().appearance.getSchemesFolder() - .getNonexistentChildFile ("Scheme", AppearanceSettings::getSchemeFileSuffix()), - AppearanceSettings::getSchemeFileWildCard()); - - if (fc.browseForFileToSave (true)) - { - File file (fc.getResult().withFileExtension (AppearanceSettings::getSchemeFileSuffix())); - getAppSettings().appearance.writeToFile (file); - getAppSettings().appearance.refreshPresetSchemeList(); - - saveSchemeState(); - ProjucerApplication::getApp().selectEditorColourSchemeWithName (file.getFileNameWithoutExtension()); - } - else if (isExit) - { - restorePreviousScheme(); - } - } - - void loadScheme() - { - FileChooser fc ("Please select a colour-scheme file to load...", - getAppSettings().appearance.getSchemesFolder(), - AppearanceSettings::getSchemeFileWildCard()); - - if (fc.browseForFileToOpen()) - { - if (getAppSettings().appearance.readFromFile (fc.getResult())) - { - rebuildProperties(); - saveSchemeState(); - } - } - } - - void lookAndFeelChanged() override - { - loadButton.setColour (TextButton::buttonColourId, - findColour (secondaryButtonBackgroundColourId)); - } - - void saveSchemeState() - { - auto& appearance = getAppSettings().appearance; - const auto colourNames = appearance.getColourNames(); - - codeFont = appearance.getCodeFont(); - - colourValues.clear(); - for (int i = 0; i < colourNames.size(); ++i) - colourValues.add (appearance.getColourValue (colourNames[i]).getValue()); - } - - bool hasSchemeBeenModifiedSinceSave() - { - auto& appearance = getAppSettings().appearance; - const auto colourNames = appearance.getColourNames(); - - if (codeFont != appearance.getCodeFont()) - return true; - - for (int i = 0; i < colourNames.size(); ++i) - if (colourValues[i] != appearance.getColourValue (colourNames[i]).getValue()) - return true; - - return false; - } - - void restorePreviousScheme() - { - auto& appearance = getAppSettings().appearance; - const auto colourNames = appearance.getColourNames(); - - appearance.getCodeFontValue().setValue (codeFont.toString()); - - for (int i = 0; i < colourNames.size(); ++i) - appearance.getColourValue (colourNames[i]).setValue (colourValues[i]); - } - - - JUCE_DECLARE_NON_COPYABLE (EditorPanel) - }; - - //============================================================================== - struct FontNameValueSource : public ValueSourceFilter - { - FontNameValueSource (const Value& source) : ValueSourceFilter (source) {} - - var getValue() const override - { - return Font::fromString (sourceValue.toString()).getTypefaceName(); - } - - void setValue (const var& newValue) override - { - auto font = Font::fromString (sourceValue.toString()); - font.setTypefaceName (newValue.toString().isEmpty() ? Font::getDefaultMonospacedFontName() - : newValue.toString()); - sourceValue = font.toString(); - } - - static ChoicePropertyComponent* createProperty (const String& title, const Value& value) - { - auto fontNames = getAppSettings().monospacedFontNames; - - Array values; - values.add (Font::getDefaultMonospacedFontName()); - values.add (var()); - - for (int i = 0; i < fontNames.size(); ++i) - values.add (fontNames[i]); - - StringArray names; - names.add (""); - names.add (String()); - names.addArray (getAppSettings().monospacedFontNames); - - return new ChoicePropertyComponent (Value (new FontNameValueSource (value)), - title, names, values); - } - }; - - //============================================================================== - struct FontSizeValueSource : public ValueSourceFilter - { - FontSizeValueSource (const Value& source) : ValueSourceFilter (source) {} - - var getValue() const override - { - return Font::fromString (sourceValue.toString()).getHeight(); - } - - void setValue (const var& newValue) override - { - sourceValue = Font::fromString (sourceValue.toString()).withHeight (newValue).toString(); - } - - static PropertyComponent* createProperty (const String& title, const Value& value) - { - return new SliderPropertyComponent (Value (new FontSizeValueSource (value)), - title, 5.0, 40.0, 0.1, 0.5); - } - }; -}; - -void AppearanceSettings::showGlobalPreferences (ScopedPointer& ownerPointer, bool showCodeEditorTab) -{ - if (ownerPointer != nullptr) - ownerPointer->toFront (true); - else - { - auto* prefs = new GlobalPreferencesComponent(); - - new FloatingToolWindow ("Preferences", - "globalPreferencesEditorPos", - prefs, - ownerPointer, false, - 500, 500, 500, 500, 500, 500); - - if (showCodeEditorTab) - prefs->setCurrentTabIndex (1); - } -} - -//============================================================================== -AppearanceSettingsTab::AppearanceSettingsTab() -{ - if (getAppSettings().monospacedFontNames.size() == 0) - content = new AppearanceEditor::FontScanPanel(); - else - content = new AppearanceEditor::EditorPanel(); - - changeContent (content); -} - -Component* AppearanceSettingsTab::getContent() -{ - return this; -} - -void AppearanceSettingsTab::changeContent (Component* newContent) -{ - content = newContent; - addAndMakeVisible (content); - content->setBounds (getLocalBounds()); -} - -String AppearanceSettingsTab::getName() const noexcept -{ - return "Code Editor"; -} - -void AppearanceSettingsTab::resized() -{ - content->setBounds (getLocalBounds()); -} - -//============================================================================== -GlobalPreferencesComponent::GlobalPreferencesComponent() - : TabbedComponent (TabbedButtonBar::TabsAtTop) -{ - preferenceTabs.add (new PathSettingsTab (TargetOS::getThisOS())); - preferenceTabs.add (new AppearanceSettingsTab); - - for (GlobalPreferencesTab** tab = preferenceTabs.begin(); tab != preferenceTabs.end(); ++tab) - addTab ((*tab)->getName(), findColour (backgroundColourId, true), (*tab)->getContent(), true); -} - -void GlobalPreferencesComponent::paint (Graphics& g) -{ - g.fillAll (findColour (backgroundColourId)); -} - -void GlobalPreferencesComponent::lookAndFeelChanged() -{ - for (auto* tab : preferenceTabs) - tab->getContent()->sendLookAndFeelChange(); -} diff --git a/extras/Projucer/Source/Application/jucer_GlobalPreferences.h b/extras/Projucer/Source/Application/jucer_GlobalPreferences.h deleted file mode 100644 index 4689936a09..0000000000 --- a/extras/Projucer/Source/Application/jucer_GlobalPreferences.h +++ /dev/null @@ -1,116 +0,0 @@ -/* - ============================================================================== - - This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. - - JUCE is an open source library subject to commercial or open-source - licensing. - - By using JUCE, you agree to the terms of both the JUCE 5 End-User License - Agreement and JUCE 5 Privacy Policy (both updated and effective as of the - 27th April 2017). - - End User License Agreement: www.juce.com/juce-5-licence - Privacy Policy: www.juce.com/juce-5-privacy-policy - - Or: You may also use this code under the terms of the GPL v3 (see - www.gnu.org/licenses). - - JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER - EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE - DISCLAIMED. - - ============================================================================== -*/ - -#pragma once - -//============================================================================== - -#include "../Project/jucer_DependencyPathPropertyComponent.h" - - -class GlobalPreferencesTab -{ -public: - virtual ~GlobalPreferencesTab() {} - - virtual Component* getContent() = 0; - virtual String getName() const noexcept = 0; -}; - -//============================================================================== -/** This component implements the "Paths" tab in the global preferences window, - which defines the default paths for dependencies like third-party SDKs - for this machine. -*/ -class PathSettingsTab : public GlobalPreferencesTab, - public Component, - private TextPropertyComponent::Listener -{ -public: - PathSettingsTab (DependencyPathOS); - ~PathSettingsTab(); - - Component* getContent() override; - String getName() const noexcept override; - - void resized() override; - -private: - void textPropertyComponentChanged (TextPropertyComponent*) override; - - Identifier getKeyForPropertyComponent (TextPropertyComponent*) const; - - void lookAndFeelChanged() override; - - OwnedArray pathComponents; - - TextPropertyComponent* vst3PathComponent; - TextPropertyComponent* rtasPathComponent; - TextPropertyComponent* aaxPathComponent; - TextPropertyComponent* androidSdkPathComponent; - TextPropertyComponent* androidNdkPathComponent; - - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (PathSettingsTab) -}; - -//============================================================================== -/** This component implements the "Code Editor" tab in the global preferences window, - which sets font sizes and colours for the Projucer's code editor. - The content is either an EditorPanel (the actual settings tab) or a FontScanPanel - (shown if the tab is scanning for available fonts before showing the EditorPanel). -*/ -class AppearanceSettingsTab : public GlobalPreferencesTab, - public Component -{ -public: - AppearanceSettingsTab(); - - Component* getContent() override; - void changeContent (Component* newContent); - String getName() const noexcept override; - - void resized() override; - -private: - ScopedPointer content; - - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AppearanceSettingsTab) -}; - -//============================================================================== -class GlobalPreferencesComponent : public TabbedComponent -{ -public: - GlobalPreferencesComponent(); - void paint (Graphics&) override; - -private: - void lookAndFeelChanged() override; - - OwnedArray preferenceTabs; - - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (GlobalPreferencesComponent) -}; diff --git a/extras/Projucer/Source/Application/jucer_Main.cpp b/extras/Projucer/Source/Application/jucer_Main.cpp index 1f68447cd2..d858fed6c7 100644 --- a/extras/Projucer/Source/Application/jucer_Main.cpp +++ b/extras/Projucer/Source/Application/jucer_Main.cpp @@ -35,6 +35,8 @@ #include "../Utility/jucer_SVGPathDataComponent.h" #include "../Utility/jucer_AboutWindowComponent.h" #include "../Utility/jucer_ApplicationUsageDataWindowComponent.h" +#include "../Utility/jucer_EditorColourSchemeWindowComponent.h" +#include "../Utility/jucer_GlobalSearchPathsWindowComponent.h" #include "../Utility/jucer_FloatingToolWindow.h" #include "../LiveBuildEngine/projucer_MessageIDs.h" diff --git a/extras/Projucer/Source/Application/jucer_MainWindow.cpp b/extras/Projucer/Source/Application/jucer_MainWindow.cpp index e0be5970d9..5adcde5fe0 100644 --- a/extras/Projucer/Source/Application/jucer_MainWindow.cpp +++ b/extras/Projucer/Source/Application/jucer_MainWindow.cpp @@ -580,39 +580,3 @@ Project* MainWindowList::getFrontmostProject() return nullptr; } - -File findDefaultModulesFolder (bool mustContainJuceCoreModule) -{ - auto& windows = ProjucerApplication::getApp().mainWindowList; - - for (int i = windows.windows.size(); --i >= 0;) - { - if (auto* p = windows.windows.getUnchecked (i)->getProject()) - { - const File f (EnabledModuleList::findDefaultModulesFolder (*p)); - - if (isJuceModulesFolder (f) || (f.isDirectory() && ! mustContainJuceCoreModule)) - return f; - } - } - - if (mustContainJuceCoreModule) - return findDefaultModulesFolder (false); - - auto f = File::getSpecialLocation (File::currentApplicationFile); - - for (;;) - { - auto parent = f.getParentDirectory(); - - if (parent == f || ! parent.isDirectory()) - break; - - if (isJuceFolder (parent)) - return parent.getChildFile ("modules"); - - f = parent; - } - - return {}; -} diff --git a/extras/Projucer/Source/ComponentEditor/ui/jucer_JucerDocumentEditor.cpp b/extras/Projucer/Source/ComponentEditor/ui/jucer_JucerDocumentEditor.cpp index 010fb0be7e..f638ecda18 100644 --- a/extras/Projucer/Source/ComponentEditor/ui/jucer_JucerDocumentEditor.cpp +++ b/extras/Projucer/Source/ComponentEditor/ui/jucer_JucerDocumentEditor.cpp @@ -26,7 +26,6 @@ #include "../../jucer_Headers.h" #include "../../Application/jucer_AppearanceSettings.h" -#include "../../Application/jucer_GlobalPreferences.h" #include "../../Application/jucer_Application.h" #include "jucer_JucerDocumentEditor.h" #include "jucer_TestComponent.h" diff --git a/extras/Projucer/Source/LiveBuildEngine/projucer_CompileEngineClient.cpp b/extras/Projucer/Source/LiveBuildEngine/projucer_CompileEngineClient.cpp index 252143f327..74f6de5883 100644 --- a/extras/Projucer/Source/LiveBuildEngine/projucer_CompileEngineClient.cpp +++ b/extras/Projucer/Source/LiveBuildEngine/projucer_CompileEngineClient.cpp @@ -519,9 +519,7 @@ private: paths.addArray (getSearchPathsFromString (ProjectProperties::getSystemHeaderPathString (project))); if (project.getProjectType().isAudioPlugin()) - { - paths.add (getAppSettings().getGlobalPath (Ids::vst3Path, TargetOS::getThisOS()).toString()); - } + paths.add (getAppSettings().getStoredPath (Ids::vst3Path).toString()); OwnedArray modules; project.getModules().createRequiredModules (modules); diff --git a/extras/Projucer/Source/Project Saving/jucer_ProjectExporter.cpp b/extras/Projucer/Source/Project Saving/jucer_ProjectExporter.cpp index 0d0f2a99f4..212c2f2c3a 100644 --- a/extras/Projucer/Source/Project Saving/jucer_ProjectExporter.cpp +++ b/extras/Projucer/Source/Project Saving/jucer_ProjectExporter.cpp @@ -493,8 +493,26 @@ Value ProjectExporter::getPathForModuleValue (const String& moduleID) String ProjectExporter::getPathForModuleString (const String& moduleID) const { - return settings.getChildWithName (Ids::MODULEPATHS) - .getChildWithProperty (Ids::ID, moduleID) [Ids::path].toString(); + auto exporterPath = settings.getChildWithName (Ids::MODULEPATHS) + .getChildWithProperty (Ids::ID, moduleID) [Ids::path].toString(); + + if (exporterPath.isEmpty() || project.getModules().shouldUseGlobalPath (moduleID).getValue()) + { + auto id = EnabledModuleList::isJuceModule (moduleID) ? Ids::defaultJuceModulePath + : Ids::defaultUserModulePath; + + if (TargetOS::getThisOS() != getTargetOSForExporter()) + return getAppSettings().getFallbackPathForOS (id, getTargetOSForExporter()).toString(); + + if (id == Ids::defaultJuceModulePath) + return getAppSettings().getStoredPath (Ids::defaultJuceModulePath).toString(); + + return EnabledModuleList::findUserModuleFolder (moduleID, + getAppSettings().getStoredPath (Ids::defaultUserModulePath).toString(), + project).getFullPathName(); + } + + return exporterPath; } void ProjectExporter::removePathForModule (const String& moduleID) @@ -504,6 +522,18 @@ void ProjectExporter::removePathForModule (const String& moduleID) paths.removeChild (m, project.getUndoManagerFor (settings)); } +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(); + + return targetOS; +} + RelativePath ProjectExporter::getModuleFolderRelativeToProject (const String& moduleID) const { if (project.getModules().shouldCopyModuleFilesLocally (moduleID).getValue()) diff --git a/extras/Projucer/Source/Project Saving/jucer_ProjectExporter.h b/extras/Projucer/Source/Project Saving/jucer_ProjectExporter.h index 8d249b6773..bc97d44d79 100644 --- a/extras/Projucer/Source/Project Saving/jucer_ProjectExporter.h +++ b/extras/Projucer/Source/Project Saving/jucer_ProjectExporter.h @@ -28,7 +28,7 @@ #include "../Project/jucer_Project.h" #include "../Project/jucer_ProjectType.h" -#include "../Application/jucer_GlobalPreferences.h" +#include "../Project/jucer_DependencyPathPropertyComponent.h" class ProjectSaver; @@ -160,6 +160,8 @@ public: String getPathForModuleString (const String& moduleID) const; void removePathForModule (const String& moduleID); + TargetOS::OS getTargetOSForExporter() const; + RelativePath getLegacyModulePath (const String& moduleID) const; String getLegacyModulePath() const; diff --git a/extras/Projucer/Source/Project/jucer_ConfigTree_Base.h b/extras/Projucer/Source/Project/jucer_ConfigTree_Base.h index 512010188d..cc9cb29728 100644 --- a/extras/Projucer/Source/Project/jucer_ConfigTree_Base.h +++ b/extras/Projucer/Source/Project/jucer_ConfigTree_Base.h @@ -28,24 +28,11 @@ class InfoButton : public Button { public: - InfoButton (PropertyComponent& comp) - : Button (String()), - associatedComponent (comp) + InfoButton (const String& infoToDisplay = String()) + : Button (String()) { - tooltip = associatedComponent.getTooltip(); - auto stringWidth = Font (14.0f).getStringWidthFloat (tooltip); - - int maxWidth = 300; - - if (stringWidth > maxWidth) - { - width = maxWidth; - numLines += static_cast (stringWidth / width); - } - else - { - width = roundToInt (stringWidth); - } + if (infoToDisplay.isNotEmpty()) + setInfoToDisplay (infoToDisplay); } void paintButton (Graphics& g, bool isMouseOverButton, bool isButtonDown) override @@ -64,23 +51,38 @@ public: void clicked() override { - auto* w = new InfoWindow (tooltip); + auto* w = new InfoWindow (info); w->setSize (width, w->getHeight() * numLines + 10); CallOutBox::launchAsynchronously (w, getScreenBounds(), nullptr); } - PropertyComponent& associatedComponent; + void setInfoToDisplay (const String& infoToDisplay) + { + if (infoToDisplay.isNotEmpty()) + { + info = infoToDisplay; + + auto stringWidth = roundToInt (Font (14.0f).getStringWidthFloat (info)); + width = jmin (300, stringWidth); + + numLines += static_cast (stringWidth / width); + } + } + + void setAssociatedComponent (Component* comp) { associatedComponent = comp; } + Component* getAssociatedComponent() { return associatedComponent; } private: - String tooltip; + String info; + Component* associatedComponent = nullptr; int width; int numLines = 1; //============================================================================== struct InfoWindow : public Component { - InfoWindow (String s) + InfoWindow (const String& s) : stringToDisplay (s) { setSize (150, 14); @@ -125,7 +127,8 @@ public: if (! prop->getTooltip().isEmpty()) { - addAndMakeVisible (infoButtons.add (new InfoButton (*prop))); + addAndMakeVisible (infoButtons.add (new InfoButton (prop->getTooltip()))); + infoButtons.getLast()->setAssociatedComponent (prop); prop->setTooltip (String()); // set the tooltip to empty so it only displays when its button is clicked } } @@ -145,7 +148,7 @@ public: InfoButton* buttonToUse = nullptr; for (auto* b : infoButtons) - if (&b->associatedComponent == pp) + if (b->getAssociatedComponent() == pp) buttonToUse = b; if (buttonToUse != nullptr) @@ -189,11 +192,12 @@ public: if (pp->getName() == "Dependencies") return; - if (auto* propertyChild = pp->getChildComponent (0)) + for (int i = pp->getNumChildComponents() - 1; i >= 0; --i) { - auto bounds = propertyChild->getBounds(); + auto* child = pp->getChildComponent (i); - propertyChild->setBounds (bounds.withSizeKeepingCentre (propertyChild->getWidth(), pp->getPreferredHeight())); + auto bounds = child->getBounds(); + child->setBounds (bounds.withSizeKeepingCentre (child->getWidth(), pp->getPreferredHeight())); } } diff --git a/extras/Projucer/Source/Project/jucer_ConfigTree_Modules.h b/extras/Projucer/Source/Project/jucer_ConfigTree_Modules.h index e4d08513a8..d60f7694db 100644 --- a/extras/Projucer/Source/Project/jucer_ConfigTree_Modules.h +++ b/extras/Projucer/Source/Project/jucer_ConfigTree_Modules.h @@ -30,12 +30,16 @@ public: ModuleItem (Project& p, const String& modID) : project (p), moduleID (modID) { + auto moduleVersionNum = project.getModules().getModuleInfo (moduleID).getVersion(); + + if (moduleVersionNum != ProjucerApplication::getApp().getApplicationVersion()) + moduleVersion = moduleVersionNum; } bool canBeSelected() const override { return true; } bool mightContainSubItems() override { return false; } String getUniqueName() const override { return "module_" + moduleID; } - String getDisplayName() const override { return moduleID; } + String getDisplayName() const override { return moduleID + (moduleVersion.isNotEmpty() ? String (" (" + moduleVersion + ")") : ""); } String getRenamingName() const override { return getDisplayName(); } void setName (const String&) override {} bool isMissing() const override { return hasMissingDependencies(); } @@ -86,6 +90,7 @@ public: Project& project; String moduleID; + String moduleVersion; private: bool hasMissingDependencies() const @@ -94,7 +99,8 @@ private: } //============================================================================== - class ModuleSettingsPanel : public Component + class ModuleSettingsPanel : public Component, + private Value::Listener { public: ModuleSettingsPanel (Project& p, const String& modID) @@ -102,37 +108,72 @@ private: project (p), moduleID (modID) { + defaultJuceModulePathValue.referTo (getAppSettings().getStoredPath (Ids::defaultJuceModulePath)); + defaultUserModulePathValue.referTo (getAppSettings().getStoredPath (Ids::defaultUserModulePath)); + + defaultJuceModulePathValue.addListener (this); + defaultUserModulePathValue.addListener (this); + addAndMakeVisible (group); refresh(); } void refresh() { - setEnabled (project.getModules().isModuleEnabled (moduleID)); + auto& modules = project.getModules(); + const auto& isUsingGlobalPathValue = modules.shouldUseGlobalPath (moduleID); + + setEnabled (modules.isModuleEnabled (moduleID)); PropertyListBuilder props; props.add (new ModuleInfoComponent (project, moduleID)); - if (project.getModules().getExtraDependenciesNeeded (moduleID).size() > 0) + if (modules.getExtraDependenciesNeeded (moduleID).size() > 0) props.add (new MissingDependenciesComponent (project, moduleID)); + modulePathValueSources.clear(); + for (Project::ExporterIterator exporter (project); exporter.next();) - props.add (new FilePathPropertyComponent (exporter->getPathForModuleValue (moduleID), - "Path for " + exporter->getName().quoted(), - true, "*", project.getProjectFolder()), + { + auto key = modules.isJuceModule (moduleID) ? Ids::defaultJuceModulePath + : Ids::defaultUserModulePath; + + Value src (modulePathValueSources.add (new DependencyPathValueSource (exporter->getPathForModuleValue (moduleID), + key, exporter->getTargetOSForExporter()))); + + auto* pathComponent = new DependencyFilePathPropertyComponent (src, "Path for " + exporter->getName().quoted(), + true, "*", project.getProjectFolder()); + + props.add (pathComponent, "A path to the folder that contains the " + moduleID + " module when compiling the " - + exporter->getName().quoted() + " target. " + + exporter->getName().quoted() + " target. " "This can be an absolute path, or relative to the jucer project folder, but it " - "must be valid on the filesystem of the target machine that will be performing this build."); + "must be valid on the filesystem of the target machine that will be performing this build. If this " + "is empty then the global path will be used."); - props.add (new BooleanPropertyComponent (project.getModules().shouldCopyModuleFilesLocally (moduleID), + pathComponent->setEnabled (! isUsingGlobalPathValue.getValue()); + } + + globalPathValue.referTo (isUsingGlobalPathValue); + + auto menuItemString = (TargetOS::getThisOS() == TargetOS::osx ? "\"Projucer->Global Search Paths...\"" + : "\"File->Global Search Paths...\""); + + props.add (new BooleanPropertyComponent (globalPathValue, + "Use global path", "Use global path for this module"), + String ("If this is enabled, then the locally-stored global path (set in the ") + menuItemString + " menu item) " + "will be used as the path to this module. " + "This means that if this Projucer project is opened on another machine it will use that machine's global path as the path to this module."); + globalPathValue.addListener (this); + + props.add (new BooleanPropertyComponent (modules.shouldCopyModuleFilesLocally (moduleID), "Create local copy", "Copy the module into the project folder"), "If this is enabled, then a local copy of the entire module will be made inside your project (in the auto-generated JuceLibraryFiles folder), " "so that your project will be self-contained, and won't need to contain any references to files in other folders. " "This also means that you can check the module into your source-control system to make sure it is always in sync with your own code."); - props.add (new BooleanPropertyComponent (project.getModules().shouldShowAllModuleFilesInProject (moduleID), + props.add (new BooleanPropertyComponent (modules.shouldShowAllModuleFilesInProject (moduleID), "Add source to project", "Make module files browsable in projects"), "If this is enabled, then the entire source tree from this module will be shown inside your project, " "making it easy to browse/edit the module's classes. If disabled, then only the minimum number of files " @@ -148,7 +189,7 @@ private: mappings.add (Project::configFlagEnabled); mappings.add (Project::configFlagDisabled); - ModuleDescription info (project.getModules().getModuleInfo (moduleID)); + ModuleDescription info (modules.getModuleInfo (moduleID)); if (info.isValid()) { @@ -177,6 +218,28 @@ private: PropertyGroupComponent group; Project& project; String moduleID; + Value globalPathValue; + Value defaultJuceModulePathValue, defaultUserModulePathValue; + + ReferenceCountedArray modulePathValueSources; + + //============================================================================== + void valueChanged (Value& v) override + { + if (v == globalPathValue) + { + auto useGlobalPath = globalPathValue.getValue(); + + for (auto prop : group.properties) + { + if (auto* pathPropertyComponent = dynamic_cast (prop)) + pathPropertyComponent->setEnabled (! useGlobalPath); + } + } + + if (auto* moduleInfo = dynamic_cast (group.properties.getUnchecked (0))) + moduleInfo->refresh(); + } //============================================================================== class ModuleInfoComponent : public PropertyComponent, @@ -193,13 +256,13 @@ private: refresh(); } - private: void refresh() override { info = project.getModules().getModuleInfo (moduleID); repaint(); } + private: void paint (Graphics& g) override { auto bounds = getLocalBounds().reduced (10); @@ -207,15 +270,17 @@ private: if (info.isValid()) { - auto topSlice = bounds.removeFromTop (bounds.getHeight() / 3); + auto topSlice = bounds.removeFromTop (bounds.getHeight() / 2); bounds.removeFromTop (bounds.getHeight() / 6); auto bottomSlice = bounds; g.setColour (findColour (defaultTextColourId)); - g.drawFittedText (info.getName(), topSlice.removeFromTop (topSlice.getHeight() / 3), Justification::centredLeft, 1); - g.drawFittedText ("Version: " + info.getVersion(), topSlice.removeFromTop (topSlice.getHeight() / 2), Justification::centredLeft, 1); - g.drawFittedText ("License: " + info.getLicense(), topSlice.removeFromTop (topSlice.getHeight()), Justification::centredLeft, 1); + g.drawFittedText (info.getName(), topSlice.removeFromTop (topSlice.getHeight() / 4), Justification::centredLeft, 1); + g.drawFittedText ("Version: " + info.getVersion(), topSlice.removeFromTop (topSlice.getHeight() / 3), Justification::centredLeft, 1); + g.drawFittedText ("License: " + info.getLicense(), topSlice.removeFromTop (topSlice.getHeight() / 2), Justification::centredLeft, 1); + g.drawFittedText ("Location: " + info.getFolder().getParentDirectory().getFullPathName(), + topSlice.removeFromTop (topSlice.getHeight()), Justification::centredLeft, 1); g.drawFittedText (info.getDescription(), bottomSlice, Justification::topLeft, 3, 1.0f); } @@ -270,23 +335,22 @@ private: void buttonClicked (Button*) override { - bool anyFailed = false; - ModuleList list; - list.scanAllKnownFolders (project); - for (int i = missingDependencies.size(); --i >= 0;) - { - if (const ModuleDescription* info = list.getModuleWithID (missingDependencies[i])) - project.getModules().addModule (info->moduleFolder, project.getModules().areMostModulesCopiedLocally()); - else - anyFailed = true; - } + list.scanGlobalJuceModulePath(); + + if (! tryToFix (list)) + list.scanGlobalUserModulePath(); + + if (! tryToFix (list)) + list.scanProjectExporterModulePaths (project); + + bool fixed = tryToFix (list); if (ModuleSettingsPanel* p = findParentComponentOfClass()) p->refresh(); - if (anyFailed) + if (! fixed) AlertWindow::showMessageBoxAsync (AlertWindow::WarningIcon, "Adding Missing Dependencies", "Couldn't locate some of these modules - you'll need to find their " @@ -304,6 +368,26 @@ private: StringArray missingDependencies; TextButton fixButton; + bool tryToFix (ModuleList& list) + { + auto& modules = project.getModules(); + auto copyLocally = modules.areMostModulesCopiedLocally(); + auto useGlobalPath = modules.areMostModulesUsingGlobalPath(); + + StringArray missing; + + for (auto missingModule : missingDependencies) + { + if (auto* info = list.getModuleWithID (missingModule)) + modules.addModule (info->moduleFolder, copyLocally, useGlobalPath); + else + missing.add (missingModule); + } + + missingDependencies.swapWith (missing); + return (missingDependencies.size() == 0); + } + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MissingDependenciesComponent) }; }; @@ -370,7 +454,8 @@ public: for (int i = 0; i < modules.size(); ++i) project.getModules().addModule (modules.getReference(i).moduleFolder, - project.getModules().areMostModulesCopiedLocally()); + project.getModules().areMostModulesCopiedLocally(), + project.getModules().areMostModulesUsingGlobalPath()); } void addSubItems() override @@ -381,12 +466,36 @@ public: void showPopupMenu() override { - PopupMenu menu, knownModules, copyModeMenu; + auto& modules = project.getModules(); + PopupMenu knownModules, jucePathModules, userPathModules, exporterPathsModules; - const StringArray modules (getAvailableModules()); - for (int i = 0; i < modules.size(); ++i) - knownModules.addItem (1 + i, modules[i], ! project.getModules().isModuleEnabled (modules[i])); + auto index = 100; + auto globalJucePathModules = getAvailableModulesInGlobalJucePath(); + for (auto m : globalJucePathModules) + jucePathModules.addItem (index++, m, ! modules.isModuleEnabled (m)); + + knownModules.addSubMenu ("Global JUCE modules path", jucePathModules); + + index = 200; + auto globalUserPathModules = getAvailableModulesInGlobalUserPath(); + for (auto m : getAvailableModulesInGlobalUserPath()) + { + if (! globalJucePathModules.contains (m)) + userPathModules.addItem (index++, m, ! modules.isModuleEnabled (m)); + } + knownModules.addSubMenu ("Global user modules path", userPathModules); + + index = 300; + for (auto m : getAvailableModulesInExporterPaths()) + { + if (! globalJucePathModules.contains (m) && ! globalUserPathModules.contains (m)) + exporterPathsModules.addItem (index++, m, ! modules.isModuleEnabled (m)); + } + + knownModules.addSubMenu ("Exporter paths", exporterPathsModules); + + PopupMenu menu; menu.addSubMenu ("Add a module", knownModules); menu.addSeparator(); menu.addItem (1001, "Add a module from a specified folder..."); @@ -396,16 +505,51 @@ public: void handlePopupMenuResult (int resultCode) override { + auto& modules = project.getModules(); + if (resultCode == 1001) - project.getModules().addModuleFromUserSelectedFile(); + { + modules.addModuleFromUserSelectedFile(); + } else if (resultCode > 0) - project.getModules().addModuleInteractive (getAvailableModules() [resultCode - 1]); + { + if (resultCode < 200) + modules.addModuleInteractive (getAvailableModulesInGlobalJucePath() [resultCode - 100]); + else if (resultCode < 300) + modules.addModuleInteractive (getAvailableModulesInGlobalUserPath() [resultCode - 200]); + else if (resultCode < 400) + modules.addModuleInteractive (getAvailableModulesInExporterPaths() [resultCode - 300]); + } + } + + StringArray getAvailableModulesInExporterPaths() + { + ModuleList list; + list.scanProjectExporterModulePaths (project); + + return list.getIDs(); + } + + StringArray getAvailableModulesInGlobalJucePath() + { + ModuleList list; + list.addAllModulesInFolder ({ getAppSettings().getStoredPath (Ids::defaultJuceModulePath).toString() }); + + return list.getIDs(); } - StringArray getAvailableModules() + StringArray getAvailableModulesInGlobalUserPath() { ModuleList list; - list.scanAllKnownFolders (project); + auto paths = StringArray::fromTokens (getAppSettings().getStoredPath (Ids::defaultUserModulePath).toString(), ";", {}); + + for (auto p : paths) + { + auto f = File::createFileWithoutCheckingPath (p.trim()); + if (f.exists()) + list.addAllModulesInFolder (f); + } + return list.getIDs(); } diff --git a/extras/Projucer/Source/Project/jucer_DependencyPathPropertyComponent.cpp b/extras/Projucer/Source/Project/jucer_DependencyPathPropertyComponent.cpp index 5dc2f9a675..e5e4433502 100644 --- a/extras/Projucer/Source/Project/jucer_DependencyPathPropertyComponent.cpp +++ b/extras/Projucer/Source/Project/jucer_DependencyPathPropertyComponent.cpp @@ -26,8 +26,6 @@ #include "../jucer_Headers.h" #include "jucer_DependencyPathPropertyComponent.h" -#include "../Application/jucer_GlobalPreferences.h" - //============================================================================== DependencyPathValueSource::DependencyPathValueSource (const Value& projectSettingsPath, @@ -36,10 +34,11 @@ DependencyPathValueSource::DependencyPathValueSource (const Value& projectSettin : projectSettingsValue (projectSettingsPath), globalKey (globalSettingsKey), os (osThisSettingAppliesTo), - globalSettingsValue (getAppSettings().getGlobalPath (globalKey, os)), - fallbackValue (getAppSettings().getFallbackPath (globalKey, os)) + globalSettingsValue (getAppSettings().getStoredPath (globalKey)), + fallbackValue (getAppSettings().getFallbackPathForOS (globalKey, os)) { globalSettingsValue.addListener (this); + fallbackValue.addListener (this); } bool DependencyPathValueSource::isValidPath (const File& relativeTo) const @@ -135,3 +134,153 @@ void DependencyPathPropertyComponent::lookAndFeelChanged() { textWasEdited(); } + +//============================================================================== +DependencyFilePathPropertyComponent::DependencyFilePathPropertyComponent (Value& value, + const String& propertyDescription, + bool isDir, + const String& wc, + const File& rootToUseForRelativePaths) +try : TextPropertyComponent (propertyDescription, 1024, false), + pathRelativeTo (rootToUseForRelativePaths), + pathValue (value), + pathValueSource (dynamic_cast (pathValue.getValueSource())), + browseButton ("..."), + isDirectory (isDir), + wildcards (wc) +{ + auto initialValueIsEmpty = ! pathValueSource.isUsingProjectSettings(); + + getValue().referTo (pathValue); + + if (initialValueIsEmpty) + getValue().setValue (String()); + + getValue().addListener (this); + + if (auto* label = dynamic_cast (getChildComponent (0))) + label->addListener (this); + else + jassertfalse; + + setInterestedInFileDrag (false); + + addAndMakeVisible (browseButton); + browseButton.addListener (this); + + lookAndFeelChanged(); +} +catch (const std::bad_cast&) +{ + // a DependencyPathPropertyComponent must be initialised with a Value + // that is referring to a DependencyPathValueSource! + jassertfalse; + throw; +} + +void DependencyFilePathPropertyComponent::resized() +{ + auto bounds = getLookAndFeel().getPropertyComponentContentPosition (*this); + + browseButton.setBounds (bounds.removeFromRight (30)); + getChildComponent (0)->setBounds (bounds); +} + +void DependencyFilePathPropertyComponent::paintOverChildren (Graphics& g) +{ + if (highlightForDragAndDrop) + { + g.setColour (findColour (defaultHighlightColourId).withAlpha (0.5f)); + g.fillRect (getChildComponent (0)->getBounds()); + } +} + +void DependencyFilePathPropertyComponent::filesDropped (const StringArray& files, int, int) +{ + const File firstFile (files[0]); + + if (isDirectory) + setTo (firstFile.isDirectory() ? firstFile + : firstFile.getParentDirectory()); + else + setTo (firstFile); + + highlightForDragAndDrop = false; +} + +void DependencyFilePathPropertyComponent::setTo (const File& f) +{ + pathValue = (pathRelativeTo == File()) ? f.getFullPathName() + : f.getRelativePathFrom (pathRelativeTo); + + textWasEdited(); +} + +void DependencyFilePathPropertyComponent::enablementChanged() +{ + getValue().referTo (isEnabled() ? pathValue + : pathValueSource.appliesToThisOS() ? pathValueSource.getGlobalSettingsValue() + : pathValueSource.getFallbackSettingsValue()); + textWasEdited(); + repaint(); +} + +void DependencyFilePathPropertyComponent::textWasEdited() +{ + setColour (textColourId, getTextColourToDisplay()); + TextPropertyComponent::textWasEdited(); +} + +void DependencyFilePathPropertyComponent::valueChanged (Value& value) +{ + if ((value.refersToSameSourceAs (pathValue) && pathValueSource.isUsingGlobalSettings()) + || value.refersToSameSourceAs (pathValueSource.getGlobalSettingsValue())) + textWasEdited(); +} + +void DependencyFilePathPropertyComponent::editorShown (Label*, TextEditor& editor) +{ + if (! pathValueSource.isUsingProjectSettings()) + editor.setText (String(), dontSendNotification); +} + +void DependencyFilePathPropertyComponent::buttonClicked (Button*) +{ + auto currentFile = pathRelativeTo.getChildFile (pathValue.toString()); + + if (isDirectory) + { + FileChooser chooser ("Select directory", currentFile); + + if (chooser.browseForDirectory()) + setTo (chooser.getResult()); + } + else + { + FileChooser chooser ("Select file", currentFile, wildcards); + + if (chooser.browseForFileToOpen()) + setTo (chooser.getResult()); + } +} + +Colour DependencyFilePathPropertyComponent::getTextColourToDisplay() const +{ + auto alpha = 1.0f; + auto key = pathValueSource.getKey(); + const auto& globalSettingsValue = pathValueSource.getGlobalSettingsValue(); + + if (! pathValueSource.isUsingProjectSettings() && isEnabled()) + alpha = 0.5f; + + if ((key == Ids::defaultUserModulePath && getValue().toString().contains (";")) || ! pathValueSource.appliesToThisOS()) + return findColour (widgetTextColourId).withMultipliedAlpha (alpha); + + auto usingGlobalPath = (getValue().refersToSameSourceAs (globalSettingsValue)); + + auto isValidPath = getAppSettings().isGlobalPathValid (pathRelativeTo, key, + (usingGlobalPath ? globalSettingsValue : pathValue).toString()); + + return isValidPath ? findColour (widgetTextColourId).withMultipliedAlpha (alpha) + : Colours::red.withMultipliedAlpha (alpha); +} diff --git a/extras/Projucer/Source/Project/jucer_DependencyPathPropertyComponent.h b/extras/Projucer/Source/Project/jucer_DependencyPathPropertyComponent.h index dfbba807ea..8d935bd573 100644 --- a/extras/Projucer/Source/Project/jucer_DependencyPathPropertyComponent.h +++ b/extras/Projucer/Source/Project/jucer_DependencyPathPropertyComponent.h @@ -52,7 +52,7 @@ public: if (isUsingGlobalSettings()) return globalSettingsValue.getValue(); - return fallbackValue; + return fallbackValue.getValue(); } void setValue (const var& newValue) override @@ -75,7 +75,7 @@ public: bool isUsingFallbackValue() const { - return ! projectSettingsValueIsValid() && !globalSettingsValueIsValid(); + return ! projectSettingsValueIsValid() && ! globalSettingsValueIsValid(); } bool appliesToThisOS() const @@ -87,10 +87,16 @@ public: bool isValidPath() const; + Identifier getKey() { return globalKey; } + + Value getGlobalSettingsValue() { return globalSettingsValue; } + Value getFallbackSettingsValue() { return fallbackValue; } + private: void valueChanged (Value& value) override { - if ((value.refersToSameSourceAs (globalSettingsValue) && isUsingGlobalSettings())) + if ((value.refersToSameSourceAs (globalSettingsValue) && isUsingGlobalSettings()) + || (value.refersToSameSourceAs (fallbackValue) && isUsingFallbackValue())) { sendChangeMessage (true); setValue (String()); // make sure that the project-specific value is still blank @@ -136,7 +142,7 @@ private: /** the dependency path fallback setting. used instead of the global setting whenever the latter doesn't apply, e.g. the setting is for another OS than the ome this machine is running. */ - String fallbackValue; + Value fallbackValue; }; @@ -181,3 +187,61 @@ private: JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DependencyPathPropertyComponent) }; + +//============================================================================== +class DependencyFilePathPropertyComponent : public TextPropertyComponent, + public FileDragAndDropTarget, + private Value::Listener, + private Label::Listener, + private Button::Listener +{ +public: + DependencyFilePathPropertyComponent (Value& value, + const String& propertyDescription, + bool isDirectory, + const String& wildcards = "*", + const File& rootToUseForRelativePaths = File()); + + void resized() override; + void paintOverChildren (Graphics& g) override; + + bool isInterestedInFileDrag (const StringArray&) override { return isEnabled(); } + void fileDragEnter (const StringArray&, int, int) override { highlightForDragAndDrop = true; repaint(); } + void fileDragExit (const StringArray&) override { highlightForDragAndDrop = false; repaint(); } + void filesDropped (const StringArray&, int, int) override; + + void setTo (const File& f); + + void enablementChanged() override; + +private: + void textWasEdited() override; + + void valueChanged (Value&) override; + + void labelTextChanged (Label*) override {} + void editorHidden (Label*, TextEditor&) override {} + void editorShown (Label*, TextEditor&) override; + + void buttonClicked (Button*) override; + + void lookAndFeelChanged() override + { + browseButton.setColour (TextButton::buttonColourId, + findColour (secondaryButtonBackgroundColourId)); + textWasEdited(); + } + + Colour getTextColourToDisplay() const; + + //========================================================================== + File pathRelativeTo; + Value pathValue; + DependencyPathValueSource& pathValueSource; + + TextButton browseButton; + bool isDirectory, highlightForDragAndDrop = false; + String wildcards; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DependencyFilePathPropertyComponent) +}; diff --git a/extras/Projucer/Source/Project/jucer_Module.cpp b/extras/Projucer/Source/Project/jucer_Module.cpp index 777ecab675..dad6bd3da1 100644 --- a/extras/Projucer/Source/Project/jucer_Module.cpp +++ b/extras/Projucer/Source/Project/jucer_Module.cpp @@ -209,15 +209,23 @@ Result ModuleList::addAllModulesInSubfoldersRecursively (const File& path, int d return Result::ok(); } -static Array getAllPossibleModulePaths (Project& project) +static Array getAllPossibleModulePathsFromExporters (Project& project) { StringArray paths; for (Project::ExporterIterator exporter (project); exporter.next();) { - for (int i = 0; i < project.getModules().getNumModules(); ++i) + auto& modules = project.getModules(); + auto n = modules.getNumModules(); + + for (int i = 0; i < n; ++i) { - const String path (exporter->getPathForModuleString (project.getModules().getModuleID (i))); + auto id = modules.getModuleID (i); + + if (modules.shouldUseGlobalPath (id).getValue()) + continue; + + const auto path = exporter->getPathForModuleString (id); if (path.isNotEmpty()) paths.addIfNotAlreadyThere (path); @@ -237,7 +245,7 @@ static Array getAllPossibleModulePaths (Project& project) if (f.isDirectory()) { - files.add (f); + files.addIfNotAlreadyThere (f); if (f.getChildFile ("modules").isDirectory()) files.addIfNotAlreadyThere (f.getChildFile ("modules")); @@ -247,12 +255,12 @@ static Array getAllPossibleModulePaths (Project& project) return files; } -Result ModuleList::scanAllKnownFolders (Project& project) +Result ModuleList::scanProjectExporterModulePaths (Project& project) { modules.clear(); Result result (Result::ok()); - for (auto& m : getAllPossibleModulePaths (project)) + for (auto& m : getAllPossibleModulePathsFromExporters (project)) { result = addAllModulesInFolder (m); @@ -264,6 +272,36 @@ Result ModuleList::scanAllKnownFolders (Project& project) return result; } +void ModuleList::scanGlobalJuceModulePath() +{ + modules.clear(); + + auto& settings = getAppSettings(); + + auto path = settings.getStoredPath (Ids::defaultJuceModulePath).toString(); + + if (path.isNotEmpty()) + addAllModulesInFolder ({ path }); + + sort(); +} + +void ModuleList::scanGlobalUserModulePath() +{ + modules.clear(); + + auto paths = StringArray::fromTokens (getAppSettings().getStoredPath (Ids::defaultUserModulePath).toString(), ";", {}); + + for (auto p : paths) + { + auto f = File::createFileWithoutCheckingPath (p.trim()); + if (f.exists()) + addAllModulesInFolder (f); + } + + sort(); +} + //============================================================================== LibraryModule::LibraryModule (const ModuleDescription& d) : moduleInfo (d) @@ -342,14 +380,16 @@ void LibraryModule::addSettingsForModuleToExporter (ProjectExporter& exporter, P { Array compiled; + auto& modules = project.getModules(); + auto id = getID(); - const File localModuleFolder = project.getModules().shouldCopyModuleFilesLocally (getID()).getValue() - ? project.getLocalModuleFolder (getID()) + const File localModuleFolder = modules.shouldCopyModuleFilesLocally (id).getValue() + ? project.getLocalModuleFolder (id) : moduleInfo.getFolder(); findAndAddCompiledUnits (exporter, &projectSaver, compiled); - if (project.getModules().shouldShowAllModuleFilesInProject (getID()).getValue()) + if (modules.shouldShowAllModuleFilesInProject (id).getValue()) addBrowseableCode (exporter, compiled, localModuleFolder); } @@ -606,35 +646,33 @@ bool EnabledModuleList::isAudioPluginModuleMissing() const && ! isModuleEnabled ("juce_audio_plugin_client"); } +Value EnabledModuleList::shouldUseGlobalPath (const String& moduleID) const +{ + return state.getChildWithProperty (Ids::ID, moduleID) + .getPropertyAsValue (Ids::useGlobalPath, getUndoManager()); +} + Value EnabledModuleList::shouldShowAllModuleFilesInProject (const String& moduleID) { return state.getChildWithProperty (Ids::ID, moduleID) .getPropertyAsValue (Ids::showAllCode, getUndoManager()); } -File EnabledModuleList::findLocalModuleFolder (const String& moduleID, bool useExportersForOtherOSes) +File EnabledModuleList::getModuleFolderFromPathIfItExists (const String& path, const String& moduleID, const Project& project) { - for (Project::ExporterIterator exporter (project); exporter.next();) + if (path.isNotEmpty()) { - if (useExportersForOtherOSes || exporter->mayCompileOnCurrentOS()) - { - auto path = exporter->getPathForModuleString (moduleID); + auto moduleFolder = project.resolveFilename (path); - if (path.isNotEmpty()) - { - auto moduleFolder = project.resolveFilename (path); - - if (moduleFolder.exists()) - { - if (ModuleDescription (moduleFolder).isValid()) - return moduleFolder; + if (moduleFolder.exists()) + { + if (ModuleDescription (moduleFolder).isValid()) + return moduleFolder; - auto f = moduleFolder.getChildFile (moduleID); + auto f = moduleFolder.getChildFile (moduleID); - if (ModuleDescription (f).isValid()) - return f; - } - } + if (ModuleDescription (f).isValid()) + return f; } } @@ -643,12 +681,23 @@ File EnabledModuleList::findLocalModuleFolder (const String& moduleID, bool useE File EnabledModuleList::getModuleFolder (const String& moduleID) { - File f = findLocalModuleFolder (moduleID, false); + if (shouldUseGlobalPath (moduleID).getValue()) + { + if (isJuceModule (moduleID)) + return getModuleFolderFromPathIfItExists (getAppSettings().getStoredPath (Ids::defaultJuceModulePath).toString(), moduleID, project); + + return findUserModuleFolder (moduleID, getAppSettings().getStoredPath (Ids::defaultUserModulePath).toString(), project); + } - if (f == File()) - f = findLocalModuleFolder (moduleID, true); + auto paths = getAllPossibleModulePathsFromExporters (project); + for (auto p : paths) + { + auto f = getModuleFolderFromPathIfItExists (p.getFullPathName(), moduleID, project); + if (f != File()) + return f; + } - return f; + return {}; } struct ModuleTreeSorter @@ -671,7 +720,7 @@ Value EnabledModuleList::shouldCopyModuleFilesLocally (const String& moduleID) c .getPropertyAsValue (Ids::useLocalCopy, getUndoManager()); } -void EnabledModuleList::addModule (const File& moduleFolder, bool copyLocally) +void EnabledModuleList::addModule (const File& moduleFolder, bool copyLocally, bool useGlobalPath) { ModuleDescription info (moduleFolder); @@ -689,6 +738,7 @@ void EnabledModuleList::addModule (const File& moduleFolder, bool copyLocally) shouldShowAllModuleFilesInProject (moduleID) = true; shouldCopyModuleFilesLocally (moduleID) = copyLocally; + shouldUseGlobalPath (moduleID) = useGlobalPath; RelativePath path (moduleFolder.getParentDirectory(), project.getProjectFolder(), RelativePath::projectFolder); @@ -720,7 +770,7 @@ StringArray EnabledModuleList::getAllModules() const StringArray moduleIDs; for (int i = 0; i < getNumModules(); ++i) - moduleIDs.add (getModuleID(i)); + moduleIDs.add (getModuleID (i)); return moduleIDs; } @@ -751,11 +801,26 @@ StringArray EnabledModuleList::getExtraDependenciesNeeded (const String& moduleI return extraDepsNeeded; } +bool EnabledModuleList::areMostModulesUsingGlobalPath() const +{ + auto numYes = 0, numNo = 0; + + for (auto i = getNumModules(); --i >= 0;) + { + if (shouldUseGlobalPath (getModuleID (i)).getValue()) + ++numYes; + else + ++numNo; + } + + return numYes > numNo; +} + bool EnabledModuleList::areMostModulesCopiedLocally() const { - int numYes = 0, numNo = 0; + auto numYes = 0, numNo = 0; - for (int i = getNumModules(); --i >= 0;) + for (auto i = getNumModules(); --i >= 0;) { if (shouldCopyModuleFilesLocally (getModuleID (i)).getValue()) ++numYes; @@ -772,10 +837,26 @@ void EnabledModuleList::setLocalCopyModeForAllModules (bool copyLocally) shouldCopyModuleFilesLocally (project.getModules().getModuleID (i)) = copyLocally; } +File EnabledModuleList::findGlobalModulesFolder() +{ + auto& settings = getAppSettings(); + auto path = settings.getStoredPath (Ids::defaultJuceModulePath).toString(); + + if (settings.isGlobalPathValid (File(), Ids::defaultJuceModulePath, path)) + return { path }; + + return {}; +} + File EnabledModuleList::findDefaultModulesFolder (Project& project) { + auto globalPath = findGlobalModulesFolder(); + + if (globalPath != File()) + return globalPath; + ModuleList available; - available.scanAllKnownFolders (project); + available.scanProjectExporterModulePaths (project); for (int i = available.modules.size(); --i >= 0;) { @@ -788,6 +869,52 @@ File EnabledModuleList::findDefaultModulesFolder (Project& project) return File::getCurrentWorkingDirectory(); } +File EnabledModuleList::findUserModuleFolder (const String& moduleID, const String& possiblePaths, const Project& project) +{ + auto paths = StringArray::fromTokens (possiblePaths, ";", {}); + + for (auto p : paths) + { + auto f = File::createFileWithoutCheckingPath (p.trim()); + if (f.exists()) + { + auto moduleFolder = getModuleFolderFromPathIfItExists (f.getFullPathName(), moduleID, project); + if (moduleFolder != File()) + return moduleFolder; + } + } + + return {}; +} + +bool EnabledModuleList::isJuceModule (const String& moduleID) +{ + static StringArray juceModuleIds = + { + "juce_audio_basics", + "juce_audio_devices", + "juce_audio_formats", + "juce_audio_plugin_client", + "juce_audio_processors", + "juce_audio_utils", + "juce_blocks_basics", + "juce_box2d", + "juce_core", + "juce_cryptography", + "juce_data_structures", + "juce_events", + "juce_graphics", + "juce_gui_basics", + "juce_gui_extra", + "juce_opengl", + "juce_osc", + "juce_product_unlocking", + "juce_video" + }; + + return juceModuleIds.contains (moduleID); +} + void EnabledModuleList::addModuleFromUserSelectedFile() { static File lastLocation (findDefaultModulesFolder (project)); @@ -804,10 +931,24 @@ void EnabledModuleList::addModuleFromUserSelectedFile() void EnabledModuleList::addModuleInteractive (const String& moduleID) { ModuleList list; - list.scanAllKnownFolders (project); + list.scanGlobalJuceModulePath(); + if (auto* info = list.getModuleWithID (moduleID)) + { + addModule (info->moduleFolder, areMostModulesCopiedLocally(), areMostModulesUsingGlobalPath()); + return; + } + + list.scanGlobalUserModulePath(); + if (auto* info = list.getModuleWithID (moduleID)) + { + addModule (info->moduleFolder, areMostModulesCopiedLocally(), areMostModulesUsingGlobalPath()); + return; + } + + list.scanProjectExporterModulePaths (project); if (auto* info = list.getModuleWithID (moduleID)) - addModule (info->moduleFolder, areMostModulesCopiedLocally()); + addModule (info->moduleFolder, areMostModulesCopiedLocally(), false); else addModuleFromUserSelectedFile(); } @@ -830,7 +971,7 @@ void EnabledModuleList::addModuleOfferingToCopy (const File& f) return; } - addModule (m.moduleFolder, areMostModulesCopiedLocally()); + addModule (m.moduleFolder, areMostModulesCopiedLocally(), areMostModulesUsingGlobalPath()); } bool isJuceFolder (const File& f) diff --git a/extras/Projucer/Source/Project/jucer_Module.h b/extras/Projucer/Source/Project/jucer_Module.h index da40406aae..09fd6fc991 100644 --- a/extras/Projucer/Source/Project/jucer_Module.h +++ b/extras/Projucer/Source/Project/jucer_Module.h @@ -31,7 +31,6 @@ class ProjectExporter; class ProjectSaver; //============================================================================== -File findDefaultModulesFolder (bool mustContainJuceCoreModule = true); bool isJuceModulesFolder (const File&); bool isJuceFolder (const File&); @@ -79,7 +78,9 @@ struct ModuleList Result addAllModulesInFolder (const File&); Result addAllModulesInSubfoldersRecursively (const File&, int depth); - Result scanAllKnownFolders (Project&); + Result scanProjectExporterModulePaths (Project&); + void scanGlobalJuceModulePath(); + void scanGlobalUserModulePath(); OwnedArray modules; }; @@ -135,17 +136,24 @@ class EnabledModuleList public: EnabledModuleList (Project&, const ValueTree&); + static File findGlobalModulesFolder(); + static File findDefaultModulesFolder (Project&); + static File findUserModuleFolder (const String& moduleID, const String& possiblePaths, const Project&); + static bool isJuceModule (const String& moduleID); + bool isModuleEnabled (const String& moduleID) const; + + Value shouldUseGlobalPath (const String& moduleID) const; Value shouldShowAllModuleFilesInProject (const String& moduleID); Value shouldCopyModuleFilesLocally (const String& moduleID) const; + void removeModule (String moduleID); bool isAudioPluginModuleMissing() const; ModuleDescription getModuleInfo (const String& moduleID); - File getModuleFolder (const String& moduleID); - void addModule (const File& moduleManifestFile, bool copyLocally); + void addModule (const File& moduleManifestFile, bool copyLocally, bool useGlobalPath); void addModuleInteractive (const String& moduleID); void addModuleFromUserSelectedFile(); void addModuleOfferingToCopy (const File&); @@ -157,11 +165,12 @@ public: int getNumModules() const { return state.getNumChildren(); } String getModuleID (int index) const { return state.getChild (index) [Ids::ID].toString(); } + bool areMostModulesUsingGlobalPath() const; bool areMostModulesCopiedLocally() const; + void setLocalCopyModeForAllModules (bool copyLocally); - void sortAlphabetically(); - static File findDefaultModulesFolder (Project&); + void sortAlphabetically(); Project& project; ValueTree state; @@ -169,7 +178,7 @@ public: private: UndoManager* getUndoManager() const { return project.getUndoManagerFor (state); } - File findLocalModuleFolder (const String& moduleID, bool useExportersForOtherOSes); + static File getModuleFolderFromPathIfItExists (const String& path, const String& moduleID, const Project&); JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (EnabledModuleList) }; diff --git a/extras/Projucer/Source/Project/jucer_ModulesPanel.h b/extras/Projucer/Source/Project/jucer_ModulesPanel.h index 066e2e224d..d4e0bead24 100644 --- a/extras/Projucer/Source/Project/jucer_ModulesPanel.h +++ b/extras/Projucer/Source/Project/jucer_ModulesPanel.h @@ -35,7 +35,8 @@ public: modulesValueTree (p.getModules().state), header ("Modules", Icon (getIcons().modules, Colours::transparentBlack)), setCopyModeButton ("Set copy-mode for all modules..."), - copyPathButton ("Set paths for all modules...") + copyPathButton ("Set paths for all modules..."), + globalPathsButton ("Enable/disable global path for all modules...") { listHeader = new ListBoxHeader ( { "Module", "Version", "Make Local Copy", "Paths" }, { 0.25f, 0.2f, 0.2f, 0.35f } ); @@ -51,10 +52,12 @@ public: addAndMakeVisible (setCopyModeButton); addAndMakeVisible (copyPathButton); + addAndMakeVisible (globalPathsButton); setCopyModeButton.addListener (this); setCopyModeButton.setTriggeredOnMouseDown (true); copyPathButton.addListener (this); copyPathButton.setTriggeredOnMouseDown (true); + globalPathsButton.addListener (this); modulesValueTree.addListener (this); lookAndFeelChanged(); @@ -85,13 +88,15 @@ public: setCopyModeButton.setBounds (buttonRow.removeFromLeft (jmin (200, bounds.getWidth() / 3))); buttonRow.removeFromLeft (8); copyPathButton.setBounds (buttonRow.removeFromLeft (jmin (200, bounds.getWidth() / 3))); + buttonRow.removeFromLeft (8); + globalPathsButton.setBounds (buttonRow.removeFromLeft (jmin (200, bounds.getWidth() / 3))); } } void parentSizeChanged() override { const auto width = jmax (550, getParentWidth()); - auto y = list.getRowPosition (getNumRows() - 1, true).getBottom() + 100; + auto y = list.getRowPosition (getNumRows() - 1, true).getBottom() + 200; y = jmax (getParentHeight(), y); @@ -135,12 +140,21 @@ public: g.drawFittedText (copyLocally, bounds.removeFromLeft (roundToInt (listHeader->getProportionAtIndex (2) * width)), Justification::centredLeft, 1); //====================================================================== - StringArray paths; + String pathText; - for (Project::ExporterIterator exporter (project); exporter.next();) - paths.addIfNotAlreadyThere (exporter->getPathForModuleString (moduleID).trim()); + if (project.getModules().shouldUseGlobalPath (moduleID).getValue()) + { + pathText = "Global"; + } + else + { + StringArray paths; - const auto pathText = paths.joinIntoString (", "); + for (Project::ExporterIterator exporter (project); exporter.next();) + paths.addIfNotAlreadyThere (exporter->getPathForModuleString (moduleID).trim()); + + pathText = paths.joinIntoString (", "); + } g.drawFittedText (pathText, bounds.removeFromLeft (roundToInt (listHeader->getProportionAtIndex (3) * width)), Justification::centredLeft, 1); } @@ -163,12 +177,14 @@ public: { if (b == &setCopyModeButton) showCopyModeMenu(); if (b == ©PathButton) showSetPathsMenu(); + if (b == &globalPathsButton) showGlobalPathsMenu(); } void lookAndFeelChanged() override { setCopyModeButton.setColour (TextButton::buttonColourId, findColour (secondaryButtonBackgroundColourId)); copyPathButton.setColour (TextButton::buttonColourId, findColour (defaultButtonBackgroundColourId)); + globalPathsButton.setColour (TextButton::buttonColourId, findColour (defaultButtonBackgroundColourId)); } private: @@ -185,7 +201,7 @@ private: ContentViewHeader header; ListBox list; ListBoxHeader* listHeader; - TextButton setCopyModeButton, copyPathButton; + TextButton setCopyModeButton, copyPathButton, globalPathsButton; std::map modulePathClipboard; void valueTreePropertyChanged (ValueTree&, const Identifier&) override { itemChanged(); } @@ -207,12 +223,32 @@ private: m.addItem (1, "Set all modules to copy locally"); m.addItem (2, "Set all modules to not copy locally"); - int res = m.showAt (&setCopyModeButton); + auto res = m.showAt (&setCopyModeButton); if (res != 0) project.getModules().setLocalCopyModeForAllModules (res == 1); } + void showGlobalPathsMenu() + { + PopupMenu m; + m.addItem (1, "Set all modules to use global paths"); + m.addItem (2, "Set all modules to not use global paths"); + + auto res = m.showAt (&globalPathsButton); + + if (res != 0) + { + auto enableGlobalPaths = (res == 1); + + auto& modules = project.getModules(); + auto moduleIDs = modules.getAllModules(); + + for (auto id : moduleIDs) + modules.shouldUseGlobalPath (id).setValue (enableGlobalPaths); + } + } + void showSetPathsMenu() { enum diff --git a/extras/Projucer/Source/Project/jucer_Project.cpp b/extras/Projucer/Source/Project/jucer_Project.cpp index 1ad6077f0b..4c5ebfd3f6 100644 --- a/extras/Projucer/Source/Project/jucer_Project.cpp +++ b/extras/Projucer/Source/Project/jucer_Project.cpp @@ -307,20 +307,28 @@ static bool isAnyModuleNewerThanProjucer (const OwnedArray& m void Project::warnAboutOldProjucerVersion() { ModuleList available; - available.scanAllKnownFolders (*this); - if (isAnyModuleNewerThanProjucer (available.modules)) - { - 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!"); - } + 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!"); } //============================================================================== @@ -423,6 +431,11 @@ File Project::resolveFilename (String filename) const filename = replacePreprocessorDefs (getPreprocessorDefs(), filename); + #if ! JUCE_WINDOWS + if (filename.startsWith ("~")) + return File::getSpecialLocation (File::userHomeDirectory).getChildFile (filename.trimCharactersAtStart ("~/")); + #endif + if (FileHelpers::isAbsolutePath (filename)) return File::createFileWithoutCheckingPath (FileHelpers::currentOSStylePath (filename)); // (avoid assertions for windows-style paths) diff --git a/extras/Projucer/Source/Project/jucer_TreeItemTypes.h b/extras/Projucer/Source/Project/jucer_TreeItemTypes.h index 735c666efa..662c2f682c 100644 --- a/extras/Projucer/Source/Project/jucer_TreeItemTypes.h +++ b/extras/Projucer/Source/Project/jucer_TreeItemTypes.h @@ -32,6 +32,7 @@ #include "../Wizards/jucer_NewFileWizard.h" #include "jucer_GroupInformationComponent.h" #include "jucer_ModulesPanel.h" +#include "jucer_DependencyPathPropertyComponent.h" struct FileTreeItemTypes diff --git a/extras/Projucer/Source/Utility/jucer_EditorColourSchemeWindowComponent.h b/extras/Projucer/Source/Utility/jucer_EditorColourSchemeWindowComponent.h new file mode 100644 index 0000000000..0a159a84b3 --- /dev/null +++ b/extras/Projucer/Source/Utility/jucer_EditorColourSchemeWindowComponent.h @@ -0,0 +1,344 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2017 - ROLI Ltd. + + JUCE is an open source library subject to commercial or open-source + licensing. + + By using JUCE, you agree to the terms of both the JUCE 5 End-User License + Agreement and JUCE 5 Privacy Policy (both updated and effective as of the + 27th April 2017). + + End User License Agreement: www.juce.com/juce-5-licence + Privacy Policy: www.juce.com/juce-5-privacy-policy + + Or: You may also use this code under the terms of the GPL v3 (see + www.gnu.org/licenses). + + JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER + EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE + DISCLAIMED. + + ============================================================================== +*/ + +#pragma once + +#include "../Utility/jucer_ColourPropertyComponent.h" + +class EditorColourSchemeWindowComponent : public Component +{ +public: + EditorColourSchemeWindowComponent() + { + if (getAppSettings().monospacedFontNames.size() == 0) + content = new AppearanceEditor::FontScanPanel(); + else + content = new AppearanceEditor::EditorPanel(); + + changeContent (content); + } + + void paint (Graphics& g) override + { + g.fillAll (findColour (backgroundColourId)); + } + + void resized() override + { + content->setBounds (getLocalBounds()); + } + + void changeContent (Component* newContent) + { + content = newContent; + addAndMakeVisible (content); + content->setBounds (getLocalBounds().reduced (10)); + } +private: + ScopedPointer content; + + //============================================================================== + struct AppearanceEditor + { + struct FontScanPanel : public Component, + private Timer + { + FontScanPanel() + { + fontsToScan = Font::findAllTypefaceNames(); + startTimer (1); + } + + void paint (Graphics& g) override + { + g.fillAll (findColour (backgroundColourId)); + + g.setFont (14.0f); + g.setColour (findColour (defaultTextColourId)); + g.drawFittedText ("Scanning for fonts..", getLocalBounds(), Justification::centred, 2); + + const auto size = 30; + getLookAndFeel().drawSpinningWaitAnimation (g, Colours::white, (getWidth() - size) / 2, getHeight() / 2 - 50, size, size); + } + + void timerCallback() override + { + repaint(); + + if (fontsToScan.size() == 0) + { + getAppSettings().monospacedFontNames = fontsFound; + + if (auto* owner = findParentComponentOfClass()) + owner->changeContent (new EditorPanel()); + } + else + { + if (isMonospacedTypeface (fontsToScan[0])) + fontsFound.add (fontsToScan[0]); + + fontsToScan.remove (0); + } + } + + // A rather hacky trick to select only the fixed-pitch fonts.. + // This is unfortunately a bit slow, but will work on all platforms. + static bool isMonospacedTypeface (const String& name) + { + const Font font (name, 20.0f, Font::plain); + + const auto width = font.getStringWidth ("...."); + + return width == font.getStringWidth ("WWWW") + && width == font.getStringWidth ("0000") + && width == font.getStringWidth ("1111") + && width == font.getStringWidth ("iiii"); + } + + StringArray fontsToScan, fontsFound; + }; + + //============================================================================== + struct EditorPanel : public Component, + private ButtonListener + { + EditorPanel() + : loadButton ("Load Scheme..."), + saveButton ("Save Scheme...") + { + rebuildProperties(); + addAndMakeVisible (panel); + + addAndMakeVisible (loadButton); + addAndMakeVisible (saveButton); + + loadButton.addListener (this); + saveButton.addListener (this); + + lookAndFeelChanged(); + + saveSchemeState(); + } + + ~EditorPanel() + { + if (hasSchemeBeenModifiedSinceSave()) + saveScheme (true); + } + + void rebuildProperties() + { + auto& scheme = getAppSettings().appearance; + + Array props; + auto fontValue = scheme.getCodeFontValue(); + props.add (FontNameValueSource::createProperty ("Code Editor Font", fontValue)); + props.add (FontSizeValueSource::createProperty ("Font Size", fontValue)); + + const auto colourNames = scheme.getColourNames(); + + for (int i = 0; i < colourNames.size(); ++i) + props.add (new ColourPropertyComponent (nullptr, colourNames[i], + scheme.getColourValue (colourNames[i]), + Colours::white, false)); + + panel.clear(); + panel.addProperties (props); + } + + void resized() override + { + auto r = getLocalBounds(); + panel.setBounds (r.removeFromTop (getHeight() - 28).reduced (10, 2)); + loadButton.setBounds (r.removeFromLeft (getWidth() / 2).reduced (10, 1)); + saveButton.setBounds (r.reduced (10, 1)); + } + + private: + PropertyPanel panel; + TextButton loadButton, saveButton; + + Font codeFont; + Array colourValues; + + void buttonClicked (Button* b) override + { + if (b == &loadButton) + loadScheme(); + else + saveScheme (false); + } + + void saveScheme (bool isExit) + { + FileChooser fc ("Select a file in which to save this colour-scheme...", + getAppSettings().appearance.getSchemesFolder() + .getNonexistentChildFile ("Scheme", AppearanceSettings::getSchemeFileSuffix()), + AppearanceSettings::getSchemeFileWildCard()); + + if (fc.browseForFileToSave (true)) + { + File file (fc.getResult().withFileExtension (AppearanceSettings::getSchemeFileSuffix())); + getAppSettings().appearance.writeToFile (file); + getAppSettings().appearance.refreshPresetSchemeList(); + + saveSchemeState(); + ProjucerApplication::getApp().selectEditorColourSchemeWithName (file.getFileNameWithoutExtension()); + } + else if (isExit) + { + restorePreviousScheme(); + } + } + + void loadScheme() + { + FileChooser fc ("Please select a colour-scheme file to load...", + getAppSettings().appearance.getSchemesFolder(), + AppearanceSettings::getSchemeFileWildCard()); + + if (fc.browseForFileToOpen()) + { + if (getAppSettings().appearance.readFromFile (fc.getResult())) + { + rebuildProperties(); + saveSchemeState(); + } + } + } + + void lookAndFeelChanged() override + { + loadButton.setColour (TextButton::buttonColourId, + findColour (secondaryButtonBackgroundColourId)); + } + + void saveSchemeState() + { + auto& appearance = getAppSettings().appearance; + const auto colourNames = appearance.getColourNames(); + + codeFont = appearance.getCodeFont(); + + colourValues.clear(); + for (int i = 0; i < colourNames.size(); ++i) + colourValues.add (appearance.getColourValue (colourNames[i]).getValue()); + } + + bool hasSchemeBeenModifiedSinceSave() + { + auto& appearance = getAppSettings().appearance; + const auto colourNames = appearance.getColourNames(); + + if (codeFont != appearance.getCodeFont()) + return true; + + for (int i = 0; i < colourNames.size(); ++i) + if (colourValues[i] != appearance.getColourValue (colourNames[i]).getValue()) + return true; + + return false; + } + + void restorePreviousScheme() + { + auto& appearance = getAppSettings().appearance; + const auto colourNames = appearance.getColourNames(); + + appearance.getCodeFontValue().setValue (codeFont.toString()); + + for (int i = 0; i < colourNames.size(); ++i) + appearance.getColourValue (colourNames[i]).setValue (colourValues[i]); + } + + + JUCE_DECLARE_NON_COPYABLE (EditorPanel) + }; + + //============================================================================== + struct FontNameValueSource : public ValueSourceFilter + { + FontNameValueSource (const Value& source) : ValueSourceFilter (source) {} + + var getValue() const override + { + return Font::fromString (sourceValue.toString()).getTypefaceName(); + } + + void setValue (const var& newValue) override + { + auto font = Font::fromString (sourceValue.toString()); + font.setTypefaceName (newValue.toString().isEmpty() ? Font::getDefaultMonospacedFontName() + : newValue.toString()); + sourceValue = font.toString(); + } + + static ChoicePropertyComponent* createProperty (const String& title, const Value& value) + { + auto fontNames = getAppSettings().monospacedFontNames; + + Array values; + values.add (Font::getDefaultMonospacedFontName()); + values.add (var()); + + for (int i = 0; i < fontNames.size(); ++i) + values.add (fontNames[i]); + + StringArray names; + names.add (""); + names.add (String()); + names.addArray (getAppSettings().monospacedFontNames); + + return new ChoicePropertyComponent (Value (new FontNameValueSource (value)), + title, names, values); + } + }; + + //============================================================================== + struct FontSizeValueSource : public ValueSourceFilter + { + FontSizeValueSource (const Value& source) : ValueSourceFilter (source) {} + + var getValue() const override + { + return Font::fromString (sourceValue.toString()).getHeight(); + } + + void setValue (const var& newValue) override + { + sourceValue = Font::fromString (sourceValue.toString()).withHeight (newValue).toString(); + } + + static PropertyComponent* createProperty (const String& title, const Value& value) + { + return new SliderPropertyComponent (Value (new FontSizeValueSource (value)), + title, 5.0, 40.0, 0.1, 0.5); + } + }; + }; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (EditorColourSchemeWindowComponent) +}; diff --git a/extras/Projucer/Source/Utility/jucer_FilePathPropertyComponent.h b/extras/Projucer/Source/Utility/jucer_FilePathPropertyComponent.h index 9d48337aa8..4f102d1d36 100644 --- a/extras/Projucer/Source/Utility/jucer_FilePathPropertyComponent.h +++ b/extras/Projucer/Source/Utility/jucer_FilePathPropertyComponent.h @@ -40,9 +40,10 @@ public: const String& propertyDescription, bool isDirectory, const String& wildcards = "*", - const File& rootToUseForRelativePaths = File()) + const File& rootToUseForRelativePaths = File(), + const bool supportsMultiplePaths = false) : PropertyComponent (propertyDescription), - innerComp (valueToControl, isDirectory, wildcards, rootToUseForRelativePaths) + innerComp (valueToControl, isDirectory, wildcards, rootToUseForRelativePaths, supportsMultiplePaths) { addAndMakeVisible (innerComp); } @@ -52,18 +53,21 @@ public: private: struct InnerComponent : public Component, public FileDragAndDropTarget, - private Button::Listener + private Button::Listener, + private TextEditor::Listener { - InnerComponent (Value v, bool isDir, const String& wc, const File& rt) + InnerComponent (Value v, bool isDir, const String& wc, const File& rt, const bool multiplePaths) : value (v), isDirectory (isDir), highlightForDragAndDrop (false), wildcards (wc), root (rt), - button ("...") + button ("..."), + supportsMultiplePaths (multiplePaths) { addAndMakeVisible (textbox); textbox.getTextValue().referTo (value); + textbox.addListener (this); addAndMakeVisible (button); button.addListener (this); @@ -75,8 +79,8 @@ private: { if (highlightForDragAndDrop) { - g.setColour (Colours::green.withAlpha (0.1f)); - g.fillRect (getLocalBounds()); + g.setColour (findColour (defaultHighlightColourId).withAlpha (0.5f)); + g.fillRect (textbox.getBounds()); } } @@ -101,6 +105,9 @@ private: : firstFile.getParentDirectory()); else setTo (firstFile); + + highlightForDragAndDrop = false; + repaint(); } void buttonClicked (Button*) override @@ -123,18 +130,74 @@ private: } } + void textEditorReturnKeyPressed (TextEditor& editor) override { updateEditorColour (editor); } + void textEditorFocusLost (TextEditor& editor) override { updateEditorColour (editor); } + + void updateEditorColour (TextEditor& editor) + { + if (supportsMultiplePaths) + { + auto paths = StringArray::fromTokens (editor.getTextValue().toString(), ";", {}); + + editor.clear(); + + AttributedString str; + for (auto p : paths) + { + if (root.getChildFile (p.trim()).exists()) editor.setColour (TextEditor::textColourId, findColour (widgetTextColourId)); + else editor.setColour (TextEditor::textColourId, Colours::red); + + editor.insertTextAtCaret (p); + + if (paths.indexOf (p) < paths.size() - 1) + { + editor.setColour (TextEditor::textColourId, findColour (widgetTextColourId)); + editor.insertTextAtCaret (";"); + } + } + + editor.setColour (TextEditor::textColourId, findColour (widgetTextColourId)); + } + else + { + auto pathToCheck = editor.getTextValue().toString(); + + //android SDK/NDK paths + if (pathToCheck.contains ("${user.home}")) + pathToCheck = pathToCheck.replace ("${user.home}", File::getSpecialLocation (File::userHomeDirectory).getFullPathName()); + + #if JUCE_WINDOWS + if (pathToCheck.startsWith ("~")) + pathToCheck = pathToCheck.replace ("~", File::getSpecialLocation (File::userHomeDirectory).getFullPathName()); + #endif + + const auto currentFile = root.getChildFile (pathToCheck); + + if (currentFile.exists()) + editor.applyColourToAllText (findColour (widgetTextColourId)); + else + editor.applyColourToAllText (Colours::red); + } + } + void setTo (const File& f) { - value = (root == File()) ? f.getFullPathName() - : f.getRelativePathFrom (root); + auto pathName = (root == File()) ? f.getFullPathName() + : f.getRelativePathFrom (root); + + if (supportsMultiplePaths && value.toString().isNotEmpty()) + value = value.toString().trimCharactersAtEnd (" ;") + "; " + pathName; + else + value = pathName; + + updateEditorColour (textbox); } void lookAndFeelChanged() override { textbox.setColour (TextEditor::backgroundColourId, findColour (widgetBackgroundColourId)); textbox.setColour (TextEditor::outlineColourId, Colours::transparentBlack); - textbox.setColour (TextEditor::textColourId, findColour (widgetTextColourId)); - textbox.applyFontToAllText (textbox.getFont()); + updateEditorColour (textbox); button.setColour (TextButton::buttonColourId, findColour (secondaryButtonBackgroundColourId)); button.setColour (TextButton::textColourOffId, Colours::white); @@ -146,6 +209,7 @@ private: File root; TextEditor textbox; TextButton button; + bool supportsMultiplePaths; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (InnerComponent) }; diff --git a/extras/Projucer/Source/Utility/jucer_GlobalSearchPathsWindowComponent.h b/extras/Projucer/Source/Utility/jucer_GlobalSearchPathsWindowComponent.h new file mode 100644 index 0000000000..573926d591 --- /dev/null +++ b/extras/Projucer/Source/Utility/jucer_GlobalSearchPathsWindowComponent.h @@ -0,0 +1,200 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2017 - ROLI Ltd. + + JUCE is an open source library subject to commercial or open-source + licensing. + + By using JUCE, you agree to the terms of both the JUCE 5 End-User License + Agreement and JUCE 5 Privacy Policy (both updated and effective as of the + 27th April 2017). + + End User License Agreement: www.juce.com/juce-5-licence + Privacy Policy: www.juce.com/juce-5-privacy-policy + + Or: You may also use this code under the terms of the GPL v3 (see + www.gnu.org/licenses). + + JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER + EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE + DISCLAIMED. + + ============================================================================== +*/ + +#pragma once + +class GlobalSearchPathsWindowComponent : public Component, + private ComboBox::Listener +{ +public: + GlobalSearchPathsWindowComponent() + : modulesLabel ("modulesLabel", "Modules"), + sdksLabel ("sdksLabel", "SDKs") + { + addAndMakeVisible (modulesLabel); + addAndMakeVisible (sdksLabel); + + modulesLabel.setFont (Font (18.0f, Font::FontStyleFlags::bold)); + sdksLabel.setFont (Font (18.0f, Font::FontStyleFlags::bold)); + + modulesLabel.setJustificationType (Justification::centredLeft); + sdksLabel.setJustificationType (Justification::centredLeft); + + addAndMakeVisible (info); + info.setInfoToDisplay ("Use this dropdown to set the global paths for different OSes. " + "\nN.B. These paths are stored locally and will only be used when " + "saving a project on this machine. Other machines will have their own " + "locally stored paths."); + + addAndMakeVisible (osSelector); + osSelector.addItem ("OSX", 1); + osSelector.addItem ("Windows", 2); + osSelector.addItem ("Linux", 3); + + osSelector.addListener (this); + + auto os = TargetOS::getThisOS(); + + if (os == TargetOS::osx) osSelector.setSelectedId (1); + else if (os == TargetOS::windows) osSelector.setSelectedId (2); + else if (os == TargetOS::linux) osSelector.setSelectedId (3); + + updateFilePathPropertyComponents(); + } + + void paint (Graphics& g) override + { + g.fillAll (findColour (backgroundColourId)); + } + + void resized() override + { + auto b = getLocalBounds().reduced (10); + + auto topSlice = b.removeFromTop (25); + osSelector.setSize (200, 25); + osSelector.setCentrePosition (topSlice.getCentre()); + + info.setBounds (osSelector.getBounds().withWidth (osSelector.getHeight()).translated ((osSelector.getWidth() + 5), 0).reduced (2)); + + modulesLabel.setBounds (b.removeFromTop (20)); + b.removeFromTop (20); + + auto i = 0; + for (auto propertyComponent : pathPropertyComponents) + { + propertyComponent->setBounds (b.removeFromTop (propertyComponent->getPreferredHeight())); + b.removeFromTop (5); + + if (i == 1) + { + b.removeFromTop (15); + sdksLabel.setBounds (b.removeFromTop (20)); + b.removeFromTop (20); + } + + ++i; + } + } + +private: + Label modulesLabel, sdksLabel; + OwnedArray pathPropertyComponents; + ComboBox osSelector; + ConfigTreeItemTypes::InfoButton info; + + void comboBoxChanged (ComboBox*) override + { + updateFilePathPropertyComponents(); + } + + void updateFilePathPropertyComponents() + { + pathPropertyComponents.clear(); + + auto thisOS = TargetOS::getThisOS(); + auto selectedOS = TargetOS::unknown; + + switch (osSelector.getSelectedId()) + { + case 1: selectedOS = TargetOS::osx; break; + case 2: selectedOS = TargetOS::windows; break; + case 3: selectedOS = TargetOS::linux; break; + default: break; + } + + auto& settings = getAppSettings(); + + if (selectedOS == thisOS) + { + addAndMakeVisible (pathPropertyComponents.add (new FilePathPropertyComponent (settings.getStoredPath (Ids::defaultJuceModulePath), + "JUCE Modules", true))); + addAndMakeVisible (pathPropertyComponents.add (new FilePathPropertyComponent (settings.getStoredPath (Ids::defaultUserModulePath), + "User Modules", true, {}, {}, true))); + + addAndMakeVisible (pathPropertyComponents.add (new FilePathPropertyComponent (settings.getStoredPath (Ids::vst3Path), + "VST3 SDK", true))); + + if (selectedOS == TargetOS::linux) + { + addAndMakeVisible (pathPropertyComponents.add (new FilePathPropertyComponent (Value(), "RTAS SDK", true))); + pathPropertyComponents.getLast()->setEnabled (false); + + addAndMakeVisible (pathPropertyComponents.add (new FilePathPropertyComponent (Value(), "AAX SDK", true))); + pathPropertyComponents.getLast()->setEnabled (false); + } + else + { + addAndMakeVisible (pathPropertyComponents.add (new FilePathPropertyComponent (settings.getStoredPath (Ids::rtasPath), + "RTAS SDK", true))); + addAndMakeVisible (pathPropertyComponents.add (new FilePathPropertyComponent (settings.getStoredPath (Ids::aaxPath), + "AAX SDK", true))); + } + + addAndMakeVisible (pathPropertyComponents.add (new FilePathPropertyComponent (settings.getStoredPath (Ids::androidSDKPath), + "Android SDK", true))); + addAndMakeVisible (pathPropertyComponents.add (new FilePathPropertyComponent (settings.getStoredPath (Ids::androidNDKPath), + "Android NDK", true))); + } + else + { + auto maxChars = 1024; + + addAndMakeVisible (pathPropertyComponents.add (new TextPropertyComponent (settings.getFallbackPathForOS (Ids::defaultJuceModulePath, selectedOS), + "JUCE Modules", maxChars, false))); + addAndMakeVisible (pathPropertyComponents.add (new TextPropertyComponent (settings.getFallbackPathForOS (Ids::defaultUserModulePath, selectedOS), + "User Modules", maxChars, false))); + + addAndMakeVisible (pathPropertyComponents.add (new TextPropertyComponent (settings.getFallbackPathForOS (Ids::vst3Path, selectedOS), + "VST3 SDK", maxChars, false))); + + if (selectedOS == TargetOS::linux) + { + addAndMakeVisible (pathPropertyComponents.add (new TextPropertyComponent (Value(), "RTAS SDK", maxChars, false))); + pathPropertyComponents.getLast()->setEnabled (false); + + addAndMakeVisible (pathPropertyComponents.add (new TextPropertyComponent (Value(), "AAX SDK", maxChars, false))); + pathPropertyComponents.getLast()->setEnabled (false); + } + else + { + addAndMakeVisible (pathPropertyComponents.add (new TextPropertyComponent (settings.getFallbackPathForOS (Ids::rtasPath, selectedOS), + "RTAS SDK", maxChars, false))); + addAndMakeVisible (pathPropertyComponents.add (new TextPropertyComponent (settings.getFallbackPathForOS (Ids::aaxPath, selectedOS), + "AAX SDK", maxChars, false))); + } + + addAndMakeVisible (pathPropertyComponents.add (new TextPropertyComponent (settings.getFallbackPathForOS (Ids::androidSDKPath, selectedOS), + "Android SDK", maxChars, false))); + addAndMakeVisible (pathPropertyComponents.add (new TextPropertyComponent (settings.getFallbackPathForOS (Ids::androidNDKPath, selectedOS), + "Android NDK", maxChars, false))); + } + + resized(); + } + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (GlobalSearchPathsWindowComponent) +}; diff --git a/extras/Projucer/Source/Utility/jucer_PresetIDs.h b/extras/Projucer/Source/Utility/jucer_PresetIDs.h index 4af6b287bf..3924e702b6 100644 --- a/extras/Projucer/Source/Utility/jucer_PresetIDs.h +++ b/extras/Projucer/Source/Utility/jucer_PresetIDs.h @@ -59,6 +59,11 @@ namespace Ids DECLARE_ID (intermediatesPath); DECLARE_ID (modulePaths); DECLARE_ID (searchpaths); + DECLARE_ID (osxFallback); + DECLARE_ID (windowsFallback); + DECLARE_ID (linuxFallback); + DECLARE_ID (defaultJuceModulePath); + DECLARE_ID (defaultUserModulePath); DECLARE_ID (vst3Folder); DECLARE_ID (rtasFolder); DECLARE_ID (auFolder); @@ -152,6 +157,7 @@ namespace Ids DECLARE_ID (focusOrder); DECLARE_ID (hidden); DECLARE_ID (useStdCall); + DECLARE_ID (useGlobalPath); DECLARE_ID (showAllCode); DECLARE_ID (useLocalCopy); DECLARE_ID (overwriteOnSave); diff --git a/extras/Projucer/Source/Utility/jucer_StoredSettings.cpp b/extras/Projucer/Source/Utility/jucer_StoredSettings.cpp index ef4b6cc09a..9d5c651d67 100644 --- a/extras/Projucer/Source/Utility/jucer_StoredSettings.cpp +++ b/extras/Projucer/Source/Utility/jucer_StoredSettings.cpp @@ -27,7 +27,6 @@ #include "../jucer_Headers.h" #include "jucer_StoredSettings.h" #include "../Application/jucer_Application.h" -#include "../Application/jucer_GlobalPreferences.h" //============================================================================== StoredSettings& getAppSettings() @@ -42,16 +41,20 @@ PropertiesFile& getGlobalProperties() //============================================================================== StoredSettings::StoredSettings() - : appearance (true), projectDefaults ("PROJECT_DEFAULT_SETTINGS") + : appearance (true), + projectDefaults ("PROJECT_DEFAULT_SETTINGS"), + fallbackPaths ("FALLBACK_PATHS") { updateOldProjectSettingsFiles(); reload(); projectDefaults.addListener (this); + fallbackPaths.addListener (this); } StoredSettings::~StoredSettings() { projectDefaults.removeListener (this); + fallbackPaths.removeListener (this); flush(); } @@ -122,10 +125,13 @@ void StoredSettings::reload() propertyFiles.add (createPropsFile ("Projucer", false)); ScopedPointer projectDefaultsXml (propertyFiles.getFirst()->getXmlValue ("PROJECT_DEFAULT_SETTINGS")); - if (projectDefaultsXml != nullptr) projectDefaults = ValueTree::fromXml (*projectDefaultsXml); + ScopedPointer fallbackPathsXml (propertyFiles.getFirst()->getXmlValue ("FALLBACK_PATHS")); + if (fallbackPathsXml != nullptr) + fallbackPaths = ValueTree::fromXml (*fallbackPathsXml); + // recent files... recentFiles.restoreFromString (getGlobalProperties().getValue ("recentFiles")); recentFiles.removeNonExistentFiles(); @@ -228,61 +234,76 @@ void StoredSettings::ColourSelectorWithSwatches::setSwatchColour (int index, con } //============================================================================== -static bool doesSDKPathContainFile (const File& relativeTo, const String& path, const String& fileToCheckFor) -{ - auto actualPath = path.replace ("${user.home}", File::getSpecialLocation (File::userHomeDirectory).getFullPathName()); - return relativeTo.getChildFile (actualPath + "/" + fileToCheckFor).existsAsFile(); -} - -Value StoredSettings::getGlobalPath (const Identifier& key, DependencyPathOS os) +Value StoredSettings::getStoredPath (const Identifier& key) { auto v = projectDefaults.getPropertyAsValue (key, nullptr); if (v.toString().isEmpty()) - { - auto defaultPath = getFallbackPath (key, os); - if (os == TargetOS::getThisOS()) - v = defaultPath; - } + v = getFallbackPathForOS (key, TargetOS::getThisOS()).toString(); return v; } -String StoredSettings::getFallbackPath (const Identifier& key, DependencyPathOS os) +Value StoredSettings::getFallbackPathForOS (const Identifier& key, DependencyPathOS os) { - if (key == Ids::vst3Path) - return os == TargetOS::windows ? "c:\\SDKs\\VST_SDK\\VST3_SDK" - : "~/SDKs/VST_SDK/VST3_SDK"; + auto id = Identifier(); - if (key == Ids::rtasPath) - { - if (os == TargetOS::windows) return "c:\\SDKs\\PT_90_SDK"; - if (os == TargetOS::osx) return "~/SDKs/PT_90_SDK"; + if (os == TargetOS::osx) id = Ids::osxFallback; + else if (os == TargetOS::windows) id = Ids::windowsFallback; + else if (os == TargetOS::linux) id = Ids::linuxFallback; - // no RTAS on this OS! + if (id == Identifier()) jassertfalse; - return {}; - } - if (key == Ids::aaxPath) - { - if (os == TargetOS::windows) return "c:\\SDKs\\AAX"; - if (os == TargetOS::osx) return "~/SDKs/AAX" ; + auto v = fallbackPaths.getOrCreateChildWithName (id, nullptr) + .getPropertyAsValue (key, nullptr); - // no AAX on this OS! - jassertfalse; - return {}; + if (v.toString().isEmpty()) + { + if (key == Ids::defaultJuceModulePath) + { + v = (os == TargetOS::windows ? "C:\\JUCE\\modules" + : "~/JUCE/modules"); + } + else if (key == Ids::defaultUserModulePath) + { + v = (os == TargetOS::windows ? "C:\\modules" + : "~/modules"); + } + else if (key == Ids::vst3Path) + { + v = (os == TargetOS::windows ? "C:\\SDKs\\VST_SDK\\VST3_SDK" + : "~/SDKs/VST_SDK/VST3_SDK"); + } + else if (key == Ids::rtasPath) + { + if (os == TargetOS::windows) v = "C:\\SDKs\\PT_90_SDK"; + else if (os == TargetOS::osx) v = "~/SDKs/PT_90_SDK"; + else jassertfalse; // no RTAS on this OS! + } + else if (key == Ids::aaxPath) + { + if (os == TargetOS::windows) v = "C:\\SDKs\\AAX"; + else if (os == TargetOS::osx) v = "~/SDKs/AAX" ; + else jassertfalse; // no AAX on this OS! + } + else if (key == Ids::androidSDKPath) + { + v = "${user.home}/Library/Android/sdk"; + } + else if (key == Ids::androidNDKPath) + { + v = "${user.home}/Library/Android/sdk/ndk-bundle"; + } } - if (key == Ids::androidSDKPath) - return "${user.home}/Library/Android/sdk"; - - if (key == Ids::androidNDKPath) - return "${user.home}/Library/Android/sdk/ndk-bundle"; + return v; +} - // didn't recognise the key provided! - jassertfalse; - return {}; +static bool doesSDKPathContainFile (const File& relativeTo, const String& path, const String& fileToCheckFor) +{ + 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) @@ -317,6 +338,14 @@ bool StoredSettings::isGlobalPathValid (const File& relativeTo, const Identifier fileToCheckFor = "ndk-depends"; #endif } + else if (key == Ids::defaultJuceModulePath) + { + fileToCheckFor = "juce_core"; + } + else if (key == Ids::defaultUserModulePath) + { + fileToCheckFor = {}; + } else { // didn't recognise the key provided! diff --git a/extras/Projucer/Source/Utility/jucer_StoredSettings.h b/extras/Projucer/Source/Utility/jucer_StoredSettings.h index ba0efa8a51..183b7f7483 100644 --- a/extras/Projucer/Source/Utility/jucer_StoredSettings.h +++ b/extras/Projucer/Source/Utility/jucer_StoredSettings.h @@ -62,23 +62,27 @@ public: //============================================================================== AppearanceSettings appearance; - StringArray monospacedFontNames; //============================================================================== - Value getGlobalPath (const Identifier& key, DependencyPathOS); - String getFallbackPath (const Identifier& key, DependencyPathOS); + Value getStoredPath (const Identifier& key); + Value getFallbackPathForOS (const Identifier& key, DependencyPathOS); bool isGlobalPathValid (const File& relativeTo, const Identifier& key, const String& path); private: OwnedArray propertyFiles; ValueTree projectDefaults; + ValueTree fallbackPaths; - void changed() + void changed (bool isProjectDefaults) { - ScopedPointer data (projectDefaults.createXml()); - propertyFiles.getUnchecked (0)->setValue ("PROJECT_DEFAULT_SETTINGS", data); + ScopedPointer data (isProjectDefaults ? projectDefaults.createXml() + : fallbackPaths.createXml()); + + propertyFiles.getUnchecked (0)->setValue (isProjectDefaults ? "PROJECT_DEFAULT_SETTINGS" + : "FALLBACK_PATHS", + data); } void updateGlobalPreferences(); @@ -91,11 +95,11 @@ private: void updateOldProjectSettingsFiles(); //============================================================================== - void valueTreePropertyChanged (ValueTree&, const Identifier&) override { changed(); } - void valueTreeChildAdded (ValueTree&, ValueTree&) override { changed(); } - void valueTreeChildRemoved (ValueTree&, ValueTree&, int) override { changed(); } - void valueTreeChildOrderChanged (ValueTree&, int, int) override { changed(); } - void valueTreeParentChanged (ValueTree&) override { changed(); } + void valueTreePropertyChanged (ValueTree& vt, const Identifier&) override { changed (vt == projectDefaults); } + void valueTreeChildAdded (ValueTree& vt, ValueTree&) override { changed (vt == projectDefaults); } + void valueTreeChildRemoved (ValueTree& vt, ValueTree&, int) override { changed (vt == projectDefaults); } + void valueTreeChildOrderChanged (ValueTree& vt, int, int) override { changed (vt == projectDefaults); } + void valueTreeParentChanged (ValueTree& vt) override { changed (vt == projectDefaults); } JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (StoredSettings) }; diff --git a/extras/Projucer/Source/Wizards/jucer_NewProjectWizard.h b/extras/Projucer/Source/Wizards/jucer_NewProjectWizard.h index ca2d578abc..ab7b26e2c9 100644 --- a/extras/Projucer/Source/Wizards/jucer_NewProjectWizard.h +++ b/extras/Projucer/Source/Wizards/jucer_NewProjectWizard.h @@ -103,7 +103,8 @@ struct NewProjectWizard //============================================================================== Project* runWizard (WizardComp& wc, const String& projectName, - const File& target) + const File& target, + bool useGlobalPath) { ownerWizardComp = &wc; appTitle = projectName; @@ -140,7 +141,7 @@ struct NewProjectWizard return nullptr; addExporters (*project, wc); - addDefaultModules (*project, false); + addDefaultModules (*project, useGlobalPath); if (project->save (false, true) != FileBasedDocument::savedOk) return nullptr; @@ -173,7 +174,7 @@ struct NewProjectWizard failedFiles.add (getSourceFilesFolder().getFullPathName()); } - void addDefaultModules (Project& project, bool areModulesCopiedLocally) + void addDefaultModules (Project& project, bool useGlobalPath) { StringArray mods (getDefaultModules()); @@ -182,7 +183,7 @@ struct NewProjectWizard for (int i = 0; i < mods.size(); ++i) if (const ModuleDescription* info = list.getModuleWithID (mods[i])) - project.getModules().addModule (info->moduleFolder, areModulesCopiedLocally); + project.getModules().addModule (info->moduleFolder, false, useGlobalPath); } void addExporters (Project& project, WizardComp& wizardComp) diff --git a/extras/Projucer/Source/Wizards/jucer_NewProjectWizardComponent.h b/extras/Projucer/Source/Wizards/jucer_NewProjectWizardComponent.h index 5667c57e24..acc01bd3fd 100644 --- a/extras/Projucer/Source/Wizards/jucer_NewProjectWizardComponent.h +++ b/extras/Projucer/Source/Wizards/jucer_NewProjectWizardComponent.h @@ -35,10 +35,11 @@ public: ModulesFolderPathBox (File initialFileOrDirectory) : currentPathBox ("currentPathBox"), openFolderButton (TRANS("...")), - modulesLabel (String(), TRANS("Modules Folder") + ":") + modulesLabel (String(), TRANS("Modules Folder") + ":"), + useGlobalPathsToggle ("Use global module path") { if (initialFileOrDirectory == File()) - initialFileOrDirectory = findDefaultModulesFolder(); + initialFileOrDirectory = EnabledModuleList::findGlobalModulesFolder(); setModulesFolder (initialFileOrDirectory); @@ -52,15 +53,24 @@ public: addAndMakeVisible (modulesLabel); modulesLabel.attachToComponent (¤tPathBox, true); + + addAndMakeVisible (useGlobalPathsToggle); + useGlobalPathsToggle.addListener (this); + useGlobalPathsToggle.setToggleState (true, sendNotification); } void resized() override { - auto r = getLocalBounds(); + auto b = getLocalBounds(); + + auto topSlice = b.removeFromTop (b.getHeight() / 2); + + openFolderButton.setBounds (topSlice.removeFromRight (30)); + modulesLabel.setBounds (topSlice.removeFromLeft (110)); + currentPathBox.setBounds (topSlice); - openFolderButton.setBounds (r.removeFromRight (30)); - modulesLabel.setBounds (r.removeFromLeft (110)); - currentPathBox.setBounds (r); + b.removeFromTop (5); + useGlobalPathsToggle.setBounds (b.translated (20, 0)); } static bool selectJuceFolder (File& result) @@ -68,7 +78,7 @@ public: for (;;) { FileChooser fc ("Select your JUCE modules folder...", - findDefaultModulesFolder(), + EnabledModuleList::findGlobalModulesFolder(), "*"); if (! fc.browseForDirectory()) @@ -104,9 +114,21 @@ public: } } - void buttonClicked (Button*) override + void buttonClicked (Button* b) override { - selectJuceFolder(); + if (b == &openFolderButton) + { + selectJuceFolder(); + } + else if (b == &useGlobalPathsToggle) + { + isUsingGlobalPaths = useGlobalPathsToggle.getToggleState(); + + currentPathBox.setEnabled (! isUsingGlobalPaths); + openFolderButton.setEnabled (! isUsingGlobalPaths); + modulesLabel.setEnabled (! isUsingGlobalPaths); + } + } void comboBoxChanged (ComboBox*) override @@ -115,11 +137,13 @@ public: } File modulesFolder; + bool isUsingGlobalPaths; private: ComboBox currentPathBox; TextButton openFolderButton; Label modulesLabel; + ToggleButton useGlobalPathsToggle; }; @@ -280,7 +304,7 @@ public: WizardComp() : platformTargets(), projectName (TRANS("Project name")), - modulesPathBox (findDefaultModulesFolder()) + modulesPathBox (EnabledModuleList::findGlobalModulesFolder()) { setOpaque (false); @@ -355,7 +379,7 @@ public: filesToCreate.setBounds (right.removeFromTop (22).withTrimmedLeft (150)); right.removeFromTop (20); - modulesPathBox.setBounds (right.removeFromTop (22)); + modulesPathBox.setBounds (right.removeFromTop (50)); right.removeFromTop (20); targetsOutline.setBounds (right); @@ -404,14 +428,27 @@ public: return; } - wizard->modulesFolder = modulesPathBox.modulesFolder; + + wizard->modulesFolder = modulesPathBox.isUsingGlobalPaths ? File (getAppSettings().getStoredPath (Ids::defaultJuceModulePath).toString()) + : modulesPathBox.modulesFolder; if (! isJuceModulesFolder (wizard->modulesFolder)) + { + if (modulesPathBox.isUsingGlobalPaths) + AlertWindow::showMessageBox (AlertWindow::AlertIconType::WarningIcon, "Invalid Global Path", + "Your global JUCE module search path is invalid. Please select the folder containing your JUCE modules " + "to set as the default path."); + if (! wizard->selectJuceFolder()) return; + if (modulesPathBox.isUsingGlobalPaths) + getAppSettings().getStoredPath (Ids::defaultJuceModulePath).setValue (wizard->modulesFolder.getFullPathName()); + } + if (ScopedPointer project = wizard->runWizard (*this, projectName.getText(), - fileBrowser.getSelectedFile (0))) + fileBrowser.getSelectedFile (0), + modulesPathBox.isUsingGlobalPaths)) mw->setProject (project.release()); } }