| @@ -100,8 +100,8 @@ OBJECTS_APP := \ | |||||
| $(JUCE_OBJDIR)/jucer_CompileEngineClient_aee8c99c.o \ | $(JUCE_OBJDIR)/jucer_CompileEngineClient_aee8c99c.o \ | ||||
| $(JUCE_OBJDIR)/jucer_CompileEngineServer_5d8914.o \ | $(JUCE_OBJDIR)/jucer_CompileEngineServer_5d8914.o \ | ||||
| $(JUCE_OBJDIR)/jucer_DownloadCompileEngineThread_19bb4bb3.o \ | $(JUCE_OBJDIR)/jucer_DownloadCompileEngineThread_19bb4bb3.o \ | ||||
| $(JUCE_OBJDIR)/jucer_Modules_e20cbd10.o \ | |||||
| $(JUCE_OBJDIR)/jucer_HeaderComponent_1ebf72ba.o \ | $(JUCE_OBJDIR)/jucer_HeaderComponent_1ebf72ba.o \ | ||||
| $(JUCE_OBJDIR)/jucer_Module_3f7666a5.o \ | |||||
| $(JUCE_OBJDIR)/jucer_Project_c131864a.o \ | $(JUCE_OBJDIR)/jucer_Project_c131864a.o \ | ||||
| $(JUCE_OBJDIR)/jucer_ProjectExporter_cf377b25.o \ | $(JUCE_OBJDIR)/jucer_ProjectExporter_cf377b25.o \ | ||||
| $(JUCE_OBJDIR)/jucer_ProjectSaver_4276639b.o \ | $(JUCE_OBJDIR)/jucer_ProjectSaver_4276639b.o \ | ||||
| @@ -302,14 +302,14 @@ $(JUCE_OBJDIR)/jucer_DownloadCompileEngineThread_19bb4bb3.o: ../../Source/LiveBu | |||||
| @echo "Compiling jucer_DownloadCompileEngineThread.cpp" | @echo "Compiling jucer_DownloadCompileEngineThread.cpp" | ||||
| $(V_AT)$(CXX) $(JUCE_CXXFLAGS) $(JUCE_CPPFLAGS_APP) $(JUCE_CFLAGS_APP) -o "$@" -c "$<" | $(V_AT)$(CXX) $(JUCE_CXXFLAGS) $(JUCE_CPPFLAGS_APP) $(JUCE_CFLAGS_APP) -o "$@" -c "$<" | ||||
| $(JUCE_OBJDIR)/jucer_HeaderComponent_1ebf72ba.o: ../../Source/Project/UI/jucer_HeaderComponent.cpp | |||||
| $(JUCE_OBJDIR)/jucer_Modules_e20cbd10.o: ../../Source/Project/Modules/jucer_Modules.cpp | |||||
| -$(V_AT)mkdir -p $(JUCE_OBJDIR) | -$(V_AT)mkdir -p $(JUCE_OBJDIR) | ||||
| @echo "Compiling jucer_HeaderComponent.cpp" | |||||
| @echo "Compiling jucer_Modules.cpp" | |||||
| $(V_AT)$(CXX) $(JUCE_CXXFLAGS) $(JUCE_CPPFLAGS_APP) $(JUCE_CFLAGS_APP) -o "$@" -c "$<" | $(V_AT)$(CXX) $(JUCE_CXXFLAGS) $(JUCE_CPPFLAGS_APP) $(JUCE_CFLAGS_APP) -o "$@" -c "$<" | ||||
| $(JUCE_OBJDIR)/jucer_Module_3f7666a5.o: ../../Source/Project/jucer_Module.cpp | |||||
| $(JUCE_OBJDIR)/jucer_HeaderComponent_1ebf72ba.o: ../../Source/Project/UI/jucer_HeaderComponent.cpp | |||||
| -$(V_AT)mkdir -p $(JUCE_OBJDIR) | -$(V_AT)mkdir -p $(JUCE_OBJDIR) | ||||
| @echo "Compiling jucer_Module.cpp" | |||||
| @echo "Compiling jucer_HeaderComponent.cpp" | |||||
| $(V_AT)$(CXX) $(JUCE_CXXFLAGS) $(JUCE_CPPFLAGS_APP) $(JUCE_CFLAGS_APP) -o "$@" -c "$<" | $(V_AT)$(CXX) $(JUCE_CXXFLAGS) $(JUCE_CPPFLAGS_APP) $(JUCE_CFLAGS_APP) -o "$@" -c "$<" | ||||
| $(JUCE_OBJDIR)/jucer_Project_c131864a.o: ../../Source/Project/jucer_Project.cpp | $(JUCE_OBJDIR)/jucer_Project_c131864a.o: ../../Source/Project/jucer_Project.cpp | ||||
| @@ -205,13 +205,13 @@ | |||||
| isa = PBXBuildFile; | isa = PBXBuildFile; | ||||
| fileRef = ADA538034910F52FDD2DC88D; | fileRef = ADA538034910F52FDD2DC88D; | ||||
| }; | }; | ||||
| 05A08E366EBF8D650974E695 = { | |||||
| 0E783907C6214ADD59EC95DC = { | |||||
| isa = PBXBuildFile; | isa = PBXBuildFile; | ||||
| fileRef = 516D6D7C564DD5DF5C15CB06; | |||||
| fileRef = F58B23995765C9FDBE28F871; | |||||
| }; | }; | ||||
| 3FCA61C401007B243E2E9035 = { | |||||
| 05A08E366EBF8D650974E695 = { | |||||
| isa = PBXBuildFile; | isa = PBXBuildFile; | ||||
| fileRef = F797071D88542C813CF7222A; | |||||
| fileRef = 516D6D7C564DD5DF5C15CB06; | |||||
| }; | }; | ||||
| 30B921C38DCEE787B294B746 = { | 30B921C38DCEE787B294B746 = { | ||||
| isa = PBXBuildFile; | isa = PBXBuildFile; | ||||
| @@ -548,13 +548,6 @@ | |||||
| path = "../../Source/Utility/PIPs/jucer_PIPGenerator.cpp"; | path = "../../Source/Utility/PIPs/jucer_PIPGenerator.cpp"; | ||||
| sourceTree = "SOURCE_ROOT"; | sourceTree = "SOURCE_ROOT"; | ||||
| }; | }; | ||||
| 194457D806A26E793584AC0C = { | |||||
| isa = PBXFileReference; | |||||
| lastKnownFileType = file.svg; | |||||
| name = "huckleberry_icon.svg"; | |||||
| path = "../../Source/BinaryData/Icons/huckleberry_icon.svg"; | |||||
| sourceTree = "SOURCE_ROOT"; | |||||
| }; | |||||
| 1B0F18E1D96F727C062B05FA = { | 1B0F18E1D96F727C062B05FA = { | ||||
| isa = PBXFileReference; | isa = PBXFileReference; | ||||
| lastKnownFileType = sourcecode.cpp.cpp; | lastKnownFileType = sourcecode.cpp.cpp; | ||||
| @@ -660,6 +653,13 @@ | |||||
| path = "../../Source/LiveBuildEngine/jucer_ActivityList.h"; | path = "../../Source/LiveBuildEngine/jucer_ActivityList.h"; | ||||
| sourceTree = "SOURCE_ROOT"; | sourceTree = "SOURCE_ROOT"; | ||||
| }; | }; | ||||
| 247768B490B9D759DDA79359 = { | |||||
| isa = PBXFileReference; | |||||
| lastKnownFileType = sourcecode.c.h; | |||||
| name = "jucer_UserAvatarComponent.h"; | |||||
| path = "../../Source/Project/UI/jucer_UserAvatarComponent.h"; | |||||
| sourceTree = "SOURCE_ROOT"; | |||||
| }; | |||||
| 24EB4C2412821B8019D6F754 = { | 24EB4C2412821B8019D6F754 = { | ||||
| isa = PBXFileReference; | isa = PBXFileReference; | ||||
| lastKnownFileType = sourcecode.cpp.cpp; | lastKnownFileType = sourcecode.cpp.cpp; | ||||
| @@ -709,6 +709,13 @@ | |||||
| path = "../../Source/Application/jucer_CommandLine.h"; | path = "../../Source/Application/jucer_CommandLine.h"; | ||||
| sourceTree = "SOURCE_ROOT"; | sourceTree = "SOURCE_ROOT"; | ||||
| }; | }; | ||||
| 2F0A7CA808B2FCCC9ED68992 = { | |||||
| isa = PBXFileReference; | |||||
| lastKnownFileType = sourcecode.c.h; | |||||
| name = "jucer_LicenseQueryThread.h"; | |||||
| path = "../../Source/Application/UserAccount/jucer_LicenseQueryThread.h"; | |||||
| sourceTree = "SOURCE_ROOT"; | |||||
| }; | |||||
| 2F373F97E30AC1A0BFC1FC61 = { | 2F373F97E30AC1A0BFC1FC61 = { | ||||
| isa = PBXFileReference; | isa = PBXFileReference; | ||||
| lastKnownFileType = sourcecode.c.h; | lastKnownFileType = sourcecode.c.h; | ||||
| @@ -1094,6 +1101,13 @@ | |||||
| path = "../../Source/ComponentEditor/Properties/jucer_ComponentTextProperty.h"; | path = "../../Source/ComponentEditor/Properties/jucer_ComponentTextProperty.h"; | ||||
| sourceTree = "SOURCE_ROOT"; | sourceTree = "SOURCE_ROOT"; | ||||
| }; | }; | ||||
| 582F659B801F656C2B7C51B1 = { | |||||
| isa = PBXFileReference; | |||||
| lastKnownFileType = sourcecode.c.h; | |||||
| name = "jucer_Modules.h"; | |||||
| path = "../../Source/Project/Modules/jucer_Modules.h"; | |||||
| sourceTree = "SOURCE_ROOT"; | |||||
| }; | |||||
| 5867DC4E39DF8539B54C0D59 = { | 5867DC4E39DF8539B54C0D59 = { | ||||
| isa = PBXFileReference; | isa = PBXFileReference; | ||||
| lastKnownFileType = sourcecode.cpp.objcpp; | lastKnownFileType = sourcecode.cpp.objcpp; | ||||
| @@ -1304,13 +1318,6 @@ | |||||
| path = "../../Source/ComponentEditor/UI/jucer_RelativePositionedRectangle.h"; | path = "../../Source/ComponentEditor/UI/jucer_RelativePositionedRectangle.h"; | ||||
| sourceTree = "SOURCE_ROOT"; | sourceTree = "SOURCE_ROOT"; | ||||
| }; | }; | ||||
| 7211101FFA28400ADBB1D47A = { | |||||
| isa = PBXFileReference; | |||||
| lastKnownFileType = sourcecode.c.h; | |||||
| name = "jucer_Module.h"; | |||||
| path = "../../Source/Project/jucer_Module.h"; | |||||
| sourceTree = "SOURCE_ROOT"; | |||||
| }; | |||||
| 728FE25157E9874D50BBECB2 = { | 728FE25157E9874D50BBECB2 = { | ||||
| isa = PBXFileReference; | isa = PBXFileReference; | ||||
| lastKnownFileType = wrapper.framework; | lastKnownFileType = wrapper.framework; | ||||
| @@ -1584,6 +1591,13 @@ | |||||
| path = "../../Source/Project/UI/jucer_ProjectContentComponent.h"; | path = "../../Source/Project/UI/jucer_ProjectContentComponent.h"; | ||||
| sourceTree = "SOURCE_ROOT"; | sourceTree = "SOURCE_ROOT"; | ||||
| }; | }; | ||||
| 94146B40B41BF0AACF4359DD = { | |||||
| isa = PBXFileReference; | |||||
| lastKnownFileType = sourcecode.c.h; | |||||
| name = "jucer_LicenseState.h"; | |||||
| path = "../../Source/Application/UserAccount/jucer_LicenseState.h"; | |||||
| sourceTree = "SOURCE_ROOT"; | |||||
| }; | |||||
| 951128CA33CCDEF570436B1C = { | 951128CA33CCDEF570436B1C = { | ||||
| isa = PBXFileReference; | isa = PBXFileReference; | ||||
| lastKnownFileType = file.icns; | lastKnownFileType = file.icns; | ||||
| @@ -1899,6 +1913,13 @@ | |||||
| path = "../../Source/Utility/Helpers/jucer_FileHelpers.cpp"; | path = "../../Source/Utility/Helpers/jucer_FileHelpers.cpp"; | ||||
| sourceTree = "SOURCE_ROOT"; | sourceTree = "SOURCE_ROOT"; | ||||
| }; | }; | ||||
| B6444A4A8DFD6828FF6BD1CB = { | |||||
| isa = PBXFileReference; | |||||
| lastKnownFileType = sourcecode.c.h; | |||||
| name = "jucer_LoginFormComponent.h"; | |||||
| path = "../../Source/Application/UserAccount/jucer_LoginFormComponent.h"; | |||||
| sourceTree = "SOURCE_ROOT"; | |||||
| }; | |||||
| B6F2905330EA5C560D527209 = { | B6F2905330EA5C560D527209 = { | ||||
| isa = PBXFileReference; | isa = PBXFileReference; | ||||
| lastKnownFileType = file; | lastKnownFileType = file; | ||||
| @@ -2102,6 +2123,13 @@ | |||||
| path = "../../Source/LiveBuildEngine/UI/jucer_BuildTabStatusComponent.h"; | path = "../../Source/LiveBuildEngine/UI/jucer_BuildTabStatusComponent.h"; | ||||
| sourceTree = "SOURCE_ROOT"; | sourceTree = "SOURCE_ROOT"; | ||||
| }; | }; | ||||
| CD267A28C16C4E79EB749005 = { | |||||
| isa = PBXFileReference; | |||||
| lastKnownFileType = file.svg; | |||||
| name = "gpl_logo.svg"; | |||||
| path = "../../Source/BinaryData/Icons/gpl_logo.svg"; | |||||
| sourceTree = "SOURCE_ROOT"; | |||||
| }; | |||||
| CF6C8BD0DA3D8CD4E99EBADA = { | CF6C8BD0DA3D8CD4E99EBADA = { | ||||
| isa = PBXFileReference; | isa = PBXFileReference; | ||||
| lastKnownFileType = wrapper.framework; | lastKnownFileType = wrapper.framework; | ||||
| @@ -2438,6 +2466,13 @@ | |||||
| path = "../../Source/Utility/Helpers/jucer_CodeHelpers.cpp"; | path = "../../Source/Utility/Helpers/jucer_CodeHelpers.cpp"; | ||||
| sourceTree = "SOURCE_ROOT"; | sourceTree = "SOURCE_ROOT"; | ||||
| }; | }; | ||||
| F30DF63DBEFA4BEEF7C369FC = { | |||||
| isa = PBXFileReference; | |||||
| lastKnownFileType = sourcecode.c.h; | |||||
| name = "jucer_LicenseController.h"; | |||||
| path = "../../Source/Application/UserAccount/jucer_LicenseController.h"; | |||||
| sourceTree = "SOURCE_ROOT"; | |||||
| }; | |||||
| F313EE01ECE306DB2CFE011D = { | F313EE01ECE306DB2CFE011D = { | ||||
| isa = PBXFileReference; | isa = PBXFileReference; | ||||
| lastKnownFileType = file.in; | lastKnownFileType = file.in; | ||||
| @@ -2459,6 +2494,13 @@ | |||||
| path = "../../Source/ComponentEditor/Properties/jucer_ComponentBooleanProperty.h"; | path = "../../Source/ComponentEditor/Properties/jucer_ComponentBooleanProperty.h"; | ||||
| sourceTree = "SOURCE_ROOT"; | sourceTree = "SOURCE_ROOT"; | ||||
| }; | }; | ||||
| F58B23995765C9FDBE28F871 = { | |||||
| isa = PBXFileReference; | |||||
| lastKnownFileType = sourcecode.cpp.cpp; | |||||
| name = "jucer_Modules.cpp"; | |||||
| path = "../../Source/Project/Modules/jucer_Modules.cpp"; | |||||
| sourceTree = "SOURCE_ROOT"; | |||||
| }; | |||||
| F5DD97B45B8EA60C1ED0DD80 = { | F5DD97B45B8EA60C1ED0DD80 = { | ||||
| isa = PBXFileReference; | isa = PBXFileReference; | ||||
| lastKnownFileType = sourcecode.cpp.cpp; | lastKnownFileType = sourcecode.cpp.cpp; | ||||
| @@ -2466,11 +2508,11 @@ | |||||
| path = "../../Source/Settings/jucer_StoredSettings.cpp"; | path = "../../Source/Settings/jucer_StoredSettings.cpp"; | ||||
| sourceTree = "SOURCE_ROOT"; | sourceTree = "SOURCE_ROOT"; | ||||
| }; | }; | ||||
| F797071D88542C813CF7222A = { | |||||
| F63F46CA0A51C679867855A7 = { | |||||
| isa = PBXFileReference; | isa = PBXFileReference; | ||||
| lastKnownFileType = sourcecode.cpp.cpp; | |||||
| name = "jucer_Module.cpp"; | |||||
| path = "../../Source/Project/jucer_Module.cpp"; | |||||
| lastKnownFileType = sourcecode.c.h; | |||||
| name = "jucer_ProjectMessagesComponent.h"; | |||||
| path = "../../Source/Project/UI/jucer_ProjectMessagesComponent.h"; | |||||
| sourceTree = "SOURCE_ROOT"; | sourceTree = "SOURCE_ROOT"; | ||||
| }; | }; | ||||
| F7C74E934C954F6F1A3BE4F9 = { | F7C74E934C954F6F1A3BE4F9 = { | ||||
| @@ -2501,6 +2543,13 @@ | |||||
| path = "../../Source/CodeEditor/jucer_OpenDocumentManager.cpp"; | path = "../../Source/CodeEditor/jucer_OpenDocumentManager.cpp"; | ||||
| sourceTree = "SOURCE_ROOT"; | sourceTree = "SOURCE_ROOT"; | ||||
| }; | }; | ||||
| F9A363BFBB6B1143C2E967C3 = { | |||||
| isa = PBXFileReference; | |||||
| lastKnownFileType = sourcecode.c.h; | |||||
| name = "jucer_ModuleDescription.h"; | |||||
| path = "../../Source/Project/Modules/jucer_ModuleDescription.h"; | |||||
| sourceTree = "SOURCE_ROOT"; | |||||
| }; | |||||
| FA790C59A304579F660F112F = { | FA790C59A304579F660F112F = { | ||||
| isa = PBXFileReference; | isa = PBXFileReference; | ||||
| lastKnownFileType = sourcecode.c.h; | lastKnownFileType = sourcecode.c.h; | ||||
| @@ -2522,6 +2571,13 @@ | |||||
| path = "../../Source/ComponentEditor/Properties/jucer_ComponentColourProperty.h"; | path = "../../Source/ComponentEditor/Properties/jucer_ComponentColourProperty.h"; | ||||
| sourceTree = "SOURCE_ROOT"; | sourceTree = "SOURCE_ROOT"; | ||||
| }; | }; | ||||
| FDABEE6B64546586368A4729 = { | |||||
| isa = PBXFileReference; | |||||
| lastKnownFileType = sourcecode.c.h; | |||||
| name = "jucer_AvailableModulesList.h"; | |||||
| path = "../../Source/Project/Modules/jucer_AvailableModulesList.h"; | |||||
| sourceTree = "SOURCE_ROOT"; | |||||
| }; | |||||
| FE20FE5805A02A4843048200 = { | FE20FE5805A02A4843048200 = { | ||||
| isa = PBXFileReference; | isa = PBXFileReference; | ||||
| lastKnownFileType = sourcecode.c.h; | lastKnownFileType = sourcecode.c.h; | ||||
| @@ -2550,6 +2606,17 @@ | |||||
| path = "../../Source/ComponentEditor/UI/jucer_PaintRoutinePanel.cpp"; | path = "../../Source/ComponentEditor/UI/jucer_PaintRoutinePanel.cpp"; | ||||
| sourceTree = "SOURCE_ROOT"; | sourceTree = "SOURCE_ROOT"; | ||||
| }; | }; | ||||
| 9D43579A76E23FBCE6B36333 = { | |||||
| isa = PBXGroup; | |||||
| children = ( | |||||
| F30DF63DBEFA4BEEF7C369FC, | |||||
| 2F0A7CA808B2FCCC9ED68992, | |||||
| 94146B40B41BF0AACF4359DD, | |||||
| B6444A4A8DFD6828FF6BD1CB, | |||||
| ); | |||||
| name = UserAccount; | |||||
| sourceTree = "<group>"; | |||||
| }; | |||||
| EB1D55A76652399EB81CC1F0 = { | EB1D55A76652399EB81CC1F0 = { | ||||
| isa = PBXGroup; | isa = PBXGroup; | ||||
| children = ( | children = ( | ||||
| @@ -2568,6 +2635,7 @@ | |||||
| BC67FD952A6F210A11A1ECB8 = { | BC67FD952A6F210A11A1ECB8 = { | ||||
| isa = PBXGroup; | isa = PBXGroup; | ||||
| children = ( | children = ( | ||||
| 9D43579A76E23FBCE6B36333, | |||||
| EB1D55A76652399EB81CC1F0, | EB1D55A76652399EB81CC1F0, | ||||
| 7CA44FF0BA319517C6E39651, | 7CA44FF0BA319517C6E39651, | ||||
| EE690110171E1648FF2118B8, | EE690110171E1648FF2118B8, | ||||
| @@ -2606,7 +2674,7 @@ | |||||
| 69B478C992FA0B8C885946A6, | 69B478C992FA0B8C885946A6, | ||||
| EAC1731150A7F79D59BAA0B6, | EAC1731150A7F79D59BAA0B6, | ||||
| 8F4D281E98808204E2846A7D, | 8F4D281E98808204E2846A7D, | ||||
| 194457D806A26E793584AC0C, | |||||
| CD267A28C16C4E79EB749005, | |||||
| 432EC251A122071809471804, | 432EC251A122071809471804, | ||||
| B83C9BD89F31EA9E5E12A3C6, | B83C9BD89F31EA9E5E12A3C6, | ||||
| 8FEF6F5EA676B824C021EB6F, | 8FEF6F5EA676B824C021EB6F, | ||||
| @@ -2865,6 +2933,17 @@ | |||||
| name = LiveBuildEngine; | name = LiveBuildEngine; | ||||
| sourceTree = "<group>"; | sourceTree = "<group>"; | ||||
| }; | }; | ||||
| 5108FDF7F62E617332FB13B0 = { | |||||
| isa = PBXGroup; | |||||
| children = ( | |||||
| FDABEE6B64546586368A4729, | |||||
| F9A363BFBB6B1143C2E967C3, | |||||
| F58B23995765C9FDBE28F871, | |||||
| 582F659B801F656C2B7C51B1, | |||||
| ); | |||||
| name = Modules; | |||||
| sourceTree = "<group>"; | |||||
| }; | |||||
| 236D186F5A6536C59D6E751C = { | 236D186F5A6536C59D6E751C = { | ||||
| isa = PBXGroup; | isa = PBXGroup; | ||||
| children = ( | children = ( | ||||
| @@ -2891,6 +2970,8 @@ | |||||
| B3528C08B84CBC950252EA69, | B3528C08B84CBC950252EA69, | ||||
| 1B0F18E1D96F727C062B05FA, | 1B0F18E1D96F727C062B05FA, | ||||
| 92A66A8BD87F98EB6B4FB6D0, | 92A66A8BD87F98EB6B4FB6D0, | ||||
| F63F46CA0A51C679867855A7, | |||||
| 247768B490B9D759DDA79359, | |||||
| ); | ); | ||||
| name = UI; | name = UI; | ||||
| sourceTree = "<group>"; | sourceTree = "<group>"; | ||||
| @@ -2898,9 +2979,8 @@ | |||||
| 89E9055A179B4C2019B4E1AE = { | 89E9055A179B4C2019B4E1AE = { | ||||
| isa = PBXGroup; | isa = PBXGroup; | ||||
| children = ( | children = ( | ||||
| 5108FDF7F62E617332FB13B0, | |||||
| EBC037ECAAC8156B8B19DC69, | EBC037ECAAC8156B8B19DC69, | ||||
| F797071D88542C813CF7222A, | |||||
| 7211101FFA28400ADBB1D47A, | |||||
| BAC43B20E14A340CCF14119C, | BAC43B20E14A340CCF14119C, | ||||
| BF3CEF080FA013E2778DCE90, | BF3CEF080FA013E2778DCE90, | ||||
| ); | ); | ||||
| @@ -3424,8 +3504,8 @@ | |||||
| D25EBE02B55DB244BE0D5635, | D25EBE02B55DB244BE0D5635, | ||||
| 85E7FCB0516EFF853FA7B380, | 85E7FCB0516EFF853FA7B380, | ||||
| CC6C4D351BA9B473E5F95791, | CC6C4D351BA9B473E5F95791, | ||||
| 0E783907C6214ADD59EC95DC, | |||||
| 05A08E366EBF8D650974E695, | 05A08E366EBF8D650974E695, | ||||
| 3FCA61C401007B243E2E9035, | |||||
| 30B921C38DCEE787B294B746, | 30B921C38DCEE787B294B746, | ||||
| 244567D3AE2E417A8CB2B95E, | 244567D3AE2E417A8CB2B95E, | ||||
| 26D6AEA321E80ABCC3CCCCD1, | 26D6AEA321E80ABCC3CCCCD1, | ||||
| @@ -214,11 +214,11 @@ | |||||
| <ClCompile Include="..\..\Source\LiveBuildEngine\jucer_CompileEngineClient.cpp"/> | <ClCompile Include="..\..\Source\LiveBuildEngine\jucer_CompileEngineClient.cpp"/> | ||||
| <ClCompile Include="..\..\Source\LiveBuildEngine\jucer_CompileEngineServer.cpp"/> | <ClCompile Include="..\..\Source\LiveBuildEngine\jucer_CompileEngineServer.cpp"/> | ||||
| <ClCompile Include="..\..\Source\LiveBuildEngine\jucer_DownloadCompileEngineThread.cpp"/> | <ClCompile Include="..\..\Source\LiveBuildEngine\jucer_DownloadCompileEngineThread.cpp"/> | ||||
| <ClCompile Include="..\..\Source\Project\Modules\jucer_Modules.cpp"/> | |||||
| <ClCompile Include="..\..\Source\Project\UI\jucer_HeaderComponent.cpp"/> | <ClCompile Include="..\..\Source\Project\UI\jucer_HeaderComponent.cpp"/> | ||||
| <ClCompile Include="..\..\Source\Project\UI\jucer_ProjectContentComponent.cpp"> | <ClCompile Include="..\..\Source\Project\UI\jucer_ProjectContentComponent.cpp"> | ||||
| <ExcludedFromBuild>true</ExcludedFromBuild> | <ExcludedFromBuild>true</ExcludedFromBuild> | ||||
| </ClCompile> | </ClCompile> | ||||
| <ClCompile Include="..\..\Source\Project\jucer_Module.cpp"/> | |||||
| <ClCompile Include="..\..\Source\Project\jucer_Project.cpp"/> | <ClCompile Include="..\..\Source\Project\jucer_Project.cpp"/> | ||||
| <ClCompile Include="..\..\Source\ProjectSaving\jucer_ProjectExporter.cpp"/> | <ClCompile Include="..\..\Source\ProjectSaving\jucer_ProjectExporter.cpp"/> | ||||
| <ClCompile Include="..\..\Source\ProjectSaving\jucer_ProjectSaver.cpp"/> | <ClCompile Include="..\..\Source\ProjectSaving\jucer_ProjectSaver.cpp"/> | ||||
| @@ -1483,6 +1483,10 @@ | |||||
| <ClCompile Include="..\..\JuceLibraryCode\include_juce_gui_extra.cpp"/> | <ClCompile Include="..\..\JuceLibraryCode\include_juce_gui_extra.cpp"/> | ||||
| </ItemGroup> | </ItemGroup> | ||||
| <ItemGroup> | <ItemGroup> | ||||
| <ClInclude Include="..\..\Source\Application\UserAccount\jucer_LicenseController.h"/> | |||||
| <ClInclude Include="..\..\Source\Application\UserAccount\jucer_LicenseQueryThread.h"/> | |||||
| <ClInclude Include="..\..\Source\Application\UserAccount\jucer_LicenseState.h"/> | |||||
| <ClInclude Include="..\..\Source\Application\UserAccount\jucer_LoginFormComponent.h"/> | |||||
| <ClInclude Include="..\..\Source\Application\Windows\jucer_AboutWindowComponent.h"/> | <ClInclude Include="..\..\Source\Application\Windows\jucer_AboutWindowComponent.h"/> | ||||
| <ClInclude Include="..\..\Source\Application\Windows\jucer_EditorColourSchemeWindowComponent.h"/> | <ClInclude Include="..\..\Source\Application\Windows\jucer_EditorColourSchemeWindowComponent.h"/> | ||||
| <ClInclude Include="..\..\Source\Application\Windows\jucer_FloatingToolWindow.h"/> | <ClInclude Include="..\..\Source\Application\Windows\jucer_FloatingToolWindow.h"/> | ||||
| @@ -1603,6 +1607,9 @@ | |||||
| <ClInclude Include="..\..\Source\LiveBuildEngine\jucer_MessageIDs.h"/> | <ClInclude Include="..\..\Source\LiveBuildEngine\jucer_MessageIDs.h"/> | ||||
| <ClInclude Include="..\..\Source\LiveBuildEngine\jucer_ProjectBuildInfo.h"/> | <ClInclude Include="..\..\Source\LiveBuildEngine\jucer_ProjectBuildInfo.h"/> | ||||
| <ClInclude Include="..\..\Source\LiveBuildEngine\jucer_SourceCodeRange.h"/> | <ClInclude Include="..\..\Source\LiveBuildEngine\jucer_SourceCodeRange.h"/> | ||||
| <ClInclude Include="..\..\Source\Project\Modules\jucer_AvailableModulesList.h"/> | |||||
| <ClInclude Include="..\..\Source\Project\Modules\jucer_ModuleDescription.h"/> | |||||
| <ClInclude Include="..\..\Source\Project\Modules\jucer_Modules.h"/> | |||||
| <ClInclude Include="..\..\Source\Project\UI\Sidebar\jucer_ExporterTreeItems.h"/> | <ClInclude Include="..\..\Source\Project\UI\Sidebar\jucer_ExporterTreeItems.h"/> | ||||
| <ClInclude Include="..\..\Source\Project\UI\Sidebar\jucer_FileTreeItems.h"/> | <ClInclude Include="..\..\Source\Project\UI\Sidebar\jucer_FileTreeItems.h"/> | ||||
| <ClInclude Include="..\..\Source\Project\UI\Sidebar\jucer_LiveBuildTab.h"/> | <ClInclude Include="..\..\Source\Project\UI\Sidebar\jucer_LiveBuildTab.h"/> | ||||
| @@ -1616,7 +1623,8 @@ | |||||
| <ClInclude Include="..\..\Source\Project\UI\jucer_HeaderComponent.h"/> | <ClInclude Include="..\..\Source\Project\UI\jucer_HeaderComponent.h"/> | ||||
| <ClInclude Include="..\..\Source\Project\UI\jucer_ModulesInformationComponent.h"/> | <ClInclude Include="..\..\Source\Project\UI\jucer_ModulesInformationComponent.h"/> | ||||
| <ClInclude Include="..\..\Source\Project\UI\jucer_ProjectContentComponent.h"/> | <ClInclude Include="..\..\Source\Project\UI\jucer_ProjectContentComponent.h"/> | ||||
| <ClInclude Include="..\..\Source\Project\jucer_Module.h"/> | |||||
| <ClInclude Include="..\..\Source\Project\UI\jucer_ProjectMessagesComponent.h"/> | |||||
| <ClInclude Include="..\..\Source\Project\UI\jucer_UserAvatarComponent.h"/> | |||||
| <ClInclude Include="..\..\Source\Project\jucer_Project.h"/> | <ClInclude Include="..\..\Source\Project\jucer_Project.h"/> | ||||
| <ClInclude Include="..\..\Source\ProjectSaving\jucer_ProjectExport_Android.h"/> | <ClInclude Include="..\..\Source\ProjectSaving\jucer_ProjectExport_Android.h"/> | ||||
| <ClInclude Include="..\..\Source\ProjectSaving\jucer_ProjectExport_CLion.h"/> | <ClInclude Include="..\..\Source\ProjectSaving\jucer_ProjectExport_CLion.h"/> | ||||
| @@ -2105,7 +2113,7 @@ | |||||
| <None Include="..\..\Source\BinaryData\Icons\export_linux.svg"/> | <None Include="..\..\Source\BinaryData\Icons\export_linux.svg"/> | ||||
| <None Include="..\..\Source\BinaryData\Icons\export_visualStudio.svg"/> | <None Include="..\..\Source\BinaryData\Icons\export_visualStudio.svg"/> | ||||
| <None Include="..\..\Source\BinaryData\Icons\export_xcode.svg"/> | <None Include="..\..\Source\BinaryData\Icons\export_xcode.svg"/> | ||||
| <None Include="..\..\Source\BinaryData\Icons\huckleberry_icon.svg"/> | |||||
| <None Include="..\..\Source\BinaryData\Icons\gpl_logo.svg"/> | |||||
| <None Include="..\..\Source\BinaryData\Icons\juce-logo-with-text.svg"/> | <None Include="..\..\Source\BinaryData\Icons\juce-logo-with-text.svg"/> | ||||
| <None Include="..\..\Source\BinaryData\Icons\juce_icon.png"/> | <None Include="..\..\Source\BinaryData\Icons\juce_icon.png"/> | ||||
| <None Include="..\..\Source\BinaryData\Icons\wizard_AnimatedApp.svg"/> | <None Include="..\..\Source\BinaryData\Icons\wizard_AnimatedApp.svg"/> | ||||
| @@ -2,6 +2,9 @@ | |||||
| <Project ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> | <Project ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> | ||||
| <ItemGroup> | <ItemGroup> | ||||
| <Filter Include="Projucer\Application\UserAccount"> | |||||
| <UniqueIdentifier>{DA27985D-8427-CE70-CA06-EAF7009CCC60}</UniqueIdentifier> | |||||
| </Filter> | |||||
| <Filter Include="Projucer\Application\Windows"> | <Filter Include="Projucer\Application\Windows"> | ||||
| <UniqueIdentifier>{DC7E18A5-E854-3D99-627F-AAA88246B712}</UniqueIdentifier> | <UniqueIdentifier>{DC7E18A5-E854-3D99-627F-AAA88246B712}</UniqueIdentifier> | ||||
| </Filter> | </Filter> | ||||
| @@ -47,6 +50,9 @@ | |||||
| <Filter Include="Projucer\LiveBuildEngine"> | <Filter Include="Projucer\LiveBuildEngine"> | ||||
| <UniqueIdentifier>{0A3B9446-F50B-3D4E-230F-7ED493541A07}</UniqueIdentifier> | <UniqueIdentifier>{0A3B9446-F50B-3D4E-230F-7ED493541A07}</UniqueIdentifier> | ||||
| </Filter> | </Filter> | ||||
| <Filter Include="Projucer\Project\Modules"> | |||||
| <UniqueIdentifier>{F5C79836-30DE-9DC7-9392-DAAB3F04C18E}</UniqueIdentifier> | |||||
| </Filter> | |||||
| <Filter Include="Projucer\Project\UI\Sidebar"> | <Filter Include="Projucer\Project\UI\Sidebar"> | ||||
| <UniqueIdentifier>{A0A94AE6-B447-151A-D0DA-FAE9B5410EBF}</UniqueIdentifier> | <UniqueIdentifier>{A0A94AE6-B447-151A-D0DA-FAE9B5410EBF}</UniqueIdentifier> | ||||
| </Filter> | </Filter> | ||||
| @@ -448,15 +454,15 @@ | |||||
| <ClCompile Include="..\..\Source\LiveBuildEngine\jucer_DownloadCompileEngineThread.cpp"> | <ClCompile Include="..\..\Source\LiveBuildEngine\jucer_DownloadCompileEngineThread.cpp"> | ||||
| <Filter>Projucer\LiveBuildEngine</Filter> | <Filter>Projucer\LiveBuildEngine</Filter> | ||||
| </ClCompile> | </ClCompile> | ||||
| <ClCompile Include="..\..\Source\Project\Modules\jucer_Modules.cpp"> | |||||
| <Filter>Projucer\Project\Modules</Filter> | |||||
| </ClCompile> | |||||
| <ClCompile Include="..\..\Source\Project\UI\jucer_HeaderComponent.cpp"> | <ClCompile Include="..\..\Source\Project\UI\jucer_HeaderComponent.cpp"> | ||||
| <Filter>Projucer\Project\UI</Filter> | <Filter>Projucer\Project\UI</Filter> | ||||
| </ClCompile> | </ClCompile> | ||||
| <ClCompile Include="..\..\Source\Project\UI\jucer_ProjectContentComponent.cpp"> | <ClCompile Include="..\..\Source\Project\UI\jucer_ProjectContentComponent.cpp"> | ||||
| <Filter>Projucer\Project\UI</Filter> | <Filter>Projucer\Project\UI</Filter> | ||||
| </ClCompile> | </ClCompile> | ||||
| <ClCompile Include="..\..\Source\Project\jucer_Module.cpp"> | |||||
| <Filter>Projucer\Project</Filter> | |||||
| </ClCompile> | |||||
| <ClCompile Include="..\..\Source\Project\jucer_Project.cpp"> | <ClCompile Include="..\..\Source\Project\jucer_Project.cpp"> | ||||
| <Filter>Projucer\Project</Filter> | <Filter>Projucer\Project</Filter> | ||||
| </ClCompile> | </ClCompile> | ||||
| @@ -1857,6 +1863,18 @@ | |||||
| </ClCompile> | </ClCompile> | ||||
| </ItemGroup> | </ItemGroup> | ||||
| <ItemGroup> | <ItemGroup> | ||||
| <ClInclude Include="..\..\Source\Application\UserAccount\jucer_LicenseController.h"> | |||||
| <Filter>Projucer\Application\UserAccount</Filter> | |||||
| </ClInclude> | |||||
| <ClInclude Include="..\..\Source\Application\UserAccount\jucer_LicenseQueryThread.h"> | |||||
| <Filter>Projucer\Application\UserAccount</Filter> | |||||
| </ClInclude> | |||||
| <ClInclude Include="..\..\Source\Application\UserAccount\jucer_LicenseState.h"> | |||||
| <Filter>Projucer\Application\UserAccount</Filter> | |||||
| </ClInclude> | |||||
| <ClInclude Include="..\..\Source\Application\UserAccount\jucer_LoginFormComponent.h"> | |||||
| <Filter>Projucer\Application\UserAccount</Filter> | |||||
| </ClInclude> | |||||
| <ClInclude Include="..\..\Source\Application\Windows\jucer_AboutWindowComponent.h"> | <ClInclude Include="..\..\Source\Application\Windows\jucer_AboutWindowComponent.h"> | ||||
| <Filter>Projucer\Application\Windows</Filter> | <Filter>Projucer\Application\Windows</Filter> | ||||
| </ClInclude> | </ClInclude> | ||||
| @@ -2217,6 +2235,15 @@ | |||||
| <ClInclude Include="..\..\Source\LiveBuildEngine\jucer_SourceCodeRange.h"> | <ClInclude Include="..\..\Source\LiveBuildEngine\jucer_SourceCodeRange.h"> | ||||
| <Filter>Projucer\LiveBuildEngine</Filter> | <Filter>Projucer\LiveBuildEngine</Filter> | ||||
| </ClInclude> | </ClInclude> | ||||
| <ClInclude Include="..\..\Source\Project\Modules\jucer_AvailableModulesList.h"> | |||||
| <Filter>Projucer\Project\Modules</Filter> | |||||
| </ClInclude> | |||||
| <ClInclude Include="..\..\Source\Project\Modules\jucer_ModuleDescription.h"> | |||||
| <Filter>Projucer\Project\Modules</Filter> | |||||
| </ClInclude> | |||||
| <ClInclude Include="..\..\Source\Project\Modules\jucer_Modules.h"> | |||||
| <Filter>Projucer\Project\Modules</Filter> | |||||
| </ClInclude> | |||||
| <ClInclude Include="..\..\Source\Project\UI\Sidebar\jucer_ExporterTreeItems.h"> | <ClInclude Include="..\..\Source\Project\UI\Sidebar\jucer_ExporterTreeItems.h"> | ||||
| <Filter>Projucer\Project\UI\Sidebar</Filter> | <Filter>Projucer\Project\UI\Sidebar</Filter> | ||||
| </ClInclude> | </ClInclude> | ||||
| @@ -2256,8 +2283,11 @@ | |||||
| <ClInclude Include="..\..\Source\Project\UI\jucer_ProjectContentComponent.h"> | <ClInclude Include="..\..\Source\Project\UI\jucer_ProjectContentComponent.h"> | ||||
| <Filter>Projucer\Project\UI</Filter> | <Filter>Projucer\Project\UI</Filter> | ||||
| </ClInclude> | </ClInclude> | ||||
| <ClInclude Include="..\..\Source\Project\jucer_Module.h"> | |||||
| <Filter>Projucer\Project</Filter> | |||||
| <ClInclude Include="..\..\Source\Project\UI\jucer_ProjectMessagesComponent.h"> | |||||
| <Filter>Projucer\Project\UI</Filter> | |||||
| </ClInclude> | |||||
| <ClInclude Include="..\..\Source\Project\UI\jucer_UserAvatarComponent.h"> | |||||
| <Filter>Projucer\Project\UI</Filter> | |||||
| </ClInclude> | </ClInclude> | ||||
| <ClInclude Include="..\..\Source\Project\jucer_Project.h"> | <ClInclude Include="..\..\Source\Project\jucer_Project.h"> | ||||
| <Filter>Projucer\Project</Filter> | <Filter>Projucer\Project</Filter> | ||||
| @@ -3719,7 +3749,7 @@ | |||||
| <None Include="..\..\Source\BinaryData\Icons\export_xcode.svg"> | <None Include="..\..\Source\BinaryData\Icons\export_xcode.svg"> | ||||
| <Filter>Projucer\BinaryData\Icons</Filter> | <Filter>Projucer\BinaryData\Icons</Filter> | ||||
| </None> | </None> | ||||
| <None Include="..\..\Source\BinaryData\Icons\huckleberry_icon.svg"> | |||||
| <None Include="..\..\Source\BinaryData\Icons\gpl_logo.svg"> | |||||
| <Filter>Projucer\BinaryData\Icons</Filter> | <Filter>Projucer\BinaryData\Icons</Filter> | ||||
| </None> | </None> | ||||
| <None Include="..\..\Source\BinaryData\Icons\juce-logo-with-text.svg"> | <None Include="..\..\Source\BinaryData\Icons\juce-logo-with-text.svg"> | ||||
| @@ -214,11 +214,11 @@ | |||||
| <ClCompile Include="..\..\Source\LiveBuildEngine\jucer_CompileEngineClient.cpp"/> | <ClCompile Include="..\..\Source\LiveBuildEngine\jucer_CompileEngineClient.cpp"/> | ||||
| <ClCompile Include="..\..\Source\LiveBuildEngine\jucer_CompileEngineServer.cpp"/> | <ClCompile Include="..\..\Source\LiveBuildEngine\jucer_CompileEngineServer.cpp"/> | ||||
| <ClCompile Include="..\..\Source\LiveBuildEngine\jucer_DownloadCompileEngineThread.cpp"/> | <ClCompile Include="..\..\Source\LiveBuildEngine\jucer_DownloadCompileEngineThread.cpp"/> | ||||
| <ClCompile Include="..\..\Source\Project\Modules\jucer_Modules.cpp"/> | |||||
| <ClCompile Include="..\..\Source\Project\UI\jucer_HeaderComponent.cpp"/> | <ClCompile Include="..\..\Source\Project\UI\jucer_HeaderComponent.cpp"/> | ||||
| <ClCompile Include="..\..\Source\Project\UI\jucer_ProjectContentComponent.cpp"> | <ClCompile Include="..\..\Source\Project\UI\jucer_ProjectContentComponent.cpp"> | ||||
| <ExcludedFromBuild>true</ExcludedFromBuild> | <ExcludedFromBuild>true</ExcludedFromBuild> | ||||
| </ClCompile> | </ClCompile> | ||||
| <ClCompile Include="..\..\Source\Project\jucer_Module.cpp"/> | |||||
| <ClCompile Include="..\..\Source\Project\jucer_Project.cpp"/> | <ClCompile Include="..\..\Source\Project\jucer_Project.cpp"/> | ||||
| <ClCompile Include="..\..\Source\ProjectSaving\jucer_ProjectExporter.cpp"/> | <ClCompile Include="..\..\Source\ProjectSaving\jucer_ProjectExporter.cpp"/> | ||||
| <ClCompile Include="..\..\Source\ProjectSaving\jucer_ProjectSaver.cpp"/> | <ClCompile Include="..\..\Source\ProjectSaving\jucer_ProjectSaver.cpp"/> | ||||
| @@ -1483,6 +1483,10 @@ | |||||
| <ClCompile Include="..\..\JuceLibraryCode\include_juce_gui_extra.cpp"/> | <ClCompile Include="..\..\JuceLibraryCode\include_juce_gui_extra.cpp"/> | ||||
| </ItemGroup> | </ItemGroup> | ||||
| <ItemGroup> | <ItemGroup> | ||||
| <ClInclude Include="..\..\Source\Application\UserAccount\jucer_LicenseController.h"/> | |||||
| <ClInclude Include="..\..\Source\Application\UserAccount\jucer_LicenseQueryThread.h"/> | |||||
| <ClInclude Include="..\..\Source\Application\UserAccount\jucer_LicenseState.h"/> | |||||
| <ClInclude Include="..\..\Source\Application\UserAccount\jucer_LoginFormComponent.h"/> | |||||
| <ClInclude Include="..\..\Source\Application\Windows\jucer_AboutWindowComponent.h"/> | <ClInclude Include="..\..\Source\Application\Windows\jucer_AboutWindowComponent.h"/> | ||||
| <ClInclude Include="..\..\Source\Application\Windows\jucer_EditorColourSchemeWindowComponent.h"/> | <ClInclude Include="..\..\Source\Application\Windows\jucer_EditorColourSchemeWindowComponent.h"/> | ||||
| <ClInclude Include="..\..\Source\Application\Windows\jucer_FloatingToolWindow.h"/> | <ClInclude Include="..\..\Source\Application\Windows\jucer_FloatingToolWindow.h"/> | ||||
| @@ -1603,6 +1607,9 @@ | |||||
| <ClInclude Include="..\..\Source\LiveBuildEngine\jucer_MessageIDs.h"/> | <ClInclude Include="..\..\Source\LiveBuildEngine\jucer_MessageIDs.h"/> | ||||
| <ClInclude Include="..\..\Source\LiveBuildEngine\jucer_ProjectBuildInfo.h"/> | <ClInclude Include="..\..\Source\LiveBuildEngine\jucer_ProjectBuildInfo.h"/> | ||||
| <ClInclude Include="..\..\Source\LiveBuildEngine\jucer_SourceCodeRange.h"/> | <ClInclude Include="..\..\Source\LiveBuildEngine\jucer_SourceCodeRange.h"/> | ||||
| <ClInclude Include="..\..\Source\Project\Modules\jucer_AvailableModulesList.h"/> | |||||
| <ClInclude Include="..\..\Source\Project\Modules\jucer_ModuleDescription.h"/> | |||||
| <ClInclude Include="..\..\Source\Project\Modules\jucer_Modules.h"/> | |||||
| <ClInclude Include="..\..\Source\Project\UI\Sidebar\jucer_ExporterTreeItems.h"/> | <ClInclude Include="..\..\Source\Project\UI\Sidebar\jucer_ExporterTreeItems.h"/> | ||||
| <ClInclude Include="..\..\Source\Project\UI\Sidebar\jucer_FileTreeItems.h"/> | <ClInclude Include="..\..\Source\Project\UI\Sidebar\jucer_FileTreeItems.h"/> | ||||
| <ClInclude Include="..\..\Source\Project\UI\Sidebar\jucer_LiveBuildTab.h"/> | <ClInclude Include="..\..\Source\Project\UI\Sidebar\jucer_LiveBuildTab.h"/> | ||||
| @@ -1616,7 +1623,8 @@ | |||||
| <ClInclude Include="..\..\Source\Project\UI\jucer_HeaderComponent.h"/> | <ClInclude Include="..\..\Source\Project\UI\jucer_HeaderComponent.h"/> | ||||
| <ClInclude Include="..\..\Source\Project\UI\jucer_ModulesInformationComponent.h"/> | <ClInclude Include="..\..\Source\Project\UI\jucer_ModulesInformationComponent.h"/> | ||||
| <ClInclude Include="..\..\Source\Project\UI\jucer_ProjectContentComponent.h"/> | <ClInclude Include="..\..\Source\Project\UI\jucer_ProjectContentComponent.h"/> | ||||
| <ClInclude Include="..\..\Source\Project\jucer_Module.h"/> | |||||
| <ClInclude Include="..\..\Source\Project\UI\jucer_ProjectMessagesComponent.h"/> | |||||
| <ClInclude Include="..\..\Source\Project\UI\jucer_UserAvatarComponent.h"/> | |||||
| <ClInclude Include="..\..\Source\Project\jucer_Project.h"/> | <ClInclude Include="..\..\Source\Project\jucer_Project.h"/> | ||||
| <ClInclude Include="..\..\Source\ProjectSaving\jucer_ProjectExport_Android.h"/> | <ClInclude Include="..\..\Source\ProjectSaving\jucer_ProjectExport_Android.h"/> | ||||
| <ClInclude Include="..\..\Source\ProjectSaving\jucer_ProjectExport_CLion.h"/> | <ClInclude Include="..\..\Source\ProjectSaving\jucer_ProjectExport_CLion.h"/> | ||||
| @@ -2105,7 +2113,7 @@ | |||||
| <None Include="..\..\Source\BinaryData\Icons\export_linux.svg"/> | <None Include="..\..\Source\BinaryData\Icons\export_linux.svg"/> | ||||
| <None Include="..\..\Source\BinaryData\Icons\export_visualStudio.svg"/> | <None Include="..\..\Source\BinaryData\Icons\export_visualStudio.svg"/> | ||||
| <None Include="..\..\Source\BinaryData\Icons\export_xcode.svg"/> | <None Include="..\..\Source\BinaryData\Icons\export_xcode.svg"/> | ||||
| <None Include="..\..\Source\BinaryData\Icons\huckleberry_icon.svg"/> | |||||
| <None Include="..\..\Source\BinaryData\Icons\gpl_logo.svg"/> | |||||
| <None Include="..\..\Source\BinaryData\Icons\juce-logo-with-text.svg"/> | <None Include="..\..\Source\BinaryData\Icons\juce-logo-with-text.svg"/> | ||||
| <None Include="..\..\Source\BinaryData\Icons\juce_icon.png"/> | <None Include="..\..\Source\BinaryData\Icons\juce_icon.png"/> | ||||
| <None Include="..\..\Source\BinaryData\Icons\wizard_AnimatedApp.svg"/> | <None Include="..\..\Source\BinaryData\Icons\wizard_AnimatedApp.svg"/> | ||||
| @@ -2,6 +2,9 @@ | |||||
| <Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> | <Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> | ||||
| <ItemGroup> | <ItemGroup> | ||||
| <Filter Include="Projucer\Application\UserAccount"> | |||||
| <UniqueIdentifier>{DA27985D-8427-CE70-CA06-EAF7009CCC60}</UniqueIdentifier> | |||||
| </Filter> | |||||
| <Filter Include="Projucer\Application\Windows"> | <Filter Include="Projucer\Application\Windows"> | ||||
| <UniqueIdentifier>{DC7E18A5-E854-3D99-627F-AAA88246B712}</UniqueIdentifier> | <UniqueIdentifier>{DC7E18A5-E854-3D99-627F-AAA88246B712}</UniqueIdentifier> | ||||
| </Filter> | </Filter> | ||||
| @@ -47,6 +50,9 @@ | |||||
| <Filter Include="Projucer\LiveBuildEngine"> | <Filter Include="Projucer\LiveBuildEngine"> | ||||
| <UniqueIdentifier>{0A3B9446-F50B-3D4E-230F-7ED493541A07}</UniqueIdentifier> | <UniqueIdentifier>{0A3B9446-F50B-3D4E-230F-7ED493541A07}</UniqueIdentifier> | ||||
| </Filter> | </Filter> | ||||
| <Filter Include="Projucer\Project\Modules"> | |||||
| <UniqueIdentifier>{F5C79836-30DE-9DC7-9392-DAAB3F04C18E}</UniqueIdentifier> | |||||
| </Filter> | |||||
| <Filter Include="Projucer\Project\UI\Sidebar"> | <Filter Include="Projucer\Project\UI\Sidebar"> | ||||
| <UniqueIdentifier>{A0A94AE6-B447-151A-D0DA-FAE9B5410EBF}</UniqueIdentifier> | <UniqueIdentifier>{A0A94AE6-B447-151A-D0DA-FAE9B5410EBF}</UniqueIdentifier> | ||||
| </Filter> | </Filter> | ||||
| @@ -448,15 +454,15 @@ | |||||
| <ClCompile Include="..\..\Source\LiveBuildEngine\jucer_DownloadCompileEngineThread.cpp"> | <ClCompile Include="..\..\Source\LiveBuildEngine\jucer_DownloadCompileEngineThread.cpp"> | ||||
| <Filter>Projucer\LiveBuildEngine</Filter> | <Filter>Projucer\LiveBuildEngine</Filter> | ||||
| </ClCompile> | </ClCompile> | ||||
| <ClCompile Include="..\..\Source\Project\Modules\jucer_Modules.cpp"> | |||||
| <Filter>Projucer\Project\Modules</Filter> | |||||
| </ClCompile> | |||||
| <ClCompile Include="..\..\Source\Project\UI\jucer_HeaderComponent.cpp"> | <ClCompile Include="..\..\Source\Project\UI\jucer_HeaderComponent.cpp"> | ||||
| <Filter>Projucer\Project\UI</Filter> | <Filter>Projucer\Project\UI</Filter> | ||||
| </ClCompile> | </ClCompile> | ||||
| <ClCompile Include="..\..\Source\Project\UI\jucer_ProjectContentComponent.cpp"> | <ClCompile Include="..\..\Source\Project\UI\jucer_ProjectContentComponent.cpp"> | ||||
| <Filter>Projucer\Project\UI</Filter> | <Filter>Projucer\Project\UI</Filter> | ||||
| </ClCompile> | </ClCompile> | ||||
| <ClCompile Include="..\..\Source\Project\jucer_Module.cpp"> | |||||
| <Filter>Projucer\Project</Filter> | |||||
| </ClCompile> | |||||
| <ClCompile Include="..\..\Source\Project\jucer_Project.cpp"> | <ClCompile Include="..\..\Source\Project\jucer_Project.cpp"> | ||||
| <Filter>Projucer\Project</Filter> | <Filter>Projucer\Project</Filter> | ||||
| </ClCompile> | </ClCompile> | ||||
| @@ -1857,6 +1863,18 @@ | |||||
| </ClCompile> | </ClCompile> | ||||
| </ItemGroup> | </ItemGroup> | ||||
| <ItemGroup> | <ItemGroup> | ||||
| <ClInclude Include="..\..\Source\Application\UserAccount\jucer_LicenseController.h"> | |||||
| <Filter>Projucer\Application\UserAccount</Filter> | |||||
| </ClInclude> | |||||
| <ClInclude Include="..\..\Source\Application\UserAccount\jucer_LicenseQueryThread.h"> | |||||
| <Filter>Projucer\Application\UserAccount</Filter> | |||||
| </ClInclude> | |||||
| <ClInclude Include="..\..\Source\Application\UserAccount\jucer_LicenseState.h"> | |||||
| <Filter>Projucer\Application\UserAccount</Filter> | |||||
| </ClInclude> | |||||
| <ClInclude Include="..\..\Source\Application\UserAccount\jucer_LoginFormComponent.h"> | |||||
| <Filter>Projucer\Application\UserAccount</Filter> | |||||
| </ClInclude> | |||||
| <ClInclude Include="..\..\Source\Application\Windows\jucer_AboutWindowComponent.h"> | <ClInclude Include="..\..\Source\Application\Windows\jucer_AboutWindowComponent.h"> | ||||
| <Filter>Projucer\Application\Windows</Filter> | <Filter>Projucer\Application\Windows</Filter> | ||||
| </ClInclude> | </ClInclude> | ||||
| @@ -2217,6 +2235,15 @@ | |||||
| <ClInclude Include="..\..\Source\LiveBuildEngine\jucer_SourceCodeRange.h"> | <ClInclude Include="..\..\Source\LiveBuildEngine\jucer_SourceCodeRange.h"> | ||||
| <Filter>Projucer\LiveBuildEngine</Filter> | <Filter>Projucer\LiveBuildEngine</Filter> | ||||
| </ClInclude> | </ClInclude> | ||||
| <ClInclude Include="..\..\Source\Project\Modules\jucer_AvailableModulesList.h"> | |||||
| <Filter>Projucer\Project\Modules</Filter> | |||||
| </ClInclude> | |||||
| <ClInclude Include="..\..\Source\Project\Modules\jucer_ModuleDescription.h"> | |||||
| <Filter>Projucer\Project\Modules</Filter> | |||||
| </ClInclude> | |||||
| <ClInclude Include="..\..\Source\Project\Modules\jucer_Modules.h"> | |||||
| <Filter>Projucer\Project\Modules</Filter> | |||||
| </ClInclude> | |||||
| <ClInclude Include="..\..\Source\Project\UI\Sidebar\jucer_ExporterTreeItems.h"> | <ClInclude Include="..\..\Source\Project\UI\Sidebar\jucer_ExporterTreeItems.h"> | ||||
| <Filter>Projucer\Project\UI\Sidebar</Filter> | <Filter>Projucer\Project\UI\Sidebar</Filter> | ||||
| </ClInclude> | </ClInclude> | ||||
| @@ -2256,8 +2283,11 @@ | |||||
| <ClInclude Include="..\..\Source\Project\UI\jucer_ProjectContentComponent.h"> | <ClInclude Include="..\..\Source\Project\UI\jucer_ProjectContentComponent.h"> | ||||
| <Filter>Projucer\Project\UI</Filter> | <Filter>Projucer\Project\UI</Filter> | ||||
| </ClInclude> | </ClInclude> | ||||
| <ClInclude Include="..\..\Source\Project\jucer_Module.h"> | |||||
| <Filter>Projucer\Project</Filter> | |||||
| <ClInclude Include="..\..\Source\Project\UI\jucer_ProjectMessagesComponent.h"> | |||||
| <Filter>Projucer\Project\UI</Filter> | |||||
| </ClInclude> | |||||
| <ClInclude Include="..\..\Source\Project\UI\jucer_UserAvatarComponent.h"> | |||||
| <Filter>Projucer\Project\UI</Filter> | |||||
| </ClInclude> | </ClInclude> | ||||
| <ClInclude Include="..\..\Source\Project\jucer_Project.h"> | <ClInclude Include="..\..\Source\Project\jucer_Project.h"> | ||||
| <Filter>Projucer\Project</Filter> | <Filter>Projucer\Project</Filter> | ||||
| @@ -3719,7 +3749,7 @@ | |||||
| <None Include="..\..\Source\BinaryData\Icons\export_xcode.svg"> | <None Include="..\..\Source\BinaryData\Icons\export_xcode.svg"> | ||||
| <Filter>Projucer\BinaryData\Icons</Filter> | <Filter>Projucer\BinaryData\Icons</Filter> | ||||
| </None> | </None> | ||||
| <None Include="..\..\Source\BinaryData\Icons\huckleberry_icon.svg"> | |||||
| <None Include="..\..\Source\BinaryData\Icons\gpl_logo.svg"> | |||||
| <Filter>Projucer\BinaryData\Icons</Filter> | <Filter>Projucer\BinaryData\Icons</Filter> | ||||
| </None> | </None> | ||||
| <None Include="..\..\Source\BinaryData\Icons\juce-logo-with-text.svg"> | <None Include="..\..\Source\BinaryData\Icons\juce-logo-with-text.svg"> | ||||
| @@ -214,11 +214,11 @@ | |||||
| <ClCompile Include="..\..\Source\LiveBuildEngine\jucer_CompileEngineClient.cpp"/> | <ClCompile Include="..\..\Source\LiveBuildEngine\jucer_CompileEngineClient.cpp"/> | ||||
| <ClCompile Include="..\..\Source\LiveBuildEngine\jucer_CompileEngineServer.cpp"/> | <ClCompile Include="..\..\Source\LiveBuildEngine\jucer_CompileEngineServer.cpp"/> | ||||
| <ClCompile Include="..\..\Source\LiveBuildEngine\jucer_DownloadCompileEngineThread.cpp"/> | <ClCompile Include="..\..\Source\LiveBuildEngine\jucer_DownloadCompileEngineThread.cpp"/> | ||||
| <ClCompile Include="..\..\Source\Project\Modules\jucer_Modules.cpp"/> | |||||
| <ClCompile Include="..\..\Source\Project\UI\jucer_HeaderComponent.cpp"/> | <ClCompile Include="..\..\Source\Project\UI\jucer_HeaderComponent.cpp"/> | ||||
| <ClCompile Include="..\..\Source\Project\UI\jucer_ProjectContentComponent.cpp"> | <ClCompile Include="..\..\Source\Project\UI\jucer_ProjectContentComponent.cpp"> | ||||
| <ExcludedFromBuild>true</ExcludedFromBuild> | <ExcludedFromBuild>true</ExcludedFromBuild> | ||||
| </ClCompile> | </ClCompile> | ||||
| <ClCompile Include="..\..\Source\Project\jucer_Module.cpp"/> | |||||
| <ClCompile Include="..\..\Source\Project\jucer_Project.cpp"/> | <ClCompile Include="..\..\Source\Project\jucer_Project.cpp"/> | ||||
| <ClCompile Include="..\..\Source\ProjectSaving\jucer_ProjectExporter.cpp"/> | <ClCompile Include="..\..\Source\ProjectSaving\jucer_ProjectExporter.cpp"/> | ||||
| <ClCompile Include="..\..\Source\ProjectSaving\jucer_ProjectSaver.cpp"/> | <ClCompile Include="..\..\Source\ProjectSaving\jucer_ProjectSaver.cpp"/> | ||||
| @@ -1483,6 +1483,10 @@ | |||||
| <ClCompile Include="..\..\JuceLibraryCode\include_juce_gui_extra.cpp"/> | <ClCompile Include="..\..\JuceLibraryCode\include_juce_gui_extra.cpp"/> | ||||
| </ItemGroup> | </ItemGroup> | ||||
| <ItemGroup> | <ItemGroup> | ||||
| <ClInclude Include="..\..\Source\Application\UserAccount\jucer_LicenseController.h"/> | |||||
| <ClInclude Include="..\..\Source\Application\UserAccount\jucer_LicenseQueryThread.h"/> | |||||
| <ClInclude Include="..\..\Source\Application\UserAccount\jucer_LicenseState.h"/> | |||||
| <ClInclude Include="..\..\Source\Application\UserAccount\jucer_LoginFormComponent.h"/> | |||||
| <ClInclude Include="..\..\Source\Application\Windows\jucer_AboutWindowComponent.h"/> | <ClInclude Include="..\..\Source\Application\Windows\jucer_AboutWindowComponent.h"/> | ||||
| <ClInclude Include="..\..\Source\Application\Windows\jucer_EditorColourSchemeWindowComponent.h"/> | <ClInclude Include="..\..\Source\Application\Windows\jucer_EditorColourSchemeWindowComponent.h"/> | ||||
| <ClInclude Include="..\..\Source\Application\Windows\jucer_FloatingToolWindow.h"/> | <ClInclude Include="..\..\Source\Application\Windows\jucer_FloatingToolWindow.h"/> | ||||
| @@ -1603,6 +1607,9 @@ | |||||
| <ClInclude Include="..\..\Source\LiveBuildEngine\jucer_MessageIDs.h"/> | <ClInclude Include="..\..\Source\LiveBuildEngine\jucer_MessageIDs.h"/> | ||||
| <ClInclude Include="..\..\Source\LiveBuildEngine\jucer_ProjectBuildInfo.h"/> | <ClInclude Include="..\..\Source\LiveBuildEngine\jucer_ProjectBuildInfo.h"/> | ||||
| <ClInclude Include="..\..\Source\LiveBuildEngine\jucer_SourceCodeRange.h"/> | <ClInclude Include="..\..\Source\LiveBuildEngine\jucer_SourceCodeRange.h"/> | ||||
| <ClInclude Include="..\..\Source\Project\Modules\jucer_AvailableModulesList.h"/> | |||||
| <ClInclude Include="..\..\Source\Project\Modules\jucer_ModuleDescription.h"/> | |||||
| <ClInclude Include="..\..\Source\Project\Modules\jucer_Modules.h"/> | |||||
| <ClInclude Include="..\..\Source\Project\UI\Sidebar\jucer_ExporterTreeItems.h"/> | <ClInclude Include="..\..\Source\Project\UI\Sidebar\jucer_ExporterTreeItems.h"/> | ||||
| <ClInclude Include="..\..\Source\Project\UI\Sidebar\jucer_FileTreeItems.h"/> | <ClInclude Include="..\..\Source\Project\UI\Sidebar\jucer_FileTreeItems.h"/> | ||||
| <ClInclude Include="..\..\Source\Project\UI\Sidebar\jucer_LiveBuildTab.h"/> | <ClInclude Include="..\..\Source\Project\UI\Sidebar\jucer_LiveBuildTab.h"/> | ||||
| @@ -1616,7 +1623,8 @@ | |||||
| <ClInclude Include="..\..\Source\Project\UI\jucer_HeaderComponent.h"/> | <ClInclude Include="..\..\Source\Project\UI\jucer_HeaderComponent.h"/> | ||||
| <ClInclude Include="..\..\Source\Project\UI\jucer_ModulesInformationComponent.h"/> | <ClInclude Include="..\..\Source\Project\UI\jucer_ModulesInformationComponent.h"/> | ||||
| <ClInclude Include="..\..\Source\Project\UI\jucer_ProjectContentComponent.h"/> | <ClInclude Include="..\..\Source\Project\UI\jucer_ProjectContentComponent.h"/> | ||||
| <ClInclude Include="..\..\Source\Project\jucer_Module.h"/> | |||||
| <ClInclude Include="..\..\Source\Project\UI\jucer_ProjectMessagesComponent.h"/> | |||||
| <ClInclude Include="..\..\Source\Project\UI\jucer_UserAvatarComponent.h"/> | |||||
| <ClInclude Include="..\..\Source\Project\jucer_Project.h"/> | <ClInclude Include="..\..\Source\Project\jucer_Project.h"/> | ||||
| <ClInclude Include="..\..\Source\ProjectSaving\jucer_ProjectExport_Android.h"/> | <ClInclude Include="..\..\Source\ProjectSaving\jucer_ProjectExport_Android.h"/> | ||||
| <ClInclude Include="..\..\Source\ProjectSaving\jucer_ProjectExport_CLion.h"/> | <ClInclude Include="..\..\Source\ProjectSaving\jucer_ProjectExport_CLion.h"/> | ||||
| @@ -2105,7 +2113,7 @@ | |||||
| <None Include="..\..\Source\BinaryData\Icons\export_linux.svg"/> | <None Include="..\..\Source\BinaryData\Icons\export_linux.svg"/> | ||||
| <None Include="..\..\Source\BinaryData\Icons\export_visualStudio.svg"/> | <None Include="..\..\Source\BinaryData\Icons\export_visualStudio.svg"/> | ||||
| <None Include="..\..\Source\BinaryData\Icons\export_xcode.svg"/> | <None Include="..\..\Source\BinaryData\Icons\export_xcode.svg"/> | ||||
| <None Include="..\..\Source\BinaryData\Icons\huckleberry_icon.svg"/> | |||||
| <None Include="..\..\Source\BinaryData\Icons\gpl_logo.svg"/> | |||||
| <None Include="..\..\Source\BinaryData\Icons\juce-logo-with-text.svg"/> | <None Include="..\..\Source\BinaryData\Icons\juce-logo-with-text.svg"/> | ||||
| <None Include="..\..\Source\BinaryData\Icons\juce_icon.png"/> | <None Include="..\..\Source\BinaryData\Icons\juce_icon.png"/> | ||||
| <None Include="..\..\Source\BinaryData\Icons\wizard_AnimatedApp.svg"/> | <None Include="..\..\Source\BinaryData\Icons\wizard_AnimatedApp.svg"/> | ||||
| @@ -2,6 +2,9 @@ | |||||
| <Project ToolsVersion="16.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> | <Project ToolsVersion="16.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> | ||||
| <ItemGroup> | <ItemGroup> | ||||
| <Filter Include="Projucer\Application\UserAccount"> | |||||
| <UniqueIdentifier>{DA27985D-8427-CE70-CA06-EAF7009CCC60}</UniqueIdentifier> | |||||
| </Filter> | |||||
| <Filter Include="Projucer\Application\Windows"> | <Filter Include="Projucer\Application\Windows"> | ||||
| <UniqueIdentifier>{DC7E18A5-E854-3D99-627F-AAA88246B712}</UniqueIdentifier> | <UniqueIdentifier>{DC7E18A5-E854-3D99-627F-AAA88246B712}</UniqueIdentifier> | ||||
| </Filter> | </Filter> | ||||
| @@ -47,6 +50,9 @@ | |||||
| <Filter Include="Projucer\LiveBuildEngine"> | <Filter Include="Projucer\LiveBuildEngine"> | ||||
| <UniqueIdentifier>{0A3B9446-F50B-3D4E-230F-7ED493541A07}</UniqueIdentifier> | <UniqueIdentifier>{0A3B9446-F50B-3D4E-230F-7ED493541A07}</UniqueIdentifier> | ||||
| </Filter> | </Filter> | ||||
| <Filter Include="Projucer\Project\Modules"> | |||||
| <UniqueIdentifier>{F5C79836-30DE-9DC7-9392-DAAB3F04C18E}</UniqueIdentifier> | |||||
| </Filter> | |||||
| <Filter Include="Projucer\Project\UI\Sidebar"> | <Filter Include="Projucer\Project\UI\Sidebar"> | ||||
| <UniqueIdentifier>{A0A94AE6-B447-151A-D0DA-FAE9B5410EBF}</UniqueIdentifier> | <UniqueIdentifier>{A0A94AE6-B447-151A-D0DA-FAE9B5410EBF}</UniqueIdentifier> | ||||
| </Filter> | </Filter> | ||||
| @@ -448,15 +454,15 @@ | |||||
| <ClCompile Include="..\..\Source\LiveBuildEngine\jucer_DownloadCompileEngineThread.cpp"> | <ClCompile Include="..\..\Source\LiveBuildEngine\jucer_DownloadCompileEngineThread.cpp"> | ||||
| <Filter>Projucer\LiveBuildEngine</Filter> | <Filter>Projucer\LiveBuildEngine</Filter> | ||||
| </ClCompile> | </ClCompile> | ||||
| <ClCompile Include="..\..\Source\Project\Modules\jucer_Modules.cpp"> | |||||
| <Filter>Projucer\Project\Modules</Filter> | |||||
| </ClCompile> | |||||
| <ClCompile Include="..\..\Source\Project\UI\jucer_HeaderComponent.cpp"> | <ClCompile Include="..\..\Source\Project\UI\jucer_HeaderComponent.cpp"> | ||||
| <Filter>Projucer\Project\UI</Filter> | <Filter>Projucer\Project\UI</Filter> | ||||
| </ClCompile> | </ClCompile> | ||||
| <ClCompile Include="..\..\Source\Project\UI\jucer_ProjectContentComponent.cpp"> | <ClCompile Include="..\..\Source\Project\UI\jucer_ProjectContentComponent.cpp"> | ||||
| <Filter>Projucer\Project\UI</Filter> | <Filter>Projucer\Project\UI</Filter> | ||||
| </ClCompile> | </ClCompile> | ||||
| <ClCompile Include="..\..\Source\Project\jucer_Module.cpp"> | |||||
| <Filter>Projucer\Project</Filter> | |||||
| </ClCompile> | |||||
| <ClCompile Include="..\..\Source\Project\jucer_Project.cpp"> | <ClCompile Include="..\..\Source\Project\jucer_Project.cpp"> | ||||
| <Filter>Projucer\Project</Filter> | <Filter>Projucer\Project</Filter> | ||||
| </ClCompile> | </ClCompile> | ||||
| @@ -1857,6 +1863,18 @@ | |||||
| </ClCompile> | </ClCompile> | ||||
| </ItemGroup> | </ItemGroup> | ||||
| <ItemGroup> | <ItemGroup> | ||||
| <ClInclude Include="..\..\Source\Application\UserAccount\jucer_LicenseController.h"> | |||||
| <Filter>Projucer\Application\UserAccount</Filter> | |||||
| </ClInclude> | |||||
| <ClInclude Include="..\..\Source\Application\UserAccount\jucer_LicenseQueryThread.h"> | |||||
| <Filter>Projucer\Application\UserAccount</Filter> | |||||
| </ClInclude> | |||||
| <ClInclude Include="..\..\Source\Application\UserAccount\jucer_LicenseState.h"> | |||||
| <Filter>Projucer\Application\UserAccount</Filter> | |||||
| </ClInclude> | |||||
| <ClInclude Include="..\..\Source\Application\UserAccount\jucer_LoginFormComponent.h"> | |||||
| <Filter>Projucer\Application\UserAccount</Filter> | |||||
| </ClInclude> | |||||
| <ClInclude Include="..\..\Source\Application\Windows\jucer_AboutWindowComponent.h"> | <ClInclude Include="..\..\Source\Application\Windows\jucer_AboutWindowComponent.h"> | ||||
| <Filter>Projucer\Application\Windows</Filter> | <Filter>Projucer\Application\Windows</Filter> | ||||
| </ClInclude> | </ClInclude> | ||||
| @@ -2217,6 +2235,15 @@ | |||||
| <ClInclude Include="..\..\Source\LiveBuildEngine\jucer_SourceCodeRange.h"> | <ClInclude Include="..\..\Source\LiveBuildEngine\jucer_SourceCodeRange.h"> | ||||
| <Filter>Projucer\LiveBuildEngine</Filter> | <Filter>Projucer\LiveBuildEngine</Filter> | ||||
| </ClInclude> | </ClInclude> | ||||
| <ClInclude Include="..\..\Source\Project\Modules\jucer_AvailableModulesList.h"> | |||||
| <Filter>Projucer\Project\Modules</Filter> | |||||
| </ClInclude> | |||||
| <ClInclude Include="..\..\Source\Project\Modules\jucer_ModuleDescription.h"> | |||||
| <Filter>Projucer\Project\Modules</Filter> | |||||
| </ClInclude> | |||||
| <ClInclude Include="..\..\Source\Project\Modules\jucer_Modules.h"> | |||||
| <Filter>Projucer\Project\Modules</Filter> | |||||
| </ClInclude> | |||||
| <ClInclude Include="..\..\Source\Project\UI\Sidebar\jucer_ExporterTreeItems.h"> | <ClInclude Include="..\..\Source\Project\UI\Sidebar\jucer_ExporterTreeItems.h"> | ||||
| <Filter>Projucer\Project\UI\Sidebar</Filter> | <Filter>Projucer\Project\UI\Sidebar</Filter> | ||||
| </ClInclude> | </ClInclude> | ||||
| @@ -2256,8 +2283,11 @@ | |||||
| <ClInclude Include="..\..\Source\Project\UI\jucer_ProjectContentComponent.h"> | <ClInclude Include="..\..\Source\Project\UI\jucer_ProjectContentComponent.h"> | ||||
| <Filter>Projucer\Project\UI</Filter> | <Filter>Projucer\Project\UI</Filter> | ||||
| </ClInclude> | </ClInclude> | ||||
| <ClInclude Include="..\..\Source\Project\jucer_Module.h"> | |||||
| <Filter>Projucer\Project</Filter> | |||||
| <ClInclude Include="..\..\Source\Project\UI\jucer_ProjectMessagesComponent.h"> | |||||
| <Filter>Projucer\Project\UI</Filter> | |||||
| </ClInclude> | |||||
| <ClInclude Include="..\..\Source\Project\UI\jucer_UserAvatarComponent.h"> | |||||
| <Filter>Projucer\Project\UI</Filter> | |||||
| </ClInclude> | </ClInclude> | ||||
| <ClInclude Include="..\..\Source\Project\jucer_Project.h"> | <ClInclude Include="..\..\Source\Project\jucer_Project.h"> | ||||
| <Filter>Projucer\Project</Filter> | <Filter>Projucer\Project</Filter> | ||||
| @@ -3719,7 +3749,7 @@ | |||||
| <None Include="..\..\Source\BinaryData\Icons\export_xcode.svg"> | <None Include="..\..\Source\BinaryData\Icons\export_xcode.svg"> | ||||
| <Filter>Projucer\BinaryData\Icons</Filter> | <Filter>Projucer\BinaryData\Icons</Filter> | ||||
| </None> | </None> | ||||
| <None Include="..\..\Source\BinaryData\Icons\huckleberry_icon.svg"> | |||||
| <None Include="..\..\Source\BinaryData\Icons\gpl_logo.svg"> | |||||
| <Filter>Projucer\BinaryData\Icons</Filter> | <Filter>Projucer\BinaryData\Icons</Filter> | ||||
| </None> | </None> | ||||
| <None Include="..\..\Source\BinaryData\Icons\juce-logo-with-text.svg"> | <None Include="..\..\Source\BinaryData\Icons\juce-logo-with-text.svg"> | ||||
| @@ -57,12 +57,11 @@ target_sources(Projucer PRIVATE | |||||
| Source/ComponentEditor/jucer_JucerDocument.cpp | Source/ComponentEditor/jucer_JucerDocument.cpp | ||||
| Source/ComponentEditor/jucer_ObjectTypes.cpp | Source/ComponentEditor/jucer_ObjectTypes.cpp | ||||
| Source/ComponentEditor/jucer_PaintRoutine.cpp | Source/ComponentEditor/jucer_PaintRoutine.cpp | ||||
| Source/Licenses/jucer_LicenseController.cpp | |||||
| Source/LiveBuildEngine/jucer_CompileEngineClient.cpp | Source/LiveBuildEngine/jucer_CompileEngineClient.cpp | ||||
| Source/LiveBuildEngine/jucer_CompileEngineServer.cpp | Source/LiveBuildEngine/jucer_CompileEngineServer.cpp | ||||
| Source/LiveBuildEngine/jucer_DownloadCompileEngineThread.cpp | Source/LiveBuildEngine/jucer_DownloadCompileEngineThread.cpp | ||||
| Source/Project/Modules/jucer_Modules.cpp | |||||
| Source/Project/UI/jucer_HeaderComponent.cpp | Source/Project/UI/jucer_HeaderComponent.cpp | ||||
| Source/Project/jucer_Module.cpp | |||||
| Source/Project/jucer_Project.cpp | Source/Project/jucer_Project.cpp | ||||
| Source/ProjectSaving/jucer_ProjectExporter.cpp | Source/ProjectSaving/jucer_ProjectExporter.cpp | ||||
| Source/ProjectSaving/jucer_ProjectSaver.cpp | Source/ProjectSaving/jucer_ProjectSaver.cpp | ||||
| @@ -97,7 +96,7 @@ juce_add_binary_data(ProjucerData SOURCES | |||||
| Source/BinaryData/Icons/export_linux.svg | Source/BinaryData/Icons/export_linux.svg | ||||
| Source/BinaryData/Icons/export_visualStudio.svg | Source/BinaryData/Icons/export_visualStudio.svg | ||||
| Source/BinaryData/Icons/export_xcode.svg | Source/BinaryData/Icons/export_xcode.svg | ||||
| Source/BinaryData/Icons/huckleberry_icon.svg | |||||
| Source/BinaryData/Icons/gpl_logo.svg | |||||
| Source/BinaryData/Icons/juce-logo-with-text.svg | Source/BinaryData/Icons/juce-logo-with-text.svg | ||||
| Source/BinaryData/Icons/juce_icon.png | Source/BinaryData/Icons/juce_icon.png | ||||
| Source/BinaryData/Icons/wizard_AnimatedApp.svg | Source/BinaryData/Icons/wizard_AnimatedApp.svg | ||||
| @@ -1981,60 +1981,131 @@ static const unsigned char temp_binary_data_16[] = | |||||
| const char* export_xcode_svg = (const char*) temp_binary_data_16; | const char* export_xcode_svg = (const char*) temp_binary_data_16; | ||||
| //================== huckleberry_icon.svg ================== | |||||
| //================== gpl_logo.svg ================== | |||||
| static const unsigned char temp_binary_data_17[] = | static const unsigned char temp_binary_data_17[] = | ||||
| "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" | |||||
| "<!-- Generator: Adobe Illustrator 21.0.2, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->\n" | |||||
| "<svg version=\"1.1\" id=\"Layer_1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0px\" y=\"0px\"\n" | |||||
| "\t viewBox=\"0 0 169.7 205.2\" style=\"enable-background:new 0 0 169.7 205.2;\" xml:space=\"preserve\">\n" | |||||
| "<style type=\"text/css\">\n" | |||||
| "\t.st0{fill:#A65A95;}\n" | |||||
| "\t.st1{fill:#001946;}\n" | |||||
| "</style>\n" | |||||
| "<g>\n" | |||||
| "\t<g>\n" | |||||
| "\t\t<path class=\"st0\" d=\"M45.2,167.8c-3,0-5.4-1.1-7.8-4.3l3.6-3.1c1.6,2.1,2.7,2.7,4.2,2.7c2.6,0,4.4-2,4.4-5v-17.2h5V158\n" | |||||
| "\t\t\tC54.6,163.9,50.7,167.8,45.2,167.8z\"/>\n" | |||||
| "\t\t<path class=\"st0\" d=\"M70.7,167.8c-5.8,0-10.8-4.2-10.8-11.2v-15.8h5v15.6c0,4.1,2.4,6.7,5.8,6.7c3.4,0,5.8-2.6,5.8-6.7v-15.6h5\n" | |||||
| "\t\t\tv15.8C81.5,163.5,76.5,167.8,70.7,167.8z\"/>\n" | |||||
| "\t\t<path class=\"st0\" d=\"M99,167.8c-7.6,0-13.9-6.1-13.9-13.6c0-7.6,6.3-13.6,13.9-13.6c3.4,0,6.3,1.2,9.1,3.4l-2.9,3.6\n" | |||||
| "\t\t\tc-2.6-1.8-4.1-2.4-6.2-2.4c-4.8,0-8.8,3.9-8.8,9c0,5,3.9,9,8.8,9c2,0,3.6-0.6,6.1-2.4l3,3.7C104.6,167,102,167.8,99,167.8z\"/>\n" | |||||
| "\t\t<path class=\"st0\" d=\"M111.3,167.4v-26.6h16.6v4.5h-11.6v6.4h11.2v4.5h-11.2v6.7h11.6v4.5H111.3z\"/>\n" | |||||
| "\t</g>\n" | |||||
| "\t<g>\n" | |||||
| "\t\t<circle class=\"st1\" cx=\"84.9\" cy=\"74.9\" r=\"37.4\"/>\n" | |||||
| "\t\t<circle class=\"st0\" cx=\"84.9\" cy=\"74.9\" r=\"28\"/>\n" | |||||
| "\t\t<circle class=\"st1\" cx=\"84.9\" cy=\"67.9\" r=\"2.1\"/>\n" | |||||
| "\t\t<circle class=\"st1\" cx=\"91.4\" cy=\"72.6\" r=\"2.1\"/>\n" | |||||
| "\t\t<circle class=\"st1\" cx=\"88.9\" cy=\"80.3\" r=\"2.1\"/>\n" | |||||
| "\t\t<circle class=\"st1\" cx=\"80.8\" cy=\"80.3\" r=\"2.1\"/>\n" | |||||
| "\t\t<circle class=\"st1\" cx=\"78.3\" cy=\"72.6\" r=\"2.1\"/>\n" | |||||
| "\t</g>\n" | |||||
| "\t<g>\n" | |||||
| "\t\t<path class=\"st1\" d=\"M48.2,131.7v-4.6h-4.3v4.6h-2V121h2v4.3h4.3V121h2v10.8H48.2z\"/>\n" | |||||
| "\t\t<path class=\"st1\" d=\"M56.7,131.7v-0.5c-0.5,0.4-1.3,0.7-2.1,0.7c-1.9,0-3.2-1.3-3.2-3.4v-5h2v4.8c0,1.2,0.7,1.8,1.7,1.8\n" | |||||
| "\t\t\tc1,0,1.7-0.7,1.7-1.8v-4.8h2v8.3H56.7z\"/>\n" | |||||
| "\t\t<path class=\"st1\" d=\"M63.8,131.9c-2.4,0-4.3-1.9-4.3-4.3c0-2.4,1.9-4.3,4.3-4.3c1.1,0,2.1,0.4,3.3,1.5l-1.3,1.2\n" | |||||
| "\t\t\tc-0.7-0.7-1.3-1-2-1c-1.3,0-2.3,1.1-2.3,2.5c0,1.3,1,2.5,2.3,2.5c0.6,0,1.3-0.2,2-0.9l1.3,1.2C66,131.5,65,131.9,63.8,131.9z\"/>\n" | |||||
| "\t\t<path class=\"st1\" d=\"M73.8,131.7l-3.9-3.9v3.9h-2v-11.3h2v6.8l3.6-3.7h2.5l-3.9,4l4.3,4.3H73.8z\"/>\n" | |||||
| "\t\t<path class=\"st1\" d=\"M77.1,131.7v-11.3h2v11.3H77.1z\"/>\n" | |||||
| "\t\t<path class=\"st1\" d=\"M88.6,128.4h-6.2c0.1,1,1.1,1.7,2.1,1.7c0.7,0,1.5-0.3,2.3-1.1l1.3,1.2c-1.1,1.2-2.3,1.7-3.6,1.7\n" | |||||
| "\t\t\tc-2.4,0-4.3-1.9-4.3-4.3c0-2.4,1.9-4.3,4.3-4.3c2.4,0,4.1,1.9,4.1,4.2C88.7,127.9,88.6,128.4,88.6,128.4z M84.5,125.1\n" | |||||
| "\t\t\tc-1,0-1.9,0.6-2.1,1.5h4.1C86.3,125.8,85.5,125.1,84.5,125.1z\"/>\n" | |||||
| "\t\t<path class=\"st1\" d=\"M93.9,131.9c-0.9,0-1.7-0.3-2.3-0.8v0.6h-2v-11.3h2v3.7c0.6-0.5,1.4-0.8,2.3-0.8c2.3,0,4.2,1.9,4.2,4.3\n" | |||||
| "\t\t\tC98.1,129.9,96.2,131.9,93.9,131.9z M93.9,125.1c-1.3,0-2.2,1.1-2.2,2.5c0,1.4,1,2.5,2.2,2.5c1.3,0,2.2-1.1,2.2-2.5\n" | |||||
| "\t\t\tC96.2,126.2,95.2,125.1,93.9,125.1z\"/>\n" | |||||
| "\t\t<path class=\"st1\" d=\"M107.4,128.4h-6.2c0.1,1,1.1,1.7,2.1,1.7c0.7,0,1.5-0.3,2.3-1.1l1.3,1.2c-1.1,1.2-2.3,1.7-3.6,1.7\n" | |||||
| "\t\t\tc-2.4,0-4.3-1.9-4.3-4.3c0-2.4,1.9-4.3,4.3-4.3c2.4,0,4.1,1.9,4.1,4.2C107.5,127.9,107.4,128.4,107.4,128.4z M103.3,125.1\n" | |||||
| "\t\t\tc-1,0-1.9,0.6-2.1,1.5h4.1C105.1,125.8,104.3,125.1,103.3,125.1z\"/>\n" | |||||
| "\t\t<path class=\"st1\" d=\"M110.4,127.8v3.9h-2v-8.3h2v0.8c0.7-0.6,1.4-0.9,2.6-1v1.8C111,125.4,110.4,126.6,110.4,127.8z\"/>\n" | |||||
| "\t\t<path class=\"st1\" d=\"M116,127.8v3.9h-2v-8.3h2v0.8c0.7-0.6,1.4-0.9,2.6-1v1.8C116.6,125.4,116,126.6,116,127.8z\"/>\n" | |||||
| "\t\t<path class=\"st1\" d=\"M122.2,134.6h-2.1l2.2-4.2l-3.4-6.9h2.1l2.4,4.8l2.3-4.8h2.2L122.2,134.6z\"/>\n" | |||||
| "\t</g>\n" | |||||
| "</g>\n" | |||||
| "</svg>\n"; | |||||
| "<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"720\" height=\"358\" version=\"1.1\">\n" | |||||
| " <g id=\"g1\" transform=\"translate(-26.149211,-6.9701601)\">\n" | |||||
| " <path id=\"path1\" style=\"fill:#bd0000;\" d=\"m 107.04146,18.25331 c 2e-5,0 -59.309734,258.76742 -59.309734,258.76742 0.01705,0 0.01919,0 0.05329,0 0,0 274.380814,0 274.380814,0 10e-6,0 142.70573,0 142.70573,0 -17.00107,-1.48353 -30.42327,-7.451" | |||||
| "82 -38.42076,-18.33112 -1.48353,-2.01219 -2.70918,-4.17993 -3.78347,-6.44788 -0.68207,-1.6711 -1.32368,-3.35715 -1.97167,-5.06237 -1.70524,-5.42261 -2.38517,-11.43991 -2.07824,-17.85154 0.85262,-17.87074 9.23805,-39.12802 23.76654,-61.38798 15.43225," | |||||
| "-23.65656 37.79837,-48.45089 65.49116,-71.45944 6.20699,-5.15319 12.62503,-10.24199 19.34361,-15.18714 8.30441,-6.10979 16.67281,-11.73661 25.04545,-16.99893 30.84745,-19.42077 61.61817,-33.01012 88.88467,-39.91282 -26.05577,8.50051 -55.32805,22.9313" | |||||
| "8 -84.24861,42.57724 -1.04016,0.70255 -2.06758,1.42215 -3.09072,2.13152 -16.04613,11.11805 -30.58314,22.92586 -43.26998,34.85046 -40.82303,38.38457 -62.40046,78.15884 -52.59544,101.46069 0.69916,1.6029 1.53256,3.13335 2.50455,4.58279 10.53825,15.5004" | |||||
| "7 36.65587,18.17979 69.75422,9.85831 2.25089,-0.56275 4.50178,-1.20004 6.82088,-1.86508 10.57238,-3.05237 21.797,-7.17473 33.3584,-12.25628 2.77949,-1.22777 5.58887,-2.48963 8.41953,-3.83676 0.28988,-0.13641 0.56273,-0.27283 0.85261,-0.4263 36.49173," | |||||
| "-18.13162 64.53625,-38.99458 70.60684,-51.52967 1.33008,-2.72662 1.6498,-5.0513 0.74603,-6.87417 -4.33124,-8.78188 -34.63945,-2.849 -71.35287,13.0556 -2.95003,1.2789 -5.93417,2.59492 -8.95241,3.99661 2.45552,-2.27986 5.01123,-4.56828 7.6202,-6.82089 " | |||||
| "4.16075,-3.58438 8.46856,-7.08945 13.0556,-10.55104 7.179,-5.43627 14.43685,-10.35071 21.58173,-14.70754 33.45643,-25.24067 50.3701,-49.11722 46.8403,-57.97753 -0.66505,-1.66943 -2.08037,-2.81789 -4.26306,-3.51703 -6.97438,-2.21847 -19.70383,0.42929 " | |||||
| "-35.17019,6.7676 -12.75504,5.23164 -27.40504,12.9908 -42.25752,22.64748 0,0 -2.02495,1.3322 -2.02495,1.3322 2e-5,0 -0.37302,0.21314 -0.37302,0.21314 2e-5,2e-5 -7.14061,4.74265 -7.14061,4.74265 -2e-5,-1e-5 4.10319,-7.51363 4.10319,-7.51363 6.08765,-11" | |||||
| ".14364 16.0035,-22.77067 28.3493,-33.57154 9.05473,-7.89689 19.39903,-15.33762 30.53413,-21.84816 4.21191,-2.46573 8.4174,-4.75586 12.62929,-6.82088 4.31422,-2.10769 8.63055,-3.98089 12.84246,-5.64859 12.23682,-4.8393 23.91781,-7.7798 34.10442,-8.579" | |||||
| "4 -0.92767,0 -28.61574,0 -28.61574,0 10e-6,0 -96.45159,0 -96.45159,0 10e-6,0 -425.29295,0 -425.29295,0 0,0 -33.73141,0 -33.73141,0 z m 305.98071,0.3731 c -1.67109,25.50496 3.27403,62.86211 14.49439,104.3382 1.99509,7.38021 4.16712,14.89085 6.55444,22" | |||||
| ".48762 1.48356,4.69104 3.01399,9.30881 4.58278,13.85492 -0.9208,1.35396 -1.84804,2.69937 -2.7177,4.0499 -13.99985,21.45508 -22.1828,41.73104 -24.24611,59.36303 -5.91714,-19.81468 -10.37201,-41.5183 -12.89574,-64.53198 -0.71619,-6.5941 -1.27679,-13.11" | |||||
| "442 -1.65194,-19.55675 -2.76247,-46.79639 3.34649,-89.2172 15.87988,-120.00494 z m -279.3899,33.1985 c 0.31934,-0.0075 0.63414,0 0.95918,0 0,-2e-5 63.03991,0 63.03991,0 5.38851,-2e-5 9.55564,1.00991 12.52272,3.03742 2.95002,2.03946 4.05631,4.67743 3." | |||||
| "35715,7.88665 2e-5,2e-5 -7.51363,33.94456 -7.51363,33.94456 0,3e-5 -22.22116,0 -22.22116,0 2e-5,3e-5 7.30048,-33.03866 7.30048,-33.03866 -2e-5,0 -55.63285,0 -55.63285,0 -2e-5,0 -20.08965,90.80305 -20.08965,90.80305 2e-5,0 -7.24719,32.87881 -7.24719,3" | |||||
| "2.87881 0,3e-5 55.57957,0 55.57957,0 0,3e-5 0.0533,-0.21316 0.0533,-0.21316 0,2e-5 11.13722,-50.30404 11.13722,-50.30404 0,-2e-5 -26.1112,0 -26.1112,0 0,-2e-5 2.50454,-11.35038 2.50454,-11.35038 -1e-5,-2e-5 48.33238,0 48.33238,0 0,-2e-5 -13.85493,62." | |||||
| "77348 -13.85493,62.77348 -0.18757,0.89352 -0.51582,1.70308 -0.95919,2.50453 -1.14249,2.08037 -3.13547,3.86234 -5.91498,5.32883 -3.85378,2.02923 -8.46643,3.03742 -13.85493,3.03742 0,2e-5 -62.98662,0 -62.98662,0 -5.388524,2e-5 -9.555644,-1.00821 -12.52" | |||||
| "2724,-3.03742 -2.95001,-2.0292 -4.07335,-4.62073 -3.35715,-7.83336 10e-6,-2e-5 27.763134,-125.49366 27.763134,-125.49366 0.71618,-3.20921 2.95005,-5.84721 6.82089,-7.88665 0.13642,-0.07331 0.28989,-0.08995 0.4263,-0.15986 3.53302,-1.7697 7.67937,-2.7" | |||||
| "6443 12.46944,-2.87756 z m 98.79628,0 c -2e-5,-2e-5 79.34608,0 79.34608,0 5.32031,-2e-5 9.50235,1.00991 12.46943,3.03742 2.98417,2.03946 4.12666,4.67743 3.41045,7.88665 2e-5,2e-5 -14.81411,66.92995 -14.81411,66.92995 -0.69912,3.18874 -2.98627,5.7905 " | |||||
| "-6.87417,7.83336 -3.8879,2.04966 -8.4643,3.09071 -13.80164,3.09071 -2e-5,0 -57.6578,0 -57.6578,0 0,0 -11.13723,50.25075 -11.13723,50.25075 0,2e-5 -1.8118,8.25967 -1.8118,8.25967 1e-5,2e-5 -21.74158,0 -21.74158,0 0,2e-5 1.65193,-7.46034 1.65193,-7.460" | |||||
| "34 0,2e-5 30.96044,-139.82817 30.96044,-139.82817 z m 109.61377,0 c -1e-5,-2e-5 21.74158,0 21.74158,0 2e-5,-2e-5 -24.61914,111.21241 -24.61914,111.21241 2e-5,2e-5 -5.3821,24.29942 -5.3821,24.29942 0,3e-5 52.00926,0 52.00926,0 0.54568,3.98854 1.18299," | |||||
| "7.90582 1.86508,11.77668 -2e-5,2e-5 -78.22705,0 -78.22705,0 2e-5,2e-5 6.18143,-27.92301 6.18143,-27.92301 2e-5,0 26.43094,-119.3655 26.43094,-119.3655 z M 251.559,63.65488 c 2e-5,0 -14.4411,65.11814 -14.4411,65.11814 0,-2e-5 54.19408,0 54.19408,0 -1e" | |||||
| "-5,-2e-5 14.3878,-65.11814 14.3878,-65.11814 2e-5,0 -54.14078,0 -54.14078,0 z m -45.5614,145.68987 c 0.21267,-0.007 0.42524,0 0.63947,0 2.52373,2e-5 4.72772,0.44336 6.50115,1.27891 1.8928,0.88671 3.24632,2.204 3.99662,3.94333 0.73325,1.68819 0.92508," | |||||
| "3.60441 0.53287,5.70184 0,1e-5 -0.15985,0.79933 -0.15985,0.79933 1e-5,-3e-5 -6.23472,0 -6.23472,0 -2e-5,-3e-5 0.0533,-0.74605 0.0533,-0.74605 0.16118,-1.46543 -0.0205,-2.61779 -0.58618,-3.41043 -0.0737,-0.0994 -0.18053,-0.23063 -0.26644,-0.31973 -0.1" | |||||
| "4068,-0.14363 -0.35198,-0.3078 -0.53287,-0.42631 -0.87238,-0.5495 -2.23703,-0.85262 -3.99662,-0.85262 -2.33615,2e-5 -4.08187,0.40712 -5.27552,1.22564 -1.15956,0.80146 -1.8928,1.6967 -2.13153,2.77098 -0.23875,1.09134 0.15346,1.66472 0.42631,1.97166 0." | |||||
| "008,0.008 0.0429,0.0438 0.0533,0.0533 0.34552,0.29445 1.54536,0.98476 5.27553,1.86508 3.37632,0.81851 5.61232,1.55389 6.87418,2.18482 1.90987,0.97196 3.24419,2.2317 3.94334,3.78346 0.69911,1.53472 0.8526,3.33157 0.4263,5.27553 -0.4263,1.89279 -1.3918" | |||||
| "9,3.63852 -2.82428,5.27554 -1.41532,1.637 -3.21647,2.93297 -5.38211,3.83673 -2.1486,0.88672 -4.50604,1.38549 -6.92746,1.38549 -3.0694,-1e-5 -5.53343,-0.46467 -7.46034,-1.38549 -2.01217,-0.95491 -3.46374,-2.4619 -4.31634,-4.42291 -0.81851,-1.90986 -0." | |||||
| "9933,-4.07762 -0.53289,-6.44786 0,-3e-5 0.15987,-0.74605 0.15987,-0.74605 0,0 6.12814,0 6.12814,0 0,0 -0.0533,0.74605 -0.0533,0.74605 -0.15348,1.41532 -0.002,2.55782 0.37302,3.41043 0.35809,0.81853 1.04019,1.49633 2.13152,2.02496 1.15954,0.5627 2.608" | |||||
| "98,0.8526 4.26305,0.8526 1.48355,-1e-5 2.83921,-0.24939 4.04991,-0.69273 1.19365,-0.4263 2.14218,-0.98904 2.82427,-1.70523 0.66502,-0.71622 1.05511,-1.47288 1.22563,-2.2914 0.17052,-0.73324 0.13002,-1.3535 -0.15987,-1.86508 -0.32399,-0.54564 -0.96558" | |||||
| ",-1.02953 -1.97165,-1.43878 0,-1e-5 -5.22226,-1.54536 -5.22226,-1.54536 -2.91592,-0.73323 -4.87905,-1.43238 -6.02155,-2.13152 -1.55177,-0.92081 -2.67081,-2.10169 -3.25059,-3.51702 -0.57977,-1.39831 -0.64158,-2.94791 -0.26643,-4.63608 0.3922,-1.80753 " | |||||
| "1.26399,-3.52767 2.61111,-5.06237 1.3642,-1.55177 3.12482,-2.76885 5.22225,-3.57031 1.85443,-0.6954 3.80596,-1.10321 5.86169,-1.17234 z m 55.9526,0.10658 c 0.33725,-0.0263 0.65436,0 1.01247,0 10e-6,0 3.83675,0.4263 3.83675,0.4263 -10e-6,0 1.27891,0.1" | |||||
| "0658 1.27891,0.10658 2e-5,1e-5 -1.86508,4.74264 -1.86508,4.74264 -1e-5,2e-5 -0.37302,0.74604 -0.37302,0.74604 0,-2e-5 -2.93084,-0.26644 -2.93084,-0.26644 -0.86541,0 -1.50113,0.13352 -1.91838,0.4263 -0.0277,0.0209 -0.081,0.0842 -0.10658,0.10658 -0.055" | |||||
| "9,0.049 -0.14991,0.13458 -0.21315,0.21316 -0.25659,0.33651 -0.56912,0.93786 -0.79932,2.02495 -2e-5,0 -0.11084,0.4604 -0.21315,0.85261 1.34713,1e-5 4.58277,0 4.58277,0 0,1e-5 -1.17233,5.27552 -1.17233,5.27552 0,-1e-5 -3.35076,0 -4.4762,0 -0.34106,1.53" | |||||
| "469 -4.5295,20.3028 -4.5295,20.3028 -2e-5,-2e-5 -6.18143,0 -6.18143,0 -2e-5,-2e-5 4.01793,-18.03485 4.5295,-20.3028 -1.04021,-1e-5 -3.57031,0 -3.57031,0 0,-1e-5 1.17234,-5.27552 1.17234,-5.27552 10e-6,1e-5 2.54504,0 3.51701,0 0.17052,-0.73324 0.4263," | |||||
| "-1.65194 0.4263,-1.65194 0.37516,-1.70522 0.76735,-2.93085 1.27891,-3.78346 0.69917,-1.17659 1.68606,-2.16137 2.93087,-2.87756 1.02527,-0.59575 2.32198,-0.95194 3.78346,-1.06576 z m 14.76082,0.26644 c -2e-5,-1e-5 -1.6157,7.27064 -2.02495,9.11228 1.19" | |||||
| "364,1e-5 3.99661,0 3.99661,0 0,1e-5 -1.17233,5.27552 -1.17233,5.27552 -2e-5,-1e-5 -2.93939,0 -3.99662,0 -0.32401,1.46647 -2.87756,12.84246 -2.87756,12.84246 2e-5,-2e-5 -0.21316,1.36631 -0.21316,1.75851 2.8e-4,0.0131 -10e-4,0.0437 0,0.0533 8.4e-4,0.00" | |||||
| "4 -10e-4,0.05 0,0.0533 0.002,0.003 0.0512,-0.002 0.0533,0 0,-2e-5 0.63947,0.0533 0.63947,0.0533 0,0 2.93085,-0.21315 2.93085,-0.21315 0,0 -0.31973,4.74264 -0.31973,4.74264 2e-5,0 0.0533,0.85261 0.0533,0.85261 -2e-5,-2e-5 -4.20978,0.4796 -4.20978,0.47" | |||||
| "96 -1.6711,-2e-5 -2.89675,-0.27283 -3.78346,-0.8526 -0.93785,-0.61392 -1.48354,-1.42814 -1.70522,-2.45126 -0.0341,-0.18759 -0.10658,-0.46042 -0.10658,-0.85261 0,-0.76736 0.14495,-2.06545 0.63946,-4.31634 2e-5,2e-5 2.24023,-10.03524 2.7177,-12.14972 -" | |||||
| "0.80143,-1e-5 -2.93085,0 -2.93085,0 2e-5,-1e-5 1.17233,-5.27552 1.17233,-5.27552 -2e-5,10e-6 2.07824,0 2.93086,0 0.27281,-1.22775 1.22562,-5.4354 1.22562,-5.4354 0,2e-5 4.95581,-2.61112 4.95581,-2.61112 -10e-6,2e-5 2.02495,-1.06576 2.02495,-1.06576 z" | |||||
| " m -196.366934,0.21315 c 2e-5,0 23.979684,0 23.979684,0 0,0 -1.3322,5.96828 -1.3322,5.96828 0,2e-5 -15.997114,0 -17.531814,0 -0.27282,1.21073 -1.36631,6.11322 -1.75851,7.88665 2.2168,0 15.18713,0 15.18713,0 0,0 -1.33221,5.96826 -1.33221,5.96826 -1e-5" | |||||
| ",0 -13.65242,0 -15.18713,0 -0.32399,1.48355 -3.25058,14.65426 -3.25058,14.65426 2e-5,-2e-5 -6.44786,0 -6.44786,0 1e-5,-2e-5 7.67349,-34.47745 7.67349,-34.47745 z m 32.132764,8.41954 c 0.24067,-0.0296 0.50358,0 0.74604,0 1.5347,0 2.95217,0.4668 4.3163" | |||||
| "5,1.43877 0,2e-5 0.8526,0.58617 0.8526,0.58617 0,0 -3.30386,5.4354 -3.30386,5.4354 0,-2e-5 -0.90589,-0.63946 -0.90589,-0.63946 -0.66504,-0.42629 -1.38123,-0.63946 -2.13153,-0.63946 -0.64799,2e-5 -1.25121,0.2302 -1.86509,0.63946 -0.64797,0.4263 -1.189" | |||||
| "4,0.99115 -1.59864,1.75851 -0.7162,1.31304 -1.24056,2.78377 -1.59865,4.36963 0,1e-5 -2.87756,13.10889 -2.87756,13.10889 0,-2e-5 -6.181434,0 -6.181434,0 0,-2e-5 5.701844,-25.57832 5.701844,-25.57832 2e-5,1e-5 5.75512,0 5.75512,0 0,1e-5 -0.19824,0.7758" | |||||
| "8 -0.26645,1.06576 0.27284,-0.20461 0.56059,-0.48599 0.79933,-0.63946 0.82597,-0.4929 1.69837,-0.80035 2.55782,-0.90589 z m 18.33114,0 c 0.37219,-0.0282 0.73859,0 1.11906,0 3.47866,0 6.11108,1.21922 7.83335,3.62359 1.07428,1.55174 1.65195,3.47012 1.6" | |||||
| "5195,5.75512 0,1.26185 -0.15561,2.61965 -0.47959,4.10319 0,0 -0.47961,1.86509 -0.47961,1.86509 0,-2e-5 -15.63261,0 -17.37194,0 -0.0341,0.35808 -0.0533,0.74177 -0.0533,1.06576 0,1.33009 0.27283,2.37665 0.85261,3.14401 0.2755,0.37513 0.5949,0.71299 0.9" | |||||
| "5919,0.95918 0.68678,0.44789 1.57334,0.69275 2.61112,0.69275 1.26188,-2e-5 2.37025,-0.34745 3.41044,-1.01248 0.98905,-0.63093 1.93544,-1.63914 2.77099,-3.03742 0,-3e-5 6.60773,0 6.60773,0 0,-3e-5 -0.58616,1.27891 -0.58616,1.27891 -1.26187,2.55785 -3." | |||||
| "02251,4.55294 -5.22225,5.96828 -2.19973,1.41533 -4.82791,2.1848 -7.72679,2.1848 -3.76854,-1e-5 -6.57363,-1.21922 -8.31296,-3.62358 -1.72226,-2.35323 -2.09315,-5.60593 -1.17233,-9.69846 0.92083,-4.16073 2.7582,-7.48591 5.43539,-9.80502 2.39958,-2.0806" | |||||
| "2 5.12245,-3.23468 8.15309,-3.46372 z m 27.76315,0 c 0.37218,-0.0282 0.73857,0 1.11904,0 3.47867,0 6.1111,1.21922 7.83337,3.62359 1.07429,1.55174 1.65193,3.47012 1.65193,5.75512 0,1.2448 -0.1556,2.58341 -0.4796,4.0499 0,2e-5 -0.47958,1.91838 -0.47958" | |||||
| ",1.91838 0,-2e-5 -15.63263,0 -17.37195,0 -0.0171,0.2387 -0.0362,0.45402 -0.0533,0.69275 -0.0128,0.0895 0.006,0.22541 0,0.31972 -5e-4,0.0154 0,0.0384 0,0.0533 0,0.2558 0.0362,0.5073 0.0533,0.74604 0.0852,0.97196 0.33892,1.78409 0.79932,2.39797 0.0877," | |||||
| "0.11936 0.17008,0.2667 0.26644,0.37302 0.78101,0.83554 1.8864,1.27891 3.30387,1.27891 1.26188,-2e-5 2.38731,-0.34745 3.41045,-1.01248 0.98904,-0.63093 1.93542,-1.63914 2.77098,-3.03742 0,-3e-5 6.66102,0 6.66102,0 -1e-5,-3e-5 -0.63946,1.27891 -0.63946" | |||||
| ",1.27891 -1.26186,2.55785 -3.0225,4.55294 -5.22224,5.96828 -2.19974,1.41533 -4.82791,2.1848 -7.72678,2.1848 -3.76853,-1e-5 -6.57363,-1.21922 -8.31296,-3.62358 -1.12545,-1.53472 -1.65194,-3.48931 -1.65194,-5.80841 0,-0.35812 0.0192,-0.74391 0.0533,-1." | |||||
| "11905 0.0682,-0.88671 0.20463,-1.78196 0.42631,-2.771 0.92084,-4.14368 2.70491,-7.48591 5.3821,-9.80502 2.39957,-2.08062 5.17575,-3.23468 8.20639,-3.46372 z m 75.61591,0 c 0.38084,-0.0293 0.73112,0 1.11905,0 3.54687,0 6.2731,1.20004 8.04652,3.5703 1." | |||||
| "7564,2.37023 2.18269,5.56754 1.27891,9.59186 -0.69913,3.13763 -1.70947,5.672 -3.0907,7.51365 -1.38126,1.84165 -3.14188,3.34649 -5.22226,4.36962 -2.06328,1.02314 -4.24813,1.54535 -6.44786,1.54535 -3.61507,-1e-5 -6.30719,-1.23628 -8.04652,-3.62358 -1.1" | |||||
| "2546,-1.51764 -1.70522,-3.47226 -1.70522,-5.80841 2e-5,-1.26187 0.13856,-2.6026 0.4796,-4.10319 1.02315,-4.57001 3.11202,-8.05078 6.18143,-10.28462 2.25301,-1.62634 4.74114,-2.56606 7.40705,-2.77098 z m 93.62733,0 c 0.4496,-0.0277 0.86593,0 1.3322,0 " | |||||
| "2.14859,0 3.83675,0.2174 5.11567,0.74602 1.36418,0.54569 2.33189,1.31729 2.87756,2.23811 0.52864,0.86963 0.79933,1.93756 0.79933,3.25057 10e-6,2e-5 -0.63948,3.89004 -0.63948,3.89004 3e-5,0 -1.17232,5.27554 -1.17232,5.27554 -1.00611,4.50176 -1.22776,6" | |||||
| ".18995 -1.27893,6.82089 -0.0512,0.85262 0.0277,1.6839 0.26645,2.45125 2e-5,-2e-5 0.4263,1.38549 0.4263,1.38549 2e-5,-2e-5 -6.288,0 -6.288,0 -2e-5,-2e-5 -0.26645,-0.79933 -0.26645,-0.79933 -0.10231,-0.37512 -0.0725,-0.8526 -0.10657,-1.27891 -1.19365,0" | |||||
| ".73325 -2.38944,1.38336 -3.46373,1.75851 -1.58584,0.54569 -3.23139,0.8526 -4.90251,0.8526 -2.89886,-1e-5 -5.04532,-0.75454 -6.3413,-2.2381 -0.98902,-1.09133 -1.43877,-2.42567 -1.43877,-3.94332 -2e-5,-0.57977 0.0767,-1.18087 0.21314,-1.81179 0.28988,-" | |||||
| "1.29597 0.81638,-2.49603 1.65194,-3.57032 0.81851,-1.05726 1.78623,-1.92689 2.87756,-2.55782 1.05726,-0.63092 2.23598,-1.07855 3.46374,-1.3855 -2e-5,0 3.78346,-0.63946 3.78346,-0.63946 2.91594,-0.34106 5.09221,-0.76521 6.66102,-1.22562 0.0341,-0.1705" | |||||
| "2 0.10657,-0.37302 0.10657,-0.37302 0.24726,-1.09718 0.21632,-1.92188 -0.0533,-2.34468 -0.0198,-0.0273 -0.0842,-0.0831 -0.10657,-0.10658 -0.0576,-0.0614 -0.14603,-0.15779 -0.21316,-0.21315 -0.64135,-0.50753 -1.73506,-0.74603 -3.19728,-0.74603 -1.6540" | |||||
| "7,0 -2.87756,0.27071 -3.73018,0.79931 -0.83555,0.52863 -1.60931,1.49634 -2.29139,2.87756 -1e-5,-1e-5 -6.44787,0 -6.44787,0 2e-5,-1e-5 0.58617,-1.27891 0.58617,-1.27891 0.76733,-1.77345 1.68178,-3.24418 2.82428,-4.36963 1.1425,-1.12547 2.68572,-1.9972" | |||||
| "4 4.4762,-2.61111 1.37216,-0.45297 2.8705,-0.75354 4.47621,-0.85261 z m 27.01711,0 c 0.24046,-0.0296 0.50356,0 0.74603,0 1.55173,0 3.00545,0.4668 4.36963,1.43877 2e-5,2e-5 0.79932,0.58617 0.79932,0.58617 1e-5,0 -3.30386,5.4354 -3.30386,5.4354 10e-6,-" | |||||
| "2e-5 -0.9059,-0.63946 -0.9059,-0.63946 -0.66503,-0.42629 -1.34501,-0.63946 -2.07824,-0.63946 -0.64801,2e-5 -1.28744,0.2302 -1.91839,0.63946 -0.64797,0.4263 -1.11904,0.99115 -1.54534,1.75851 -0.69916,1.31304 -1.24056,2.78377 -1.59865,4.36963 -2e-5,1e-" | |||||
| "5 -2.93085,13.10889 -2.93085,13.10889 2e-5,-2e-5 -6.18142,0 -6.18142,0 0,-2e-5 5.70182,-25.57832 5.70182,-25.57832 -10e-6,1e-5 5.75512,0 5.75512,0 0,1e-5 -0.14494,0.77588 -0.21314,1.06576 0.27282,-0.20461 0.54353,-0.48599 0.79932,-0.63946 0.81263,-0." | |||||
| "4929 1.64571,-0.80035 2.50455,-0.90589 z m 18.27783,0 c 0.37185,-0.0282 0.73859,0 1.11906,0 3.47866,0 6.12813,1.21922 7.83337,3.62359 1.09135,1.55174 1.65193,3.47012 1.65193,5.75512 -2e-5,1.26185 -0.2089,2.61965 -0.53289,4.10319 0,0 -0.4263,1.86509 -" | |||||
| "0.4263,1.86509 -10e-6,-2e-5 -15.64968,0 -17.37194,0 -0.0341,0.35808 -0.10659,0.74177 -0.10659,1.06576 0,1.33009 0.28989,2.37665 0.85262,3.14401 0.0877,0.11936 0.223,0.2667 0.31972,0.37302 0.056,0.0597 0.15412,0.15773 0.21316,0.21314 0.76235,0.69302 1" | |||||
| ".79981,1.06577 3.0907,1.06577 1.26189,-2e-5 2.37027,-0.34745 3.41045,-1.01248 0.98905,-0.63093 1.91839,-1.63914 2.77099,-3.03742 -2e-5,-3e-5 6.66102,0 6.66102,0 0,-3e-5 -0.63946,1.27891 -0.63946,1.27891 -1.26187,2.55785 -3.03955,4.55294 -5.22224,5.96" | |||||
| "828 -2.21677,1.41533 -4.81085,2.1848 -7.72679,2.1848 -3.76851,-1e-5 -6.52034,-1.21922 -8.25966,-3.62358 -1.7223,-2.35323 -2.1294,-5.60593 -1.22563,-9.69846 0.92081,-4.16073 2.70491,-7.48591 5.38211,-9.80502 2.41475,-2.08062 5.17852,-3.23468 8.20637,-" | |||||
| "3.46372 z m -93.3076,0.47959 c 2e-5,1e-5 6.34131,0 6.34131,0 2e-5,1e-5 0.78226,15.17861 0.79931,15.40027 0.25578,-0.54356 0.45424,-1.00087 0.47961,-1.06575 1e-5,1e-5 6.98074,-14.33452 6.98074,-14.33452 2e-5,1e-5 5.80841,0 5.80841,0 -10e-6,1e-5 0.5328" | |||||
| "8,14.99528 0.53288,15.08054 0.17055,-0.30691 7.93994,-15.08054 7.93994,-15.08054 -2e-5,1e-5 6.28801,0 6.28801,0 -10e-6,1e-5 -13.85493,25.57832 -13.85493,25.57832 2e-5,-2e-5 -5.70183,0 -5.70183,0 0,-2e-5 -0.55206,-13.86984 -0.58617,-14.65426 -2.35323," | |||||
| "4.8258 -7.14063,14.65426 -7.14063,14.65426 0,-2e-5 -5.86168,0 -5.86168,0 0,-2e-5 -2.02497,-25.57832 -2.02497,-25.57832 z m -148.88715,4.84922 c -1.46893,0.062 -2.80295,0.59149 -4.04989,1.59864 -0.98902,0.78441 -1.65621,1.86084 -2.18483,3.03743 0,2e-5" | |||||
| " 10.49777,0 10.49777,0 0.0171,-0.18756 0.0533,-0.41564 0.0533,-0.58617 -2e-5,-0.97195 -0.17266,-1.72653 -0.47959,-2.2381 -0.75028,-1.22777 -1.86295,-1.8118 -3.51702,-1.8118 -0.0991,0 -0.2218,-0.004 -0.31973,0 z m 27.76313,0 c -1.46892,0.062 -2.80295," | |||||
| "0.59149 -4.0499,1.59864 -0.98904,0.78441 -1.63913,1.86084 -2.1848,3.03743 0,2e-5 10.49776,0 10.49776,0 0.017,-0.18756 0.0533,-0.41564 0.0533,-0.58617 -1e-5,-0.97195 -0.1556,-1.72653 -0.4796,-2.2381 -0.14894,-0.24939 -0.35043,-0.49739 -0.53288,-0.6927" | |||||
| "4 -0.71199,-0.73572 -1.6919,-1.11906 -2.98413,-1.11906 -0.0991,0 -0.2218,-0.004 -0.31974,0 z m 214.75136,0 c -1.51056,0.0465 -2.92125,0.55794 -4.20977,1.59864 -0.97198,0.78441 -1.63915,1.86084 -2.18482,3.03743 2e-5,2e-5 10.44449,0 10.44449,0 0.017,-0" | |||||
| ".18756 0.0533,-0.41564 0.0533,-0.58617 -1e-5,-0.97195 -0.1556,-1.72653 -0.4796,-2.2381 -0.73324,-1.21072 -1.86296,-1.79474 -3.51702,-1.8118 -0.049,5.3e-4 -0.0578,-0.001 -0.10657,0 z m -139.61502,0.10658 c -1.40963,0.16739 -2.72036,0.81051 -3.94333,1." | |||||
| "91837 -1.46648,1.33009 -2.53012,3.41897 -3.14399,6.18143 -0.25579,1.15955 -0.37303,2.15071 -0.37303,3.03742 2e-5,1.2107 0.23233,2.16137 0.69275,2.87756 0.12257,0.18384 0.28516,0.37811 0.42631,0.53289 0.75804,0.80488 1.81659,1.22563 3.144,1.22563 1.72" | |||||
| "226,-2e-5 3.23992,-0.64159 4.68935,-1.97167 1.46652,-1.34714 2.51307,-3.45734 3.144,-6.288 0.5798,-2.59195 0.49878,-4.52736 -0.31972,-5.75512 -0.7844,-1.19366 -1.93116,-1.75851 -3.51701,-1.75851 -0.26911,0 -0.5383,-0.031 -0.79933,0 z m 96.29173,9.325" | |||||
| "43 c -1.44941,0.39218 -3.07367,0.72258 -5.22224,1.01248 -1.60289,0.23874 -2.74541,0.49024 -3.41045,0.74603 -0.57976,0.23872 -1.04658,0.56911 -1.43878,1.01248 -0.37516,0.42627 -0.62666,0.87392 -0.74602,1.38549 -0.0341,0.20464 -0.0533,0.41564 -0.0533,0" | |||||
| ".58617 0,0.0138 -3.1e-4,0.0396 0,0.0533 0.002,0.0406 -0.004,0.1207 0,0.15985 0.0107,0.0774 0.0322,0.1944 0.0533,0.26645 0.008,0.0237 0.0445,0.0834 0.0533,0.10657 0.0187,0.0458 0.0301,0.11611 0.0533,0.15987 0.0243,0.0433 0.0779,0.11849 0.10658,0.15986" | |||||
| " 0.0446,0.0614 0.1055,0.1556 0.15987,0.21315 0.42631,0.47746 1.27678,0.69275 2.50454,0.69275 1.34715,1e-5 2.6431,-0.25578 3.83675,-0.85261 1.17662,-0.57977 2.12513,-1.44518 2.82427,-2.45126 0.51159,-0.7162 0.92083,-1.8182 1.27892,-3.25057 z\"/>\n" | |||||
| " <path id=\"path2\" style=\"fill:none;stroke:#bd0000;stroke-width:8.52610779;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;\" d=\"m 417.07944,250.20635 c 0.69221,1.77962 1.38445,3.55633 2.1055,5.2941 1.11046,2.36077 2.39396,4.6206 " | |||||
| "3.93704,6.73045 8.33557,11.33086 22.29546,17.53495 40.01931,19.07947 0,0 19.06506,0 19.06506,0 6.02811,-0.47014 12.34467,-1.34696 18.89198,-2.62759 11.29193,-2.2079 23.26167,-5.5998 35.69288,-10.13244 9.41715,-3.43375 19.0939,-7.49624 28.91485,-12.21" | |||||
| "778 21.38688,-10.25215 43.48041,-23.47509 65.15575,-39.43235 7.13857,-5.25082 13.98872,-10.63863 20.56487,-16.1173 6.02813,-5.01576 11.8111,-10.10942 17.33446,-15.25927 15.41645,-14.85689 27.0545,-29.94597 33.64507,-43.1949 6.86452,-13.7969 8.23461,-" | |||||
| "25.59358 2.61028,-33.02493 -3.44673,-4.56146 -9.21527,-7.00589 -16.64228,-7.5813 33.45753,-25.10471 52.95528,-55.55833 45.96096,-74.7589 -2.22096,-6.1277 -7.10981,-10.6992 -14.11858,-13.2273 -3.11497,-1.1205 -6.61943,-1.8301 -10.4411,-2.1531 0,0 -10." | |||||
| "29683,0 -10.29683,0 -14.37808,1.0369 -35.28664,6.1853 -53.0249,14.8785\"/>\n" | |||||
| " <g id=\"g2\" style=\"fill:#bd0000;fill-opacity:1\" transform=\"scale(1.705222,1.705222)\">\n" | |||||
| " <path id=\"path1\" transform=\"translate(15.5783,177.825)\" d=\"m 0,36 c 0,0 7.46,0 7.46,0 0,0 3.35,-14 3.35,-14 0,0 12.12,0 12.12,0 0,0 1.56,-6 1.56,-6 0,0 -12.17,0 -12.17,0 0,0 2.02,-9 2.02,-9 0,0 12.95,0 12.95,0 0,0 1.56,-7 1.56,-7 C 28.85,0" | |||||
| " 8.4,0 8.4,0 8.4,0 0,36 0,36 z\"/>\n" | |||||
| " <path id=\"path2\" transform=\"translate(41.5283,187.825)\" d=\"m 7.51,26 c 0,0 2.24,-9.4 2.24,-9.4 1.18,-5.09 3.67,-9.6 8.38,-9.6 0.42,0 0.83,0.21 1.14,0.26 0,0 1.82,-7.21 1.82,-7.21 -0.42,0 -0.88,-0.05 -1.4,-0.05 -3.47,0 -6.38,2.44 -8.32,5.94" | |||||
| " 0,0 -0.2,0 -0.2,0 C 11.46,4.23 11.71,2.61 11.9,1 11.9,1 5.47,1 5.47,1 5.1,3.08 4.53,7.15 3.66,10.82 3.66,10.82 0,26 0,26 c 0,0 7.51,0 7.51,0 z\"/>\n" | |||||
| " <path id=\"path3\" transform=\"translate(62.1583,187.825)\" d=\"M 20.06,19.25 C 17.99,20.26 15.63,20 12.88,20 10.71,20 9.03,19.62 8.11,18.88 7.63,18.07 7.43,16.81 7.47,16 17.6,16.27 23.99,13.93 24.31,7.53 24.55,2.7 21.02,0 15.89,0 6.73,0 0.73,8" | |||||
| ".08 0.34,15.86 0,22.65 3.52,26 10.78,26 c 2.79,0 6.49,-0.32 9.52,-1.23 0,0 -0.24,-5.52 -0.24,-5.52 z M 17.32,7.53 C 17.2,9.91 14.26,10.05 8.47,10 9.1,7.91 11.22,6 14.69,6 c 1.71,0 2.69,0.65 2.63,1.53 z\"/>\n" | |||||
| " <path id=\"path4\" transform=\"translate(88.6783,187.825)\" d=\"M 20.05,19.25 C 17.98,20.26 15.62,20 12.88,20 10.7,20 9.03,19.62 8.11,18.88 7.63,18.07 7.42,16.81 7.46,16 17.6,16.27 23.98,13.93 24.31,7.53 24.55,2.7 21.02,0 15.89,0 6.72,0 0.72,8." | |||||
| "08 0.33,15.86 0,22.65 3.52,26 10.77,26 c 2.8,0 6.5,-0.32 9.53,-1.23 0,0 -0.25,-5.52 -0.25,-5.52 z M 17.32,7.53 C 17.2,9.91 14.26,10.05 8.46,10 9.1,7.91 11.21,6 14.68,6 c 1.71,0 2.7,0.65 2.64,1.53 z\"/>\n" | |||||
| " <path id=\"path5\" transform=\"translate(124.5883,187.825)\" d=\"M 22.74,26 C 22.8,22.83 23.56,17.85 24.35,14.58 24.35,14.58 27.56,1 27.56,1 25.52,0.31 22.34,0 19.28,0 6.86,0 0.7,9.2 0.27,17.82 0,23.23 2.93,26 7.49,26 c 2.95,0 6.28,-1.43 8.87,-" | |||||
| "5.73 0,0 0.11,0 0.11,0 -0.2,2.07 -0.44,4.08 -0.57,5.73 0,0 6.84,0 6.84,0 z M 17.61,11.72 C 16.15,18.08 13.17,20 10.95,20 8.88,20 7.98,18.53 8.1,16.39 8.34,11.56 12.16,6 16.98,6 c 0.78,0 1.39,-0.14 1.96,-0.28 0,0 -1.33,6 -1.33,6 z\"/>\n" | |||||
| " <path id=\"path6\" transform=\"translate(152.2183,187.825)\" d=\"m 0,24.82 c 1.44,1.12 4.46,1.13 7.61,1.18 6.73,0.05 11.81,-2.89 12.11,-8.29 0.17,-3.6 -2.62,-5.73 -5.35,-7.16 C 12.4,9.6 11.41,8.69 11.46,7.63 11.53,6.2 12.87,6 14.84,6 c 2.22,0 4" | |||||
| ",0.27 5.02,0.47 0,0 2.02,-5.42 2.02,-5.42 C 20.73,0.36 18.43,0 15.48,0 8.95,0 4.17,3.45 3.9,8.69 c -0.16,3.24 2.17,5.42 4.99,6.9 2.28,1.17 3.06,2.07 3,3.34 C 11.82,20.21 10.68,20 8.61,20 6.18,20 3.49,19.68 2.07,19.46 2.07,19.46 0,24.82 0,24.82 z\"/>\n" | |||||
| " <path id=\"path7\" transform=\"translate(184.6683,176.825)\" d=\"m 7.45,37 c 0,0 6.05,-25 6.05,-25 0,0 -7.41,0 -7.41,0 0,0 -6.09,25 -6.09,25 0,0 7.45,0 7.45,0 z M 10.92,9 C 13.46,9 15.72,6.9 15.86,3.3 15.98,0.86 14.4,0 12.07,0 9.64,0 7.42,1.58 " | |||||
| "7.29,3.94 7.17,6.32 8.75,9 10.92,9 z\"/>\n" | |||||
| " <path id=\"path8\" transform=\"translate(198.9583,188.515)\" d=\"m 7.46,25.31 c 0,0 2.64,-11.26 2.64,-11.26 1.37,-5.73 4.32,-7.74 6.75,-7.74 1.92,0 2.48,0.82 2.39,2.01 -0.05,0.96 -0.2,1.97 -0.4,2.87 0,0 -3.36,14.12 -3.36,14.12 0,0 7.46,0 7.46,0" | |||||
| " 0,0 3.54,-14.81 3.54,-14.81 C 26.75,9.22 27.05,7.31 27.11,6.15 27.33,1.64 25.08,0 20.94,0 c -3.32,0 -6.55,1.54 -9.1,4.88 0,0 -0.1,0 -0.1,0 0,0 0.68,-4.57 0.68,-4.57 0,0 -6.58,0 -6.58,0 C 5.41,2.45 4.86,5.04 4.08,8.06 4.08,8.06 0,25.31 0,25.31 c 0,0 " | |||||
| "7.46,0 7.46,0 z\"/>\n" | |||||
| " <path id=\"path9\" transform=\"translate(238.5183,177.825)\" d=\"m 0,36 c 0,0 7.46,0 7.46,0 0,0 3.36,-14 3.36,-14 0,0 12.11,0 12.11,0 0,0 1.56,-6 1.56,-6 0,0 -12.17,0 -12.17,0 0,0 2.02,-9 2.02,-9 0,0 12.95,0 12.95,0 0,0 1.56,-7 1.56,-7 C 28.85," | |||||
| "0 8.4,0 8.4,0 8.4,0 0,36 0,36 z\"/>\n" | |||||
| " <path id=\"path10\" transform=\"translate(264.4683,187.825)\" d=\"m 7.51,26 c 0,0 2.24,-9.4 2.24,-9.4 1.18,-5.09 3.67,-9.6 8.38,-9.6 0.42,0 0.83,0.21 1.14,0.26 0,0 1.82,-7.21 1.82,-7.21 -0.42,0 -0.88,-0.05 -1.4,-0.05 -3.47,0 -6.38,2.44 -8.32,5." | |||||
| "94 0,0 -0.2,0 -0.2,0 C 11.46,4.23 11.71,2.61 11.9,1 11.9,1 5.47,1 5.47,1 5.11,3.08 4.53,7.15 3.66,10.82 3.66,10.82 0,26 0,26 c 0,0 7.51,0 7.51,0 z\"/>\n" | |||||
| " <path id=\"path11\" transform=\"translate(285.0983,187.825)\" d=\"M 20.06,19.25 C 17.99,20.26 15.63,20 12.88,20 10.71,20 9.03,19.62 8.11,18.88 7.63,18.07 7.43,16.81 7.47,16 17.6,16.27 23.99,13.93 24.31,7.53 24.55,2.7 21.02,0 15.89,0 6.73,0 0.73" | |||||
| ",8.08 0.34,15.86 0,22.65 3.53,26 10.78,26 c 2.79,0 6.5,-0.32 9.52,-1.23 0,0 -0.24,-5.52 -0.24,-5.52 z M 17.32,7.53 C 17.2,9.91 14.26,10.05 8.47,10 9.1,7.91 11.22,6 14.69,6 c 1.71,0 2.69,0.65 2.63,1.53 z\"/>\n" | |||||
| " <path id=\"path12\" transform=\"translate(311.6183,187.825)\" d=\"M 20.05,19.25 C 17.98,20.26 15.62,20 12.88,20 10.7,20 9.03,19.62 8.11,18.88 7.63,18.07 7.42,16.81 7.46,16 17.6,16.27 23.98,13.93 24.31,7.53 24.55,2.7 21.02,0 15.89,0 6.72,0 0.72," | |||||
| "8.08 0.33,15.86 0,22.65 3.52,26 10.77,26 c 2.8,0 6.5,-0.32 9.53,-1.23 0,0 -0.25,-5.52 -0.25,-5.52 z M 17.32,7.53 C 17.2,9.91 14.26,10.05 8.46,10 9.1,7.91 11.21,6 14.68,6 c 1.71,0 2.7,0.65 2.64,1.53 z\"/>\n" | |||||
| " <path id=\"path13\" transform=\"translate(337.9983,176.825)\" d=\"M 23.31,0 C 23.31,0 20.5,11.36 20.5,11.36 19.48,10.94 18.15,11 17.11,11 7.53,11 0.75,19.2 0.3,28.08 0,34.34 3.34,37 7.64,37 c 3.01,0 6.18,-1.33 8.58,-4.77 0,0 0.1,0 0.1,0 0,0 -0." | |||||
| "57,4.77 -0.57,4.77 0,0 6.78,0 6.78,0 0.31,-3 0.96,-6.57 1.69,-9.83 0,0 6.49,-27.17 6.49,-27.17 0,0 -7.4,0 -7.4,0 z M 17.47,24.42 C 16.3,29.35 13.54,31 11.36,31 9.19,31 7.98,29.49 8.13,26.8 8.38,21.82 11.8,17 16.25,17 c 1.25,0 2.32,0.19 2.91,0.47 0,0 " | |||||
| "-1.69,6.95 -1.69,6.95 z\"/>\n" | |||||
| " <path id=\"path14\" transform=\"translate(367.7083,187.825)\" d=\"M 10.61,26 C 19.31,26 26.02,19.41 26.49,10.44 26.78,4.5 23.08,0 16.25,0 7.23,0 0.76,7.3 0.31,16.12 0,22.54 4.14,26 10.61,26 z m 1.17,-6 C 9.24,20 7.84,18.38 7.99,15.96 8.19,11.93" | |||||
| " 10.68,6 14.97,6 c 2.96,0 3.87,2.27 3.75,4.5 -0.22,4.4 -2.9,9.5 -6.94,9.5 z\"/>\n" | |||||
| " <path id=\"path15\" transform=\"translate(396.2683,187.825)\" d=\"M 7.2,26 C 7.2,26 9.9,14.58 9.9,14.58 11.03,9.49 13.85,6 16.38,6 c 1.82,0 2.32,1.33 2.23,3.07 -0.05,0.9 -0.25,1.91 -0.46,2.91 0,0 -3.34,14.02 -3.34,14.02 0,0 7.2,0 7.2,0 0,0 2.7," | |||||
| "-11.47 2.7,-11.47 C 25.95,9.28 28.61,6 31.09,6 c 1.71,0 2.42,1.22 2.34,2.96 -0.05,1.01 -0.26,2.12 -0.52,3.13 0,0 -3.24,13.91 -3.24,13.91 0,0 7.25,0 7.25,0 0,0 3.49,-14.81 3.49,-14.81 0.27,-1.33 0.58,-3.4 0.63,-4.46 C 41.26,2.33 39.11,0 35.23,0 31.91," | |||||
| "0 28.68,1.5 26.23,4.66 26.14,2.38 24.51,0 20.47,0 17.2,0 14.08,1.48 11.58,4.83 c 0,0 -0.1,0 -0.1,0 0,0 0.67,-3.83 0.67,-3.83 0,0 -6.42,0 -6.42,0 C 5.31,3.14 4.8,5.73 4.02,8.75 4.02,8.75 0,26 0,26 c 0,0 7.2,0 7.2,0 z\"/>\n" | |||||
| " </g>\n" | |||||
| " </g>\n" | |||||
| "</svg>"; | |||||
| const char* huckleberry_icon_svg = (const char*) temp_binary_data_17; | |||||
| const char* gpl_logo_svg = (const char*) temp_binary_data_17; | |||||
| //================== juce-logo-with-text.svg ================== | //================== juce-logo-with-text.svg ================== | ||||
| static const unsigned char temp_binary_data_18[] = | static const unsigned char temp_binary_data_18[] = | ||||
| @@ -7720,7 +7791,7 @@ const char* getNamedResource (const char* resourceNameUTF8, int& numBytes) | |||||
| case 0x96d2a1ce: numBytes = 28184; return export_linux_svg; | case 0x96d2a1ce: numBytes = 28184; return export_linux_svg; | ||||
| case 0x2505bd06: numBytes = 1706; return export_visualStudio_svg; | case 0x2505bd06: numBytes = 1706; return export_visualStudio_svg; | ||||
| case 0x3198e2bf: numBytes = 12295; return export_xcode_svg; | case 0x3198e2bf: numBytes = 12295; return export_xcode_svg; | ||||
| case 0x0cd37295: numBytes = 3375; return huckleberry_icon_svg; | |||||
| case 0xc9c78dec: numBytes = 27030; return gpl_logo_svg; | |||||
| case 0x80b17530: numBytes = 5312; return jucelogowithtext_svg; | case 0x80b17530: numBytes = 5312; return jucelogowithtext_svg; | ||||
| case 0x154a7275: numBytes = 45854; return juce_icon_png; | case 0x154a7275: numBytes = 45854; return juce_icon_png; | ||||
| case 0x1f3b6d2f: numBytes = 5978; return wizard_AnimatedApp_svg; | case 0x1f3b6d2f: numBytes = 5978; return wizard_AnimatedApp_svg; | ||||
| @@ -7793,7 +7864,7 @@ const char* namedResourceList[] = | |||||
| "export_linux_svg", | "export_linux_svg", | ||||
| "export_visualStudio_svg", | "export_visualStudio_svg", | ||||
| "export_xcode_svg", | "export_xcode_svg", | ||||
| "huckleberry_icon_svg", | |||||
| "gpl_logo_svg", | |||||
| "jucelogowithtext_svg", | "jucelogowithtext_svg", | ||||
| "juce_icon_png", | "juce_icon_png", | ||||
| "wizard_AnimatedApp_svg", | "wizard_AnimatedApp_svg", | ||||
| @@ -7861,7 +7932,7 @@ const char* originalFilenames[] = | |||||
| "export_linux.svg", | "export_linux.svg", | ||||
| "export_visualStudio.svg", | "export_visualStudio.svg", | ||||
| "export_xcode.svg", | "export_xcode.svg", | ||||
| "huckleberry_icon.svg", | |||||
| "gpl_logo.svg", | |||||
| "juce-logo-with-text.svg", | "juce-logo-with-text.svg", | ||||
| "juce_icon.png", | "juce_icon.png", | ||||
| "wizard_AnimatedApp.svg", | "wizard_AnimatedApp.svg", | ||||
| @@ -59,8 +59,8 @@ namespace BinaryData | |||||
| extern const char* export_xcode_svg; | extern const char* export_xcode_svg; | ||||
| const int export_xcode_svgSize = 12295; | const int export_xcode_svgSize = 12295; | ||||
| extern const char* huckleberry_icon_svg; | |||||
| const int huckleberry_icon_svgSize = 3375; | |||||
| extern const char* gpl_logo_svg; | |||||
| const int gpl_logo_svgSize = 27030; | |||||
| extern const char* jucelogowithtext_svg; | extern const char* jucelogowithtext_svg; | ||||
| const int jucelogowithtext_svgSize = 5312; | const int jucelogowithtext_svgSize = 5312; | ||||
| @@ -101,6 +101,16 @@ | |||||
| </EXPORTFORMATS> | </EXPORTFORMATS> | ||||
| <MAINGROUP name="Projucer" id="NhrJq66R"> | <MAINGROUP name="Projucer" id="NhrJq66R"> | ||||
| <GROUP id="{9E4C4E0D-7BAB-EB6F-87DA-FB264EC2AE68}" name="Application"> | <GROUP id="{9E4C4E0D-7BAB-EB6F-87DA-FB264EC2AE68}" name="Application"> | ||||
| <GROUP id="{70333C48-4F84-A180-24E1-0EC9EF223F3B}" name="UserAccount"> | |||||
| <FILE id="Rw7w0l" name="jucer_LicenseController.h" compile="0" resource="0" | |||||
| file="Source/Application/UserAccount/jucer_LicenseController.h"/> | |||||
| <FILE id="rUtPud" name="jucer_LicenseQueryThread.h" compile="0" resource="0" | |||||
| file="Source/Application/UserAccount/jucer_LicenseQueryThread.h"/> | |||||
| <FILE id="Dwndl3" name="jucer_LicenseState.h" compile="0" resource="0" | |||||
| file="Source/Application/UserAccount/jucer_LicenseState.h"/> | |||||
| <FILE id="d5kWEN" name="jucer_LoginFormComponent.h" compile="0" resource="0" | |||||
| file="Source/Application/UserAccount/jucer_LoginFormComponent.h"/> | |||||
| </GROUP> | |||||
| <GROUP id="{2F08ABDF-C7BB-5F54-55F5-0C2E27983930}" name="Windows"> | <GROUP id="{2F08ABDF-C7BB-5F54-55F5-0C2E27983930}" name="Windows"> | ||||
| <FILE id="w1XB4w" name="jucer_AboutWindowComponent.h" compile="0" resource="0" | <FILE id="w1XB4w" name="jucer_AboutWindowComponent.h" compile="0" resource="0" | ||||
| file="Source/Application/Windows/jucer_AboutWindowComponent.h"/> | file="Source/Application/Windows/jucer_AboutWindowComponent.h"/> | ||||
| @@ -177,8 +187,7 @@ | |||||
| file="Source/BinaryData/Icons/export_visualStudio.svg"/> | file="Source/BinaryData/Icons/export_visualStudio.svg"/> | ||||
| <FILE id="G0oYd6" name="export_xcode.svg" compile="0" resource="1" | <FILE id="G0oYd6" name="export_xcode.svg" compile="0" resource="1" | ||||
| file="Source/BinaryData/Icons/export_xcode.svg"/> | file="Source/BinaryData/Icons/export_xcode.svg"/> | ||||
| <FILE id="k4zzKu" name="huckleberry_icon.svg" compile="0" resource="1" | |||||
| file="Source/BinaryData/Icons/huckleberry_icon.svg"/> | |||||
| <FILE id="CfDTJ0" name="gpl_logo.svg" compile="0" resource="1" file="Source/BinaryData/Icons/gpl_logo.svg"/> | |||||
| <FILE id="Pk2LIn" name="juce-logo-with-text.svg" compile="0" resource="1" | <FILE id="Pk2LIn" name="juce-logo-with-text.svg" compile="0" resource="1" | ||||
| file="Source/BinaryData/Icons/juce-logo-with-text.svg"/> | file="Source/BinaryData/Icons/juce-logo-with-text.svg"/> | ||||
| <FILE id="Zrx1Gl" name="juce_icon.png" compile="0" resource="1" file="Source/BinaryData/Icons/juce_icon.png"/> | <FILE id="Zrx1Gl" name="juce_icon.png" compile="0" resource="1" file="Source/BinaryData/Icons/juce_icon.png"/> | ||||
| @@ -523,6 +532,15 @@ | |||||
| file="Source/LiveBuildEngine/jucer_SourceCodeRange.h"/> | file="Source/LiveBuildEngine/jucer_SourceCodeRange.h"/> | ||||
| </GROUP> | </GROUP> | ||||
| <GROUP id="{6653587F-C475-46AA-E7CF-1D0DFA0FEAA9}" name="Project"> | <GROUP id="{6653587F-C475-46AA-E7CF-1D0DFA0FEAA9}" name="Project"> | ||||
| <GROUP id="{F2E7D5CA-F002-2635-DA2C-898FA5EA2936}" name="Modules"> | |||||
| <FILE id="w7QIJd" name="jucer_AvailableModulesList.h" compile="0" resource="0" | |||||
| file="Source/Project/Modules/jucer_AvailableModulesList.h"/> | |||||
| <FILE id="fQrJvA" name="jucer_ModuleDescription.h" compile="0" resource="0" | |||||
| file="Source/Project/Modules/jucer_ModuleDescription.h"/> | |||||
| <FILE id="mOC0iL" name="jucer_Modules.cpp" compile="1" resource="0" | |||||
| file="Source/Project/Modules/jucer_Modules.cpp"/> | |||||
| <FILE id="TnngL4" name="jucer_Modules.h" compile="0" resource="0" file="Source/Project/Modules/jucer_Modules.h"/> | |||||
| </GROUP> | |||||
| <GROUP id="{C37B7D1A-F059-9C82-9436-A2A94552BF90}" name="UI"> | <GROUP id="{C37B7D1A-F059-9C82-9436-A2A94552BF90}" name="UI"> | ||||
| <GROUP id="{19B83596-13BE-A80E-2722-BB5CCDA111FA}" name="Sidebar"> | <GROUP id="{19B83596-13BE-A80E-2722-BB5CCDA111FA}" name="Sidebar"> | ||||
| <FILE id="bItg9o" name="jucer_ExporterTreeItems.h" compile="0" resource="0" | <FILE id="bItg9o" name="jucer_ExporterTreeItems.h" compile="0" resource="0" | ||||
| @@ -556,10 +574,11 @@ | |||||
| resource="0" file="Source/Project/UI/jucer_ProjectContentComponent.cpp"/> | resource="0" file="Source/Project/UI/jucer_ProjectContentComponent.cpp"/> | ||||
| <FILE id="z576fZ" name="jucer_ProjectContentComponent.h" compile="0" | <FILE id="z576fZ" name="jucer_ProjectContentComponent.h" compile="0" | ||||
| resource="0" file="Source/Project/UI/jucer_ProjectContentComponent.h"/> | resource="0" file="Source/Project/UI/jucer_ProjectContentComponent.h"/> | ||||
| <FILE id="itkVli" name="jucer_ProjectMessagesComponent.h" compile="0" | |||||
| resource="0" file="Source/Project/UI/jucer_ProjectMessagesComponent.h"/> | |||||
| <FILE id="UUlUDF" name="jucer_UserAvatarComponent.h" compile="0" resource="0" | |||||
| file="Source/Project/UI/jucer_UserAvatarComponent.h"/> | |||||
| </GROUP> | </GROUP> | ||||
| <FILE id="kPwhZB" name="jucer_Module.cpp" compile="1" resource="0" | |||||
| file="Source/Project/jucer_Module.cpp"/> | |||||
| <FILE id="YlGT8P" name="jucer_Module.h" compile="0" resource="0" file="Source/Project/jucer_Module.h"/> | |||||
| <FILE id="JT1rMJ" name="jucer_Project.cpp" compile="1" resource="0" | <FILE id="JT1rMJ" name="jucer_Project.cpp" compile="1" resource="0" | ||||
| file="Source/Project/jucer_Project.cpp"/> | file="Source/Project/jucer_Project.cpp"/> | ||||
| <FILE id="bUjtVS" name="jucer_Project.h" compile="0" resource="0" file="Source/Project/jucer_Project.h"/> | <FILE id="bUjtVS" name="jucer_Project.h" compile="0" resource="0" file="Source/Project/jucer_Project.h"/> | ||||
| @@ -0,0 +1,194 @@ | |||||
| /* | |||||
| ============================================================================== | |||||
| This file is part of the JUCE 6 technical preview. | |||||
| Copyright (c) 2017 - ROLI Ltd. | |||||
| You may use this code under the terms of the GPL v3 | |||||
| (see www.gnu.org/licenses). | |||||
| For this technical preview, this file is not subject to commercial licensing. | |||||
| 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 "jucer_LicenseState.h" | |||||
| //============================================================================== | |||||
| class LicenseController | |||||
| { | |||||
| public: | |||||
| LicenseController() = default; | |||||
| //============================================================================== | |||||
| LicenseState getCurrentState() const noexcept | |||||
| { | |||||
| return state; | |||||
| } | |||||
| void setState (const LicenseState& newState) | |||||
| { | |||||
| state = newState; | |||||
| licenseStateToSettings (state, getGlobalProperties()); | |||||
| stateListeners.call ([] (LicenseStateListener& l) { l.licenseStateChanged(); }); | |||||
| } | |||||
| void resetState() | |||||
| { | |||||
| setState ({}); | |||||
| } | |||||
| static LicenseState getGPLState() | |||||
| { | |||||
| static auto logoImage = []() -> Image | |||||
| { | |||||
| if (auto logo = Drawable::createFromImageData (BinaryData::gpl_logo_svg, BinaryData::gpl_logo_svgSize)) | |||||
| { | |||||
| auto bounds = logo->getDrawableBounds(); | |||||
| Image image (Image::ARGB, roundToInt (bounds.getWidth()), roundToInt (bounds.getHeight()), true); | |||||
| Graphics g (image); | |||||
| logo->draw (g, 1.0f); | |||||
| return image; | |||||
| } | |||||
| jassertfalse; | |||||
| return {}; | |||||
| }(); | |||||
| return { LicenseState::Type::gpl, {}, {}, logoImage }; | |||||
| } | |||||
| //============================================================================== | |||||
| struct LicenseStateListener | |||||
| { | |||||
| virtual ~LicenseStateListener() = default; | |||||
| virtual void licenseStateChanged() = 0; | |||||
| }; | |||||
| void addListener (LicenseStateListener* listenerToAdd) | |||||
| { | |||||
| stateListeners.add (listenerToAdd); | |||||
| } | |||||
| void removeListener (LicenseStateListener* listenerToRemove) | |||||
| { | |||||
| stateListeners.remove (listenerToRemove); | |||||
| } | |||||
| private: | |||||
| //============================================================================== | |||||
| static const char* getLicenseStateValue (LicenseState::Type type) | |||||
| { | |||||
| switch (type) | |||||
| { | |||||
| case LicenseState::Type::gpl: return "GPL"; | |||||
| case LicenseState::Type::personal: return "personal"; | |||||
| case LicenseState::Type::educational: return "edu"; | |||||
| case LicenseState::Type::indie: return "indie"; | |||||
| case LicenseState::Type::pro: return "pro"; | |||||
| case LicenseState::Type::none: | |||||
| default: break; | |||||
| } | |||||
| return nullptr; | |||||
| } | |||||
| static LicenseState::Type getLicenseTypeFromValue (const String& d) | |||||
| { | |||||
| if (d == getLicenseStateValue (LicenseState::Type::gpl)) return LicenseState::Type::gpl; | |||||
| if (d == getLicenseStateValue (LicenseState::Type::personal)) return LicenseState::Type::personal; | |||||
| if (d == getLicenseStateValue (LicenseState::Type::educational)) return LicenseState::Type::educational; | |||||
| if (d == getLicenseStateValue (LicenseState::Type::indie)) return LicenseState::Type::indie; | |||||
| if (d == getLicenseStateValue (LicenseState::Type::pro)) return LicenseState::Type::pro; | |||||
| return LicenseState::Type::none; | |||||
| } | |||||
| static Image avatarFromLicenseState (const String& licenseState) | |||||
| { | |||||
| MemoryOutputStream imageData; | |||||
| Base64::convertFromBase64 (imageData, licenseState); | |||||
| return ImageFileFormat::loadFrom (imageData.getData(), imageData.getDataSize()); | |||||
| } | |||||
| static String avatarToLicenseState (Image avatarImage) | |||||
| { | |||||
| MemoryOutputStream imageData; | |||||
| if (avatarImage.isValid() && PNGImageFormat().writeImageToStream (avatarImage, imageData)) | |||||
| return Base64::toBase64 (imageData.getData(), imageData.getDataSize()); | |||||
| return {}; | |||||
| } | |||||
| static LicenseState licenseStateFromSettings (PropertiesFile& props) | |||||
| { | |||||
| if (auto licenseXml = props.getXmlValue ("license")) | |||||
| { | |||||
| // this is here for backwards compatibility with old-style settings files using XML text elements | |||||
| if (licenseXml->getChildElementAllSubText ("type", {}).isNotEmpty()) | |||||
| { | |||||
| auto stateFromOldSettings = [&licenseXml]() -> LicenseState | |||||
| { | |||||
| return { getLicenseTypeFromValue (licenseXml->getChildElementAllSubText ("type", {})), | |||||
| licenseXml->getChildElementAllSubText ("authToken", {}), | |||||
| licenseXml->getChildElementAllSubText ("username", {}), | |||||
| avatarFromLicenseState (licenseXml->getStringAttribute ("avatar", {})) }; | |||||
| }(); | |||||
| licenseStateToSettings (stateFromOldSettings, props); | |||||
| return stateFromOldSettings; | |||||
| } | |||||
| return { getLicenseTypeFromValue (licenseXml->getStringAttribute ("type", {})), | |||||
| licenseXml->getStringAttribute ("authToken", {}), | |||||
| licenseXml->getStringAttribute ("username", {}), | |||||
| avatarFromLicenseState (licenseXml->getStringAttribute ("avatar", {})) }; | |||||
| } | |||||
| return {}; | |||||
| } | |||||
| static void licenseStateToSettings (const LicenseState& state, PropertiesFile& props) | |||||
| { | |||||
| props.removeValue ("license"); | |||||
| if (state.isValid()) | |||||
| { | |||||
| XmlElement licenseXml ("license"); | |||||
| if (auto* typeString = getLicenseStateValue (state.type)) | |||||
| licenseXml.setAttribute ("type", typeString); | |||||
| licenseXml.setAttribute ("authToken", state.authToken); | |||||
| licenseXml.setAttribute ("username", state.username); | |||||
| licenseXml.setAttribute ("avatar", avatarToLicenseState (state.avatar)); | |||||
| props.setValue ("license", &licenseXml); | |||||
| } | |||||
| props.saveIfNeeded(); | |||||
| } | |||||
| //============================================================================== | |||||
| #if JUCER_ENABLE_GPL_MODE | |||||
| LicenseState state = getGPLState(); | |||||
| #else | |||||
| LicenseState state = licenseStateFromSettings (getGlobalProperties()); | |||||
| #endif | |||||
| ListenerList<LicenseStateListener> stateListeners; | |||||
| //============================================================================== | |||||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (LicenseController) | |||||
| }; | |||||
| @@ -0,0 +1,274 @@ | |||||
| /* | |||||
| ============================================================================== | |||||
| This file is part of the JUCE 6 technical preview. | |||||
| Copyright (c) 2017 - ROLI Ltd. | |||||
| You may use this code under the terms of the GPL v3 | |||||
| (see www.gnu.org/licenses). | |||||
| For this technical preview, this file is not subject to commercial licensing. | |||||
| 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 LicenseQueryThread : public Thread | |||||
| { | |||||
| public: | |||||
| LicenseQueryThread (const String& userEmail, const String& userPassword, | |||||
| std::function<void(LicenseState, String)>&& cb) | |||||
| : Thread ("LicenseQueryThread"), | |||||
| email (userEmail), | |||||
| password (userPassword), | |||||
| completionCallback (std::move (cb)) | |||||
| { | |||||
| startThread(); | |||||
| } | |||||
| ~LicenseQueryThread() override | |||||
| { | |||||
| signalThreadShouldExit(); | |||||
| waitForThreadToExit (-1); | |||||
| } | |||||
| void run() override | |||||
| { | |||||
| LicenseState state; | |||||
| auto errorMessage = runJob (std::make_unique<UserLogin> (email, password), state); | |||||
| if (errorMessage.isEmpty()) | |||||
| errorMessage = runJob (std::make_unique<UserLicenseQuery> (state.authToken), state); | |||||
| if (errorMessage.isNotEmpty()) | |||||
| state = {}; | |||||
| WeakReference<LicenseQueryThread> weakThis (this); | |||||
| MessageManager::callAsync ([this, weakThis, state, errorMessage] | |||||
| { | |||||
| if (weakThis != nullptr) | |||||
| completionCallback (state, errorMessage); | |||||
| }); | |||||
| } | |||||
| private: | |||||
| //============================================================================== | |||||
| struct AccountEnquiryBase | |||||
| { | |||||
| virtual ~AccountEnquiryBase() = default; | |||||
| virtual bool isPOSTLikeRequest() const = 0; | |||||
| virtual String getEndpointURLSuffix() const = 0; | |||||
| virtual StringPairArray getParameterNamesAndValues() const = 0; | |||||
| virtual int getSuccessCode() const = 0; | |||||
| virtual String errorCodeToString (int) const = 0; | |||||
| virtual bool parseServerResponse (const String&, LicenseState&) = 0; | |||||
| }; | |||||
| struct UserLogin : public AccountEnquiryBase | |||||
| { | |||||
| UserLogin (const String& e, const String& p) | |||||
| : userEmail (e), userPassword (p) | |||||
| { | |||||
| } | |||||
| bool isPOSTLikeRequest() const override { return true; } | |||||
| String getEndpointURLSuffix() const override { return "/authenticate"; } | |||||
| int getSuccessCode() const override { return 200; } | |||||
| StringPairArray getParameterNamesAndValues() const override | |||||
| { | |||||
| StringPairArray namesAndValues; | |||||
| namesAndValues.set ("email", userEmail); | |||||
| namesAndValues.set ("password", userPassword); | |||||
| return namesAndValues; | |||||
| } | |||||
| String errorCodeToString (int errorCode) const override | |||||
| { | |||||
| switch (errorCode) | |||||
| { | |||||
| case 400: return "Please enter your email and password to log in."; | |||||
| case 401: return "Your email and password are incorrect."; | |||||
| case 451: return "Access denied."; | |||||
| default: return "Something went wrong, please try again."; | |||||
| } | |||||
| } | |||||
| bool parseServerResponse (const String& serverResponse, LicenseState& licenseState) override | |||||
| { | |||||
| auto json = JSON::parse (serverResponse); | |||||
| licenseState.authToken = json.getProperty ("token", {}).toString(); | |||||
| licenseState.username = json.getProperty ("user", {}).getProperty ("username", {}).toString(); | |||||
| auto avatarURL = json.getProperty ("user", {}).getProperty ("avatar_url", {}).toString(); | |||||
| if (avatarURL.isNotEmpty()) | |||||
| { | |||||
| URL url (avatarURL); | |||||
| if (auto stream = url.createInputStream (false)) | |||||
| { | |||||
| MemoryBlock mb; | |||||
| stream->readIntoMemoryBlock (mb); | |||||
| licenseState.avatar = ImageFileFormat::loadFrom (mb.getData(), mb.getSize()); | |||||
| } | |||||
| } | |||||
| return (licenseState.authToken.isNotEmpty() && licenseState.username.isNotEmpty()); | |||||
| } | |||||
| String userEmail, userPassword; | |||||
| }; | |||||
| struct UserLicenseQuery : public AccountEnquiryBase | |||||
| { | |||||
| UserLicenseQuery (const String& authToken) | |||||
| : userAuthToken (authToken) | |||||
| { | |||||
| } | |||||
| bool isPOSTLikeRequest() const override { return false; } | |||||
| String getEndpointURLSuffix() const override { return "/user/licences"; } | |||||
| int getSuccessCode() const override { return 200; } | |||||
| StringPairArray getParameterNamesAndValues() const override | |||||
| { | |||||
| StringPairArray namesAndValues; | |||||
| namesAndValues.set ("token", userAuthToken); | |||||
| return namesAndValues; | |||||
| } | |||||
| String errorCodeToString (int errorCode) const override | |||||
| { | |||||
| switch (errorCode) | |||||
| { | |||||
| case 401: return "User not found or could not be verified."; | |||||
| default: return "User licenses info fetch failed (unknown error)."; | |||||
| } | |||||
| } | |||||
| bool parseServerResponse (const String& serverResponse, LicenseState& licenseState) override | |||||
| { | |||||
| auto json = JSON::parse (serverResponse); | |||||
| if (auto* licensesJson = json.getArray()) | |||||
| { | |||||
| StringArray licenseTypes; | |||||
| for (auto& license : *licensesJson) | |||||
| { | |||||
| auto name = license.getProperty ("product_name", {}).toString(); | |||||
| auto status = license.getProperty ("status", {}).toString(); | |||||
| if (name == "Projucer" && status == "active") | |||||
| licenseTypes.add (license.getProperty ("licence_type", {}).toString()); | |||||
| } | |||||
| licenseTypes.removeEmptyStrings(); | |||||
| licenseTypes.removeDuplicates (false); | |||||
| licenseState.type = [licenseTypes] () | |||||
| { | |||||
| if (licenseTypes.contains ("juce-pro")) return LicenseState::Type::pro; | |||||
| else if (licenseTypes.contains ("juce-indie")) return LicenseState::Type::indie; | |||||
| else if (licenseTypes.contains ("juce-personal")) return LicenseState::Type::personal; | |||||
| else if (licenseTypes.contains ("juce-edu")) return LicenseState::Type::educational; | |||||
| return LicenseState::Type::none; | |||||
| }(); | |||||
| return (licenseState.type != LicenseState::Type::none); | |||||
| } | |||||
| return false; | |||||
| } | |||||
| String userAuthToken; | |||||
| }; | |||||
| //============================================================================== | |||||
| static String postDataStringAsJSON (const StringPairArray& parameters) | |||||
| { | |||||
| DynamicObject::Ptr d (new DynamicObject()); | |||||
| for (auto& key : parameters.getAllKeys()) | |||||
| d->setProperty (key, parameters[key]); | |||||
| return JSON::toString (var (d.get())); | |||||
| } | |||||
| String runJob (std::unique_ptr<AccountEnquiryBase> accountEnquiryJob, LicenseState& state) | |||||
| { | |||||
| const String endpointURL = "https://api.roli.com/api/v1"; | |||||
| const String extraHeaders = "Content-Type: application/json"; | |||||
| auto url = URL (endpointURL + accountEnquiryJob->getEndpointURLSuffix()); | |||||
| auto isPOST = accountEnquiryJob->isPOSTLikeRequest(); | |||||
| if (isPOST) | |||||
| url = url.withPOSTData (postDataStringAsJSON (accountEnquiryJob->getParameterNamesAndValues())); | |||||
| else | |||||
| url = url.withParameters (accountEnquiryJob->getParameterNamesAndValues()); | |||||
| if (threadShouldExit()) | |||||
| return "Cancelled."; | |||||
| int statusCode = 0; | |||||
| auto urlStream = url.createInputStream (isPOST, nullptr, nullptr, extraHeaders, 0, nullptr, &statusCode); | |||||
| if (urlStream == nullptr) | |||||
| return "Failed to connect to the web server."; | |||||
| if (statusCode != accountEnquiryJob->getSuccessCode()) | |||||
| return accountEnquiryJob->errorCodeToString (statusCode); | |||||
| if (threadShouldExit()) | |||||
| return "Cancelled."; | |||||
| String response; | |||||
| for (;;) | |||||
| { | |||||
| char buffer [8192]; | |||||
| auto num = urlStream->read (buffer, sizeof (buffer)); | |||||
| if (threadShouldExit()) | |||||
| return "Cancelled."; | |||||
| if (num <= 0) | |||||
| break; | |||||
| response += buffer; | |||||
| } | |||||
| if (threadShouldExit()) | |||||
| return "Cancelled."; | |||||
| if (! accountEnquiryJob->parseServerResponse (response, state)) | |||||
| return "Failed to parse server response."; | |||||
| return {}; | |||||
| } | |||||
| //============================================================================== | |||||
| const String email, password; | |||||
| const std::function<void(LicenseState, String)> completionCallback; | |||||
| //============================================================================== | |||||
| JUCE_DECLARE_WEAK_REFERENCEABLE (LicenseQueryThread) | |||||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (LicenseQueryThread) | |||||
| }; | |||||
| @@ -0,0 +1,68 @@ | |||||
| /* | |||||
| ============================================================================== | |||||
| This file is part of the JUCE 6 technical preview. | |||||
| Copyright (c) 2017 - ROLI Ltd. | |||||
| You may use this code under the terms of the GPL v3 | |||||
| (see www.gnu.org/licenses). | |||||
| For this technical preview, this file is not subject to commercial licensing. | |||||
| 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 | |||||
| //============================================================================== | |||||
| struct LicenseState | |||||
| { | |||||
| enum class Type | |||||
| { | |||||
| none, | |||||
| gpl, | |||||
| personal, | |||||
| educational, | |||||
| indie, | |||||
| pro | |||||
| }; | |||||
| LicenseState() = default; | |||||
| LicenseState (Type t, String token, String user, Image avatarImage) | |||||
| : type (t), authToken (token), username (user), avatar (avatarImage) | |||||
| { | |||||
| } | |||||
| bool isValid() const noexcept { return isGPL() || (type != Type::none && authToken.isNotEmpty() && username.isNotEmpty()); } | |||||
| bool isPaid() const noexcept { return type == Type::indie || type == Type::pro; } | |||||
| bool isGPL() const noexcept { return type == Type::gpl; } | |||||
| bool isPaidOrGPL() const noexcept { return isPaid() || isGPL(); } | |||||
| String getLicenseTypeString() const | |||||
| { | |||||
| switch (type) | |||||
| { | |||||
| case Type::none: return "No license"; | |||||
| case Type::gpl: return "GPL"; | |||||
| case Type::personal: return "Personal"; | |||||
| case Type::educational: return "Educational"; | |||||
| case Type::indie: return "Indie"; | |||||
| case Type::pro: return "Pro"; | |||||
| default: break; | |||||
| }; | |||||
| jassertfalse; | |||||
| return {}; | |||||
| } | |||||
| Type type = Type::none; | |||||
| String authToken, username; | |||||
| Image avatar; | |||||
| }; | |||||
| @@ -0,0 +1,273 @@ | |||||
| /* | |||||
| ============================================================================== | |||||
| This file is part of the JUCE 6 technical preview. | |||||
| Copyright (c) 2017 - ROLI Ltd. | |||||
| You may use this code under the terms of the GPL v3 | |||||
| (see www.gnu.org/licenses). | |||||
| For this technical preview, this file is not subject to commercial licensing. | |||||
| 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 "jucer_LicenseQueryThread.h" | |||||
| #include "../../Project/UI/jucer_UserAvatarComponent.h" | |||||
| //============================================================================== | |||||
| class LoginFormComponent : public Component | |||||
| { | |||||
| public: | |||||
| LoginFormComponent (MainWindow& window) | |||||
| : mainWindow (window) | |||||
| { | |||||
| addAndMakeVisible (emailBox); | |||||
| emailBox.setTextToShowWhenEmpty ("Email", Colours::black.withAlpha (0.2f)); | |||||
| emailBox.setJustification (Justification::centredLeft); | |||||
| emailBox.onReturnKey = [this] { submitDetails(); }; | |||||
| addAndMakeVisible (passwordBox); | |||||
| passwordBox.setTextToShowWhenEmpty ("Password", Colours::black.withAlpha (0.2f)); | |||||
| passwordBox.setPasswordCharacter ((juce_wchar) 0x2022); | |||||
| passwordBox.setJustification (Justification::centredLeft); | |||||
| passwordBox.onReturnKey = [this] { submitDetails(); }; | |||||
| addAndMakeVisible (logInButton); | |||||
| logInButton.onClick = [this] { submitDetails(); }; | |||||
| addAndMakeVisible (enableGPLButton); | |||||
| enableGPLButton.onClick = [this] | |||||
| { | |||||
| ProjucerApplication::getApp().getLicenseController().setState (LicenseController::getGPLState()); | |||||
| mainWindow.hideLoginFormOverlay(); | |||||
| }; | |||||
| addAndMakeVisible (userAvatar); | |||||
| addAndMakeVisible (createAccountLabel); | |||||
| createAccountLabel.setFont (Font (14.0f, Font::underlined)); | |||||
| createAccountLabel.addMouseListener (this, false); | |||||
| createAccountLabel.setMouseCursor (MouseCursor::PointingHandCursor); | |||||
| addAndMakeVisible (errorMessageLabel); | |||||
| errorMessageLabel.setMinimumHorizontalScale (1.0f); | |||||
| errorMessageLabel.setFont (12.0f); | |||||
| errorMessageLabel.setColour (Label::textColourId, Colours::red); | |||||
| errorMessageLabel.setVisible (false); | |||||
| dismissButton.setShape (getLookAndFeel().getCrossShape (1.0f), false, true, false); | |||||
| addAndMakeVisible (dismissButton); | |||||
| dismissButton.onClick = [this] { mainWindow.hideLoginFormOverlay(); }; | |||||
| setWantsKeyboardFocus (true); | |||||
| setOpaque (true); | |||||
| lookAndFeelChanged(); | |||||
| setSize (300, 350); | |||||
| } | |||||
| void resized() override | |||||
| { | |||||
| auto bounds = getLocalBounds().reduced (20); | |||||
| auto spacing = bounds.getHeight() / 20; | |||||
| userAvatar.setBounds (bounds.removeFromTop (iconHeight).reduced ((bounds.getWidth() / 2) - (iconHeight / 2), 0)); | |||||
| errorMessageLabel.setBounds (bounds.removeFromTop (spacing)); | |||||
| bounds.removeFromTop (spacing / 2); | |||||
| auto textEditorHeight = bounds.getHeight() / 5; | |||||
| emailBox.setBounds (bounds.removeFromTop (textEditorHeight)); | |||||
| bounds.removeFromTop (spacing); | |||||
| passwordBox.setBounds (bounds.removeFromTop (textEditorHeight)); | |||||
| bounds.removeFromTop (spacing * 2); | |||||
| emailBox.setFont (Font (textEditorHeight / 2.5f)); | |||||
| passwordBox.setFont (Font (textEditorHeight / 2.5f)); | |||||
| logInButton.setBounds (bounds.removeFromTop (textEditorHeight)); | |||||
| auto slice = bounds.removeFromTop (textEditorHeight); | |||||
| createAccountLabel.setBounds (slice.removeFromLeft (createAccountLabel.getFont().getStringWidth (createAccountLabel.getText()) + 5)); | |||||
| slice.removeFromLeft (15); | |||||
| enableGPLButton.setBounds (slice.reduced (0, 5)); | |||||
| dismissButton.setBounds (getLocalBounds().reduced (10).removeFromTop (20).removeFromRight (20)); | |||||
| } | |||||
| void paint (Graphics& g) override | |||||
| { | |||||
| g.fillAll (findColour (secondaryBackgroundColourId).contrasting (0.1f)); | |||||
| } | |||||
| void mouseUp (const MouseEvent& event) override | |||||
| { | |||||
| if (event.eventComponent == &createAccountLabel) | |||||
| URL ("https://auth.roli.com/register").launchInDefaultBrowser(); | |||||
| } | |||||
| void lookAndFeelChanged() override | |||||
| { | |||||
| enableGPLButton.setColour (TextButton::buttonColourId, findColour (secondaryButtonBackgroundColourId)); | |||||
| } | |||||
| private: | |||||
| class ProgressButton : public TextButton, | |||||
| private Timer | |||||
| { | |||||
| public: | |||||
| ProgressButton (const String& buttonName) | |||||
| : TextButton (buttonName), text (buttonName) | |||||
| { | |||||
| } | |||||
| void setBusy (bool shouldBeBusy) | |||||
| { | |||||
| isInProgress = shouldBeBusy; | |||||
| if (isInProgress) | |||||
| { | |||||
| setEnabled (false); | |||||
| setButtonText ({}); | |||||
| startTimerHz (30); | |||||
| } | |||||
| else | |||||
| { | |||||
| setEnabled (true); | |||||
| setButtonText (text); | |||||
| stopTimer(); | |||||
| } | |||||
| } | |||||
| void paint (Graphics& g) override | |||||
| { | |||||
| TextButton::paint (g); | |||||
| if (isInProgress) | |||||
| { | |||||
| auto size = getHeight() - 10; | |||||
| auto halfSize = size / 2; | |||||
| getLookAndFeel().drawSpinningWaitAnimation (g, Colours::white, | |||||
| (getWidth() / 2) - halfSize, (getHeight() / 2) - halfSize, | |||||
| size, size); | |||||
| } | |||||
| } | |||||
| private: | |||||
| void timerCallback() override | |||||
| { | |||||
| repaint(); | |||||
| } | |||||
| String text; | |||||
| bool isInProgress = false; | |||||
| }; | |||||
| //============================================================================== | |||||
| void updateLoginButtonStates (bool isLoggingIn) | |||||
| { | |||||
| logInButton.setBusy (isLoggingIn); | |||||
| emailBox.setEnabled (! isLoggingIn); | |||||
| passwordBox.setEnabled (! isLoggingIn); | |||||
| } | |||||
| void submitDetails() | |||||
| { | |||||
| if ((licenseQueryThread != nullptr && licenseQueryThread->isThreadRunning())) | |||||
| return; | |||||
| auto loginFormError = checkLoginFormsAreValid(); | |||||
| if (loginFormError.isNotEmpty()) | |||||
| { | |||||
| showErrorMessage (loginFormError); | |||||
| return; | |||||
| } | |||||
| updateLoginButtonStates (true); | |||||
| WeakReference<Component> weakThis (this); | |||||
| licenseQueryThread.reset (new LicenseQueryThread (emailBox.getText(), passwordBox.getText(), | |||||
| [this, weakThis] (LicenseState newState, String errorMessage) | |||||
| { | |||||
| if (weakThis == nullptr) | |||||
| return; | |||||
| updateLoginButtonStates (false); | |||||
| if (errorMessage.isNotEmpty()) | |||||
| { | |||||
| showErrorMessage (errorMessage); | |||||
| } | |||||
| else | |||||
| { | |||||
| hideErrorMessage(); | |||||
| auto& licenseController = ProjucerApplication::getApp().getLicenseController(); | |||||
| licenseController.setState (newState); | |||||
| mainWindow.hideLoginFormOverlay(); | |||||
| ProjucerApplication::getApp().getCommandManager().commandStatusChanged(); | |||||
| } | |||||
| })); | |||||
| } | |||||
| String checkLoginFormsAreValid() const | |||||
| { | |||||
| auto email = emailBox.getText(); | |||||
| if (email.isEmpty() || email.indexOfChar ('@') < 0) | |||||
| return "Please enter a valid email."; | |||||
| auto password = passwordBox.getText(); | |||||
| if (password.isEmpty() || password.length() < 8) | |||||
| return "Please enter a valid password."; | |||||
| return {}; | |||||
| } | |||||
| void showErrorMessage (const String& errorMessage) | |||||
| { | |||||
| errorMessageLabel.setText (errorMessage, dontSendNotification); | |||||
| errorMessageLabel.setVisible (true); | |||||
| } | |||||
| void hideErrorMessage() | |||||
| { | |||||
| errorMessageLabel.setText ({}, dontSendNotification); | |||||
| errorMessageLabel.setVisible (false); | |||||
| } | |||||
| //============================================================================== | |||||
| static constexpr int iconHeight = 50; | |||||
| MainWindow& mainWindow; | |||||
| TextEditor emailBox, passwordBox; | |||||
| ProgressButton logInButton { "Log In" }; | |||||
| TextButton enableGPLButton { "Enable GPL Mode" }; | |||||
| ShapeButton dismissButton { {}, | |||||
| findColour (treeIconColourId), | |||||
| findColour (treeIconColourId).overlaidWith (findColour (defaultHighlightedTextColourId).withAlpha (0.2f)), | |||||
| findColour (treeIconColourId).overlaidWith (findColour (defaultHighlightedTextColourId).withAlpha (0.4f)) }; | |||||
| UserAvatarComponent userAvatar { false, false }; | |||||
| Label createAccountLabel { {}, "Create an account" }, | |||||
| errorMessageLabel { {}, {} }; | |||||
| std::unique_ptr<LicenseQueryThread> licenseQueryThread; | |||||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (LoginFormComponent) | |||||
| }; | |||||
| @@ -50,23 +50,15 @@ public: | |||||
| auto bounds = getLocalBounds(); | auto bounds = getLocalBounds(); | ||||
| bounds.removeFromBottom (20); | bounds.removeFromBottom (20); | ||||
| auto rightSlice = bounds.removeFromRight (150); | |||||
| auto leftSlice = bounds.removeFromLeft (150); | auto leftSlice = bounds.removeFromLeft (150); | ||||
| auto centreSlice = bounds; | |||||
| auto centreSlice = bounds.withTrimmedRight (150); | |||||
| //============================================================================== | |||||
| rightSlice.removeFromRight (20); | |||||
| auto iconSlice = rightSlice.removeFromRight (100); | |||||
| huckleberryLogoBounds = iconSlice.removeFromBottom (100).toFloat(); | |||||
| //============================================================================== | |||||
| juceLogoBounds = leftSlice.removeFromTop (150).toFloat(); | juceLogoBounds = leftSlice.removeFromTop (150).toFloat(); | ||||
| juceLogoBounds.setWidth (juceLogoBounds.getWidth() + 100); | juceLogoBounds.setWidth (juceLogoBounds.getWidth() + 100); | ||||
| juceLogoBounds.setHeight (juceLogoBounds.getHeight() + 100); | juceLogoBounds.setHeight (juceLogoBounds.getHeight() + 100); | ||||
| copyrightLabel.setBounds (leftSlice.removeFromBottom (20)); | copyrightLabel.setBounds (leftSlice.removeFromBottom (20)); | ||||
| //============================================================================== | |||||
| auto titleHeight = 40; | auto titleHeight = 40; | ||||
| centreSlice.removeFromTop ((centreSlice.getHeight() / 2) - (titleHeight / 2)); | centreSlice.removeFromTop ((centreSlice.getHeight() / 2) - (titleHeight / 2)); | ||||
| @@ -86,9 +78,6 @@ public: | |||||
| if (juceLogo != nullptr) | if (juceLogo != nullptr) | ||||
| juceLogo->drawWithin (g, juceLogoBounds.translated (-75, -75), RectanglePlacement::centred, 1.0); | juceLogo->drawWithin (g, juceLogoBounds.translated (-75, -75), RectanglePlacement::centred, 1.0); | ||||
| if (huckleberryLogo != nullptr) | |||||
| huckleberryLogo->drawWithin (g, huckleberryLogoBounds, RectanglePlacement::centred, 1.0); | |||||
| } | } | ||||
| private: | private: | ||||
| @@ -98,13 +87,10 @@ private: | |||||
| HyperlinkButton aboutButton { "About Us", URL ("https://juce.com") }; | HyperlinkButton aboutButton { "About Us", URL ("https://juce.com") }; | ||||
| Rectangle<float> huckleberryLogoBounds, juceLogoBounds; | |||||
| Rectangle<float> juceLogoBounds; | |||||
| std::unique_ptr<Drawable> juceLogo { Drawable::createFromImageData (BinaryData::juce_icon_png, | std::unique_ptr<Drawable> juceLogo { Drawable::createFromImageData (BinaryData::juce_icon_png, | ||||
| BinaryData::juce_icon_pngSize) }; | BinaryData::juce_icon_pngSize) }; | ||||
| std::unique_ptr<Drawable> huckleberryLogo { Drawable::createFromImageData (BinaryData::huckleberry_icon_svg, | |||||
| BinaryData::huckleberry_icon_svgSize) }; | |||||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AboutWindowComponent) | JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AboutWindowComponent) | ||||
| }; | }; | ||||
| @@ -23,7 +23,8 @@ | |||||
| //============================================================================== | //============================================================================== | ||||
| class GlobalPathsWindowComponent : public Component, | class GlobalPathsWindowComponent : public Component, | ||||
| private Timer, | private Timer, | ||||
| private Value::Listener | |||||
| private Value::Listener, | |||||
| private ChangeListener | |||||
| { | { | ||||
| public: | public: | ||||
| GlobalPathsWindowComponent() | GlobalPathsWindowComponent() | ||||
| @@ -42,6 +43,16 @@ public: | |||||
| lastUserModulePath = getAppSettings().getStoredPath (Ids::defaultUserModulePath, TargetOS::getThisOS()).get(); | lastUserModulePath = getAppSettings().getStoredPath (Ids::defaultUserModulePath, TargetOS::getThisOS()).get(); | ||||
| }; | }; | ||||
| addChildComponent (warnAboutJUCEPathButton); | |||||
| warnAboutJUCEPathButton.setToggleState (ProjucerApplication::getApp().shouldPromptUserAboutIncorrectJUCEPath(), | |||||
| dontSendNotification); | |||||
| warnAboutJUCEPathButton.onClick = [this] | |||||
| { | |||||
| ProjucerApplication::getApp().setShouldPromptUserAboutIncorrectJUCEPath (warnAboutJUCEPathButton.getToggleState()); | |||||
| }; | |||||
| getGlobalProperties().addChangeListener (this); | |||||
| addAndMakeVisible (resetToDefaultsButton); | addAndMakeVisible (resetToDefaultsButton); | ||||
| resetToDefaultsButton.onClick = [this] { resetCurrentOSPathsToDefaults(); }; | resetToDefaultsButton.onClick = [this] { resetCurrentOSPathsToDefaults(); }; | ||||
| @@ -64,6 +75,8 @@ public: | |||||
| ~GlobalPathsWindowComponent() override | ~GlobalPathsWindowComponent() override | ||||
| { | { | ||||
| getGlobalProperties().removeChangeListener (this); | |||||
| auto juceValue = getAppSettings().getStoredPath (Ids::defaultJuceModulePath, TargetOS::getThisOS()); | auto juceValue = getAppSettings().getStoredPath (Ids::defaultJuceModulePath, TargetOS::getThisOS()); | ||||
| auto userValue = getAppSettings().getStoredPath (Ids::defaultUserModulePath, TargetOS::getThisOS()); | auto userValue = getAppSettings().getStoredPath (Ids::defaultUserModulePath, TargetOS::getThisOS()); | ||||
| @@ -86,12 +99,15 @@ public: | |||||
| { | { | ||||
| auto b = getLocalBounds().reduced (10); | auto b = getLocalBounds().reduced (10); | ||||
| auto buttonBounds = b.removeFromBottom (50); | |||||
| auto bottomBounds = b.removeFromBottom (80); | |||||
| auto buttonBounds = bottomBounds.removeFromBottom (50); | |||||
| rescanJUCEPathButton.setBounds (buttonBounds.removeFromLeft (150).reduced (5, 10)); | rescanJUCEPathButton.setBounds (buttonBounds.removeFromLeft (150).reduced (5, 10)); | ||||
| rescanUserPathButton.setBounds (buttonBounds.removeFromLeft (150).reduced (5, 10)); | rescanUserPathButton.setBounds (buttonBounds.removeFromLeft (150).reduced (5, 10)); | ||||
| resetToDefaultsButton.setBounds (buttonBounds.removeFromRight (150).reduced (5, 10)); | resetToDefaultsButton.setBounds (buttonBounds.removeFromRight (150).reduced (5, 10)); | ||||
| warnAboutJUCEPathButton.setBounds (bottomBounds.reduced (0, 5)); | |||||
| warnAboutJUCEPathButton.changeWidthToFitText(); | |||||
| propertyGroup.updateSize (0, 0, getWidth() - 20 - propertyViewport.getScrollBarThickness()); | propertyGroup.updateSize (0, 0, getWidth() - 20 - propertyViewport.getScrollBarThickness()); | ||||
| propertyViewport.setBounds (b); | propertyViewport.setBounds (b); | ||||
| @@ -145,6 +161,12 @@ private: | |||||
| resized(); | resized(); | ||||
| } | } | ||||
| void changeListenerCallback (ChangeBroadcaster*) override | |||||
| { | |||||
| warnAboutJUCEPathButton.setToggleState (ProjucerApplication::getApp().shouldPromptUserAboutIncorrectJUCEPath(), | |||||
| dontSendNotification); | |||||
| } | |||||
| //============================================================================== | //============================================================================== | ||||
| bool isSelectedOSThisOS() { return TargetOS::getThisOS() == getSelectedOS(); } | bool isSelectedOSThisOS() { return TargetOS::getThisOS() == getSelectedOS(); } | ||||
| @@ -223,16 +245,12 @@ private: | |||||
| "This path will be used for the \"Save Project and Open in IDE...\" option of the CLion exporter."); | "This path will be used for the \"Save Project and Open in IDE...\" option of the CLion exporter."); | ||||
| builder.add (new FilePathPropertyComponent (androidStudioExePathValue, "Android Studio " + exeLabel, false, isThisOS), | builder.add (new FilePathPropertyComponent (androidStudioExePathValue, "Android Studio " + exeLabel, false, isThisOS), | ||||
| "This path will be used for the \"Save Project and Open in IDE...\" option of the Android Studio exporter."); | "This path will be used for the \"Save Project and Open in IDE...\" option of the Android Studio exporter."); | ||||
| rescanJUCEPathButton.setVisible (true); | |||||
| rescanUserPathButton.setVisible (true); | |||||
| } | |||||
| else | |||||
| { | |||||
| rescanJUCEPathButton.setVisible (false); | |||||
| rescanUserPathButton.setVisible (false); | |||||
| } | } | ||||
| rescanJUCEPathButton.setVisible (isThisOS); | |||||
| rescanUserPathButton.setVisible (isThisOS); | |||||
| warnAboutJUCEPathButton.setVisible (isThisOS); | |||||
| propertyGroup.setProperties (builder); | propertyGroup.setProperties (builder); | ||||
| } | } | ||||
| @@ -279,6 +297,7 @@ private: | |||||
| Viewport propertyViewport; | Viewport propertyViewport; | ||||
| PropertyGroupComponent propertyGroup { "Global Paths", { getIcons().openFolder, Colours::transparentBlack } }; | PropertyGroupComponent propertyGroup { "Global Paths", { getIcons().openFolder, Colours::transparentBlack } }; | ||||
| ToggleButton warnAboutJUCEPathButton { "Warn about incorrect JUCE path" }; | |||||
| TextButton rescanJUCEPathButton { "Re-scan JUCE Modules" }, | TextButton rescanJUCEPathButton { "Re-scan JUCE Modules" }, | ||||
| rescanUserPathButton { "Re-scan User Modules" }, | rescanUserPathButton { "Re-scan User Modules" }, | ||||
| resetToDefaultsButton { "Reset to Defaults" }; | resetToDefaultsButton { "Reset to Defaults" }; | ||||
| @@ -18,6 +18,7 @@ | |||||
| #pragma once | #pragma once | ||||
| //============================================================================== | //============================================================================== | ||||
| static String getWidthLimitedStringFromVarArray (const var& varArray) noexcept | static String getWidthLimitedStringFromVarArray (const var& varArray) noexcept | ||||
| { | { | ||||
| @@ -16,7 +16,7 @@ | |||||
| ============================================================================== | ============================================================================== | ||||
| */ | */ | ||||
| void createGUIEditorMenu (PopupMenu&); | |||||
| PopupMenu createGUIEditorMenu(); | |||||
| void handleGUIEditorMenuCommand (int); | void handleGUIEditorMenuCommand (int); | ||||
| void registerGUIEditorCommands(); | void registerGUIEditorCommands(); | ||||
| @@ -36,9 +36,7 @@ struct ProjucerApplication::MainMenuModel : public MenuBarModel | |||||
| PopupMenu getMenuForIndex (int /*topLevelMenuIndex*/, const String& menuName) override | PopupMenu getMenuForIndex (int /*topLevelMenuIndex*/, const String& menuName) override | ||||
| { | { | ||||
| PopupMenu menu; | |||||
| getApp().createMenu (menu, menuName); | |||||
| return menu; | |||||
| return getApp().createMenu (menuName); | |||||
| } | } | ||||
| void menuItemSelected (int menuItemID, int /*topLevelMenuIndex*/) override | void menuItemSelected (int menuItemID, int /*topLevelMenuIndex*/) override | ||||
| @@ -48,10 +46,6 @@ struct ProjucerApplication::MainMenuModel : public MenuBarModel | |||||
| }; | }; | ||||
| //============================================================================== | //============================================================================== | ||||
| ProjucerApplication::ProjucerApplication() : isRunningCommandLine (false) | |||||
| { | |||||
| } | |||||
| void ProjucerApplication::initialise (const String& commandLine) | void ProjucerApplication::initialise (const String& commandLine) | ||||
| { | { | ||||
| if (commandLine.trimStart().startsWith ("--server")) | if (commandLine.trimStart().startsWith ("--server")) | ||||
| @@ -99,22 +93,6 @@ void ProjucerApplication::initialise (const String& commandLine) | |||||
| return; | return; | ||||
| } | } | ||||
| rescanJUCEPathModules(); | |||||
| rescanUserPathModules(); | |||||
| openDocumentManager.registerType (new ProjucerAppClasses::LiveBuildCodeEditorDocument::Type(), 2); | |||||
| childProcessCache.reset (new ChildProcessCache()); | |||||
| initCommandManager(); | |||||
| menuModel.reset (new MainMenuModel()); | |||||
| settings->appearance.refreshPresetSchemeList(); | |||||
| setColourScheme (settings->getGlobalProperties().getIntValue ("COLOUR SCHEME"), false); | |||||
| setEditorColourScheme (settings->getGlobalProperties().getIntValue ("EDITOR COLOUR SCHEME"), false); | |||||
| updateEditorColourSchemeIfNeeded(); | |||||
| // do further initialisation in a moment when the message loop has started | // do further initialisation in a moment when the message loop has started | ||||
| triggerAsyncUpdate(); | triggerAsyncUpdate(); | ||||
| } | } | ||||
| @@ -153,29 +131,37 @@ void ProjucerApplication::initialiseWindows (const String& commandLine) | |||||
| void ProjucerApplication::handleAsyncUpdate() | void ProjucerApplication::handleAsyncUpdate() | ||||
| { | { | ||||
| licenseController = std::make_unique<LicenseController>(); | |||||
| LookAndFeel::setDefaultLookAndFeel (&lookAndFeel); | LookAndFeel::setDefaultLookAndFeel (&lookAndFeel); | ||||
| ImageCache::setCacheTimeout (30 * 1000); | |||||
| icons = std::make_unique<Icons>(); | |||||
| rescanJUCEPathModules(); | |||||
| rescanUserPathModules(); | |||||
| tooltipWindow = std::make_unique<TooltipWindow> (nullptr, 1200); | |||||
| openDocumentManager.registerType (new ProjucerAppClasses::LiveBuildCodeEditorDocument::Type(), 2); | |||||
| childProcessCache.reset (new ChildProcessCache()); | |||||
| #if JUCE_MAC | |||||
| PopupMenu extraAppleMenuItems; | |||||
| createExtraAppleMenuItems (extraAppleMenuItems); | |||||
| initCommandManager(); | |||||
| menuModel.reset (new MainMenuModel()); | |||||
| // workaround broken "Open Recent" submenu: not passing the | |||||
| // submenu's title here avoids the defect in JuceMainMenuHandler::addMenuItem | |||||
| MenuBarModel::setMacMainMenu (menuModel.get(), &extraAppleMenuItems); //, "Open Recent"); | |||||
| #if JUCE_MAC | |||||
| rebuildAppleMenu(); | |||||
| appleMenuRebuildListener = std::make_unique<AppleMenuRebuildListener>(); | |||||
| #endif | #endif | ||||
| if (getGlobalProperties().getValue (Ids::dontQueryForUpdate, {}).isEmpty()) | |||||
| LatestVersionCheckerAndUpdater::getInstance()->checkForNewVersion (false); | |||||
| settings->appearance.refreshPresetSchemeList(); | |||||
| setColourScheme (getGlobalProperties().getIntValue ("COLOUR SCHEME"), false); | |||||
| setEditorColourScheme (getGlobalProperties().getIntValue ("EDITOR COLOUR SCHEME"), false); | |||||
| updateEditorColourSchemeIfNeeded(); | |||||
| initialiseWindows (getCommandLineParameters()); | |||||
| ImageCache::setCacheTimeout (30 * 1000); | |||||
| icons = std::make_unique<Icons>(); | |||||
| tooltipWindow = std::make_unique<TooltipWindow> (nullptr, 1200); | |||||
| if (isAutomaticVersionCheckingEnabled()) | |||||
| LatestVersionCheckerAndUpdater::getInstance()->checkForNewVersion (true); | |||||
| if (! isRunningCommandLine && settings->shouldAskUserToSetJUCEPath()) | |||||
| showSetJUCEPathAlert(); | |||||
| initialiseWindows (getCommandLineParameters()); | |||||
| } | } | ||||
| static void deleteTemporaryFiles() | static void deleteTemporaryFiles() | ||||
| @@ -314,25 +300,52 @@ MenuBarModel* ProjucerApplication::getMenuModel() | |||||
| StringArray ProjucerApplication::getMenuNames() | StringArray ProjucerApplication::getMenuNames() | ||||
| { | { | ||||
| return { "File", "Edit", "View", "Build", "Window", "Document", "GUI Editor", "Tools", "Help" }; | |||||
| StringArray currentMenuNames { "File", "Edit", "View", "Build", "Window", "Document", "GUI Editor", "Tools", "Help" }; | |||||
| if (! isLiveBuildEnabled()) currentMenuNames.removeString ("Build"); | |||||
| if (! isGUIEditorEnabled()) currentMenuNames.removeString ("GUI Editor"); | |||||
| return currentMenuNames; | |||||
| } | } | ||||
| void ProjucerApplication::createMenu (PopupMenu& menu, const String& menuName) | |||||
| PopupMenu ProjucerApplication::createMenu (const String& menuName) | |||||
| { | { | ||||
| if (menuName == "File") createFileMenu (menu); | |||||
| else if (menuName == "Edit") createEditMenu (menu); | |||||
| else if (menuName == "View") createViewMenu (menu); | |||||
| else if (menuName == "Build") createBuildMenu (menu); | |||||
| else if (menuName == "Window") createWindowMenu (menu); | |||||
| else if (menuName == "Document") createDocumentMenu (menu); | |||||
| else if (menuName == "Tools") createToolsMenu (menu); | |||||
| else if (menuName == "Help") createHelpMenu (menu); | |||||
| else if (menuName == "GUI Editor") createGUIEditorMenu (menu); | |||||
| else jassertfalse; // names have changed? | |||||
| if (menuName == "File") | |||||
| return createFileMenu(); | |||||
| if (menuName == "Edit") | |||||
| return createEditMenu(); | |||||
| if (menuName == "View") | |||||
| return createViewMenu(); | |||||
| if (menuName == "Build") | |||||
| if (isLiveBuildEnabled()) | |||||
| return createBuildMenu(); | |||||
| if (menuName == "Window") | |||||
| return createWindowMenu(); | |||||
| if (menuName == "Document") | |||||
| return createDocumentMenu(); | |||||
| if (menuName == "Tools") | |||||
| return createToolsMenu(); | |||||
| if (menuName == "Help") | |||||
| return createHelpMenu(); | |||||
| if (menuName == "GUI Editor") | |||||
| if (isGUIEditorEnabled()) | |||||
| return createGUIEditorMenu(); | |||||
| jassertfalse; // names have changed? | |||||
| return {}; | |||||
| } | } | ||||
| void ProjucerApplication::createFileMenu (PopupMenu& menu) | |||||
| PopupMenu ProjucerApplication::createFileMenu() | |||||
| { | { | ||||
| PopupMenu menu; | |||||
| menu.addCommandItem (commandManager.get(), CommandIDs::newProject); | menu.addCommandItem (commandManager.get(), CommandIDs::newProject); | ||||
| menu.addCommandItem (commandManager.get(), CommandIDs::newProjectFromClipboard); | menu.addCommandItem (commandManager.get(), CommandIDs::newProjectFromClipboard); | ||||
| menu.addCommandItem (commandManager.get(), CommandIDs::newPIP); | menu.addCommandItem (commandManager.get(), CommandIDs::newPIP); | ||||
| @@ -353,12 +366,7 @@ void ProjucerApplication::createFileMenu (PopupMenu& menu) | |||||
| menu.addSubMenu ("Open Recent", recentFiles); | menu.addSubMenu ("Open Recent", recentFiles); | ||||
| } | } | ||||
| { | |||||
| PopupMenu examples; | |||||
| createExamplesPopupMenu (examples); | |||||
| menu.addSubMenu ("Open Example", examples); | |||||
| } | |||||
| menu.addSubMenu ("Open Example", createExamplesPopupMenu()); | |||||
| menu.addSeparator(); | menu.addSeparator(); | ||||
| menu.addCommandItem (commandManager.get(), CommandIDs::closeDocument); | menu.addCommandItem (commandManager.get(), CommandIDs::closeDocument); | ||||
| @@ -373,17 +381,25 @@ void ProjucerApplication::createFileMenu (PopupMenu& menu) | |||||
| menu.addCommandItem (commandManager.get(), CommandIDs::saveAndOpenInIDE); | menu.addCommandItem (commandManager.get(), CommandIDs::saveAndOpenInIDE); | ||||
| menu.addSeparator(); | menu.addSeparator(); | ||||
| #if ! JUCE_MAC | |||||
| menu.addCommandItem (commandManager.get(), CommandIDs::showAboutWindow); | |||||
| menu.addCommandItem (commandManager.get(), CommandIDs::checkForNewVersion); | |||||
| menu.addCommandItem (commandManager.get(), CommandIDs::showGlobalPathsWindow); | |||||
| menu.addSeparator(); | |||||
| menu.addCommandItem (commandManager.get(), StandardApplicationCommandIDs::quit); | |||||
| #endif | |||||
| #if ! JUCER_ENABLE_GPL_MODE | |||||
| menu.addCommandItem (commandManager.get(), CommandIDs::loginLogout); | |||||
| #endif | |||||
| #if ! JUCE_MAC | |||||
| menu.addCommandItem (commandManager.get(), CommandIDs::showAboutWindow); | |||||
| menu.addCommandItem (commandManager.get(), CommandIDs::checkForNewVersion); | |||||
| menu.addCommandItem (commandManager.get(), CommandIDs::enableNewVersionCheck); | |||||
| menu.addCommandItem (commandManager.get(), CommandIDs::showGlobalPathsWindow); | |||||
| menu.addSeparator(); | |||||
| menu.addCommandItem (commandManager.get(), StandardApplicationCommandIDs::quit); | |||||
| #endif | |||||
| return menu; | |||||
| } | } | ||||
| void ProjucerApplication::createEditMenu (PopupMenu& menu) | |||||
| PopupMenu ProjucerApplication::createEditMenu() | |||||
| { | { | ||||
| PopupMenu menu; | |||||
| menu.addCommandItem (commandManager.get(), StandardApplicationCommandIDs::undo); | menu.addCommandItem (commandManager.get(), StandardApplicationCommandIDs::undo); | ||||
| menu.addCommandItem (commandManager.get(), StandardApplicationCommandIDs::redo); | menu.addCommandItem (commandManager.get(), StandardApplicationCommandIDs::redo); | ||||
| menu.addSeparator(); | menu.addSeparator(); | ||||
| @@ -398,10 +414,12 @@ void ProjucerApplication::createEditMenu (PopupMenu& menu) | |||||
| menu.addCommandItem (commandManager.get(), CommandIDs::findSelection); | menu.addCommandItem (commandManager.get(), CommandIDs::findSelection); | ||||
| menu.addCommandItem (commandManager.get(), CommandIDs::findNext); | menu.addCommandItem (commandManager.get(), CommandIDs::findNext); | ||||
| menu.addCommandItem (commandManager.get(), CommandIDs::findPrevious); | menu.addCommandItem (commandManager.get(), CommandIDs::findPrevious); | ||||
| return menu; | |||||
| } | } | ||||
| void ProjucerApplication::createViewMenu (PopupMenu& menu) | |||||
| PopupMenu ProjucerApplication::createViewMenu() | |||||
| { | { | ||||
| PopupMenu menu; | |||||
| menu.addCommandItem (commandManager.get(), CommandIDs::showProjectSettings); | menu.addCommandItem (commandManager.get(), CommandIDs::showProjectSettings); | ||||
| menu.addCommandItem (commandManager.get(), CommandIDs::showProjectTab); | menu.addCommandItem (commandManager.get(), CommandIDs::showProjectTab); | ||||
| menu.addCommandItem (commandManager.get(), CommandIDs::showBuildTab); | menu.addCommandItem (commandManager.get(), CommandIDs::showBuildTab); | ||||
| @@ -412,10 +430,13 @@ void ProjucerApplication::createViewMenu (PopupMenu& menu) | |||||
| menu.addSeparator(); | menu.addSeparator(); | ||||
| createColourSchemeItems (menu); | createColourSchemeItems (menu); | ||||
| return menu; | |||||
| } | } | ||||
| void ProjucerApplication::createBuildMenu (PopupMenu& menu) | |||||
| PopupMenu ProjucerApplication::createBuildMenu() | |||||
| { | { | ||||
| PopupMenu menu; | |||||
| menu.addCommandItem (commandManager.get(), CommandIDs::toggleBuildEnabled); | menu.addCommandItem (commandManager.get(), CommandIDs::toggleBuildEnabled); | ||||
| menu.addCommandItem (commandManager.get(), CommandIDs::buildNow); | menu.addCommandItem (commandManager.get(), CommandIDs::buildNow); | ||||
| menu.addCommandItem (commandManager.get(), CommandIDs::toggleContinuousBuild); | menu.addCommandItem (commandManager.get(), CommandIDs::toggleContinuousBuild); | ||||
| @@ -429,55 +450,60 @@ void ProjucerApplication::createBuildMenu (PopupMenu& menu) | |||||
| menu.addSeparator(); | menu.addSeparator(); | ||||
| menu.addCommandItem (commandManager.get(), CommandIDs::nextError); | menu.addCommandItem (commandManager.get(), CommandIDs::nextError); | ||||
| menu.addCommandItem (commandManager.get(), CommandIDs::prevError); | menu.addCommandItem (commandManager.get(), CommandIDs::prevError); | ||||
| return menu; | |||||
| } | } | ||||
| void ProjucerApplication::createColourSchemeItems (PopupMenu& menu) | void ProjucerApplication::createColourSchemeItems (PopupMenu& menu) | ||||
| { | { | ||||
| PopupMenu colourSchemeMenu; | |||||
| { | |||||
| PopupMenu colourSchemeMenu; | |||||
| colourSchemeMenu.addItem (PopupMenu::Item ("Dark") | |||||
| .setTicked (selectedColourSchemeIndex == 0) | |||||
| .setAction ([this] { setColourScheme (0, true); updateEditorColourSchemeIfNeeded(); })); | |||||
| colourSchemeMenu.addItem (PopupMenu::Item ("Dark") | |||||
| .setTicked (selectedColourSchemeIndex == 0) | |||||
| .setAction ([this] { setColourScheme (0, true); updateEditorColourSchemeIfNeeded(); })); | |||||
| colourSchemeMenu.addItem (PopupMenu::Item ("Grey") | |||||
| .setTicked (selectedColourSchemeIndex == 1) | |||||
| .setAction ([this] { setColourScheme (1, true); updateEditorColourSchemeIfNeeded(); })); | |||||
| colourSchemeMenu.addItem (PopupMenu::Item ("Grey") | |||||
| .setTicked (selectedColourSchemeIndex == 1) | |||||
| .setAction ([this] { setColourScheme (1, true); updateEditorColourSchemeIfNeeded(); })); | |||||
| colourSchemeMenu.addItem (PopupMenu::Item ("Light") | |||||
| .setTicked (selectedColourSchemeIndex == 2) | |||||
| .setAction ([this] { setColourScheme (2, true); updateEditorColourSchemeIfNeeded(); })); | |||||
| colourSchemeMenu.addItem (PopupMenu::Item ("Light") | |||||
| .setTicked (selectedColourSchemeIndex == 2) | |||||
| .setAction ([this] { setColourScheme (2, true); updateEditorColourSchemeIfNeeded(); })); | |||||
| menu.addSubMenu ("Colour Scheme", colourSchemeMenu); | |||||
| menu.addSubMenu ("Colour Scheme", colourSchemeMenu); | |||||
| } | |||||
| //============================================================================== | |||||
| PopupMenu editorColourSchemeMenu; | |||||
| { | |||||
| PopupMenu editorColourSchemeMenu; | |||||
| auto& appearanceSettings = getAppSettings().appearance; | |||||
| auto& appearanceSettings = getAppSettings().appearance; | |||||
| appearanceSettings.refreshPresetSchemeList(); | |||||
| auto schemes = appearanceSettings.getPresetSchemes(); | |||||
| appearanceSettings.refreshPresetSchemeList(); | |||||
| auto schemes = appearanceSettings.getPresetSchemes(); | |||||
| auto i = 0; | |||||
| auto i = 0; | |||||
| for (auto& s : schemes) | |||||
| { | |||||
| editorColourSchemeMenu.addItem (PopupMenu::Item (s) | |||||
| .setEnabled (editorColourSchemeWindow == nullptr) | |||||
| .setTicked (selectedEditorColourSchemeIndex == i) | |||||
| .setAction ([this, i] { setEditorColourScheme (i, true); })); | |||||
| ++i; | |||||
| } | |||||
| for (auto& s : schemes) | |||||
| { | |||||
| editorColourSchemeMenu.addItem (PopupMenu::Item (s) | |||||
| .setEnabled (editorColourSchemeWindow == nullptr) | |||||
| .setTicked (selectedEditorColourSchemeIndex == i) | |||||
| .setAction ([this, i] { setEditorColourScheme (i, true); })); | |||||
| ++i; | |||||
| } | |||||
| editorColourSchemeMenu.addSeparator(); | |||||
| editorColourSchemeMenu.addItem (PopupMenu::Item ("Create...") | |||||
| .setEnabled (editorColourSchemeWindow == nullptr) | |||||
| .setAction ([this] { showEditorColourSchemeWindow(); })); | |||||
| editorColourSchemeMenu.addSeparator(); | |||||
| editorColourSchemeMenu.addItem (PopupMenu::Item ("Create...") | |||||
| .setEnabled (editorColourSchemeWindow == nullptr) | |||||
| .setAction ([this] { showEditorColourSchemeWindow(); })); | |||||
| menu.addSubMenu ("Editor Colour Scheme", editorColourSchemeMenu); | |||||
| menu.addSubMenu ("Editor Colour Scheme", editorColourSchemeMenu); | |||||
| } | |||||
| } | } | ||||
| void ProjucerApplication::createWindowMenu (PopupMenu& menu) | |||||
| PopupMenu ProjucerApplication::createWindowMenu() | |||||
| { | { | ||||
| PopupMenu menu; | |||||
| menu.addCommandItem (commandManager.get(), CommandIDs::goToPreviousWindow); | menu.addCommandItem (commandManager.get(), CommandIDs::goToPreviousWindow); | ||||
| menu.addCommandItem (commandManager.get(), CommandIDs::goToNextWindow); | menu.addCommandItem (commandManager.get(), CommandIDs::goToNextWindow); | ||||
| menu.addCommandItem (commandManager.get(), CommandIDs::closeWindow); | menu.addCommandItem (commandManager.get(), CommandIDs::closeWindow); | ||||
| @@ -486,16 +512,22 @@ void ProjucerApplication::createWindowMenu (PopupMenu& menu) | |||||
| int counter = 0; | int counter = 0; | ||||
| for (auto* window : mainWindowList.windows) | for (auto* window : mainWindowList.windows) | ||||
| { | |||||
| if (window != nullptr) | if (window != nullptr) | ||||
| { | |||||
| if (auto* project = window->getProject()) | if (auto* project = window->getProject()) | ||||
| menu.addItem (openWindowsBaseID + counter++, project->getProjectNameString()); | menu.addItem (openWindowsBaseID + counter++, project->getProjectNameString()); | ||||
| } | |||||
| } | |||||
| menu.addSeparator(); | menu.addSeparator(); | ||||
| menu.addCommandItem (commandManager.get(), CommandIDs::closeAllWindows); | menu.addCommandItem (commandManager.get(), CommandIDs::closeAllWindows); | ||||
| return menu; | |||||
| } | } | ||||
| void ProjucerApplication::createDocumentMenu (PopupMenu& menu) | |||||
| PopupMenu ProjucerApplication::createDocumentMenu() | |||||
| { | { | ||||
| PopupMenu menu; | |||||
| menu.addCommandItem (commandManager.get(), CommandIDs::goToPreviousDoc); | menu.addCommandItem (commandManager.get(), CommandIDs::goToPreviousDoc); | ||||
| menu.addCommandItem (commandManager.get(), CommandIDs::goToNextDoc); | menu.addCommandItem (commandManager.get(), CommandIDs::goToNextDoc); | ||||
| menu.addCommandItem (commandManager.get(), CommandIDs::goToCounterpart); | menu.addCommandItem (commandManager.get(), CommandIDs::goToCounterpart); | ||||
| @@ -511,34 +543,46 @@ void ProjucerApplication::createDocumentMenu (PopupMenu& menu) | |||||
| menu.addSeparator(); | menu.addSeparator(); | ||||
| menu.addCommandItem (commandManager.get(), CommandIDs::closeAllDocuments); | menu.addCommandItem (commandManager.get(), CommandIDs::closeAllDocuments); | ||||
| return menu; | |||||
| } | } | ||||
| void ProjucerApplication::createToolsMenu (PopupMenu& menu) | |||||
| PopupMenu ProjucerApplication::createToolsMenu() | |||||
| { | { | ||||
| PopupMenu menu; | |||||
| menu.addCommandItem (commandManager.get(), CommandIDs::showUTF8Tool); | menu.addCommandItem (commandManager.get(), CommandIDs::showUTF8Tool); | ||||
| menu.addCommandItem (commandManager.get(), CommandIDs::showSVGPathTool); | menu.addCommandItem (commandManager.get(), CommandIDs::showSVGPathTool); | ||||
| menu.addCommandItem (commandManager.get(), CommandIDs::showTranslationTool); | menu.addCommandItem (commandManager.get(), CommandIDs::showTranslationTool); | ||||
| menu.addSeparator(); | |||||
| menu.addCommandItem (commandManager.get(), CommandIDs::enableLiveBuild); | |||||
| menu.addCommandItem (commandManager.get(), CommandIDs::enableGUIEditor); | |||||
| return menu; | |||||
| } | } | ||||
| void ProjucerApplication::createHelpMenu (PopupMenu& menu) | |||||
| PopupMenu ProjucerApplication::createHelpMenu() | |||||
| { | { | ||||
| PopupMenu menu; | |||||
| menu.addCommandItem (commandManager.get(), CommandIDs::showForum); | menu.addCommandItem (commandManager.get(), CommandIDs::showForum); | ||||
| menu.addSeparator(); | menu.addSeparator(); | ||||
| menu.addCommandItem (commandManager.get(), CommandIDs::showAPIModules); | menu.addCommandItem (commandManager.get(), CommandIDs::showAPIModules); | ||||
| menu.addCommandItem (commandManager.get(), CommandIDs::showAPIClasses); | menu.addCommandItem (commandManager.get(), CommandIDs::showAPIClasses); | ||||
| menu.addCommandItem (commandManager.get(), CommandIDs::showTutorials); | menu.addCommandItem (commandManager.get(), CommandIDs::showTutorials); | ||||
| return menu; | |||||
| } | } | ||||
| void ProjucerApplication::createExtraAppleMenuItems (PopupMenu& menu) | |||||
| PopupMenu ProjucerApplication::createExtraAppleMenuItems() | |||||
| { | { | ||||
| PopupMenu menu; | |||||
| menu.addCommandItem (commandManager.get(), CommandIDs::showAboutWindow); | menu.addCommandItem (commandManager.get(), CommandIDs::showAboutWindow); | ||||
| menu.addCommandItem (commandManager.get(), CommandIDs::checkForNewVersion); | menu.addCommandItem (commandManager.get(), CommandIDs::checkForNewVersion); | ||||
| menu.addCommandItem (commandManager.get(), CommandIDs::enableNewVersionCheck); | |||||
| menu.addSeparator(); | menu.addSeparator(); | ||||
| menu.addCommandItem (commandManager.get(), CommandIDs::showGlobalPathsWindow); | menu.addCommandItem (commandManager.get(), CommandIDs::showGlobalPathsWindow); | ||||
| return menu; | |||||
| } | } | ||||
| void ProjucerApplication::createExamplesPopupMenu (PopupMenu& menu) noexcept | |||||
| PopupMenu ProjucerApplication::createExamplesPopupMenu() noexcept | |||||
| { | { | ||||
| PopupMenu menu; | |||||
| numExamples = 0; | numExamples = 0; | ||||
| for (auto& dir : getSortedExampleDirectories()) | for (auto& dir : getSortedExampleDirectories()) | ||||
| { | { | ||||
| @@ -561,8 +605,21 @@ void ProjucerApplication::createExamplesPopupMenu (PopupMenu& menu) noexcept | |||||
| menu.addSeparator(); | menu.addSeparator(); | ||||
| menu.addCommandItem (commandManager.get(), CommandIDs::launchDemoRunner); | menu.addCommandItem (commandManager.get(), CommandIDs::launchDemoRunner); | ||||
| } | } | ||||
| return menu; | |||||
| } | } | ||||
| #if JUCE_MAC | |||||
| void ProjucerApplication::rebuildAppleMenu() | |||||
| { | |||||
| auto extraAppleMenuItems = createExtraAppleMenuItems(); | |||||
| // workaround broken "Open Recent" submenu: not passing the | |||||
| // submenu's title here avoids the defect in JuceMainMenuHandler::addMenuItem | |||||
| MenuBarModel::setMacMainMenu (menuModel.get(), &extraAppleMenuItems); //, "Open Recent"); | |||||
| } | |||||
| #endif | |||||
| //============================================================================== | //============================================================================== | ||||
| static File getJUCEExamplesDirectoryPathFromGlobal() | static File getJUCEExamplesDirectoryPathFromGlobal() | ||||
| { | { | ||||
| @@ -907,12 +964,16 @@ void ProjucerApplication::getAllCommands (Array <CommandID>& commands) | |||||
| CommandIDs::showGlobalPathsWindow, | CommandIDs::showGlobalPathsWindow, | ||||
| CommandIDs::showUTF8Tool, | CommandIDs::showUTF8Tool, | ||||
| CommandIDs::showSVGPathTool, | CommandIDs::showSVGPathTool, | ||||
| CommandIDs::enableLiveBuild, | |||||
| CommandIDs::enableGUIEditor, | |||||
| CommandIDs::showAboutWindow, | CommandIDs::showAboutWindow, | ||||
| CommandIDs::checkForNewVersion, | CommandIDs::checkForNewVersion, | ||||
| CommandIDs::enableNewVersionCheck, | |||||
| CommandIDs::showForum, | CommandIDs::showForum, | ||||
| CommandIDs::showAPIModules, | CommandIDs::showAPIModules, | ||||
| CommandIDs::showAPIClasses, | CommandIDs::showAPIClasses, | ||||
| CommandIDs::showTutorials }; | |||||
| CommandIDs::showTutorials, | |||||
| CommandIDs::loginLogout }; | |||||
| commands.addArray (ids, numElementsInArray (ids)); | commands.addArray (ids, numElementsInArray (ids)); | ||||
| } | } | ||||
| @@ -990,6 +1051,20 @@ void ProjucerApplication::getCommandInfo (CommandID commandID, ApplicationComman | |||||
| result.setInfo ("SVG Path Converter", "Shows the SVG->Path data conversion utility", CommandCategories::general, 0); | result.setInfo ("SVG Path Converter", "Shows the SVG->Path data conversion utility", CommandCategories::general, 0); | ||||
| break; | break; | ||||
| case CommandIDs::enableLiveBuild: | |||||
| result.setInfo ("Live-Build Enabled", | |||||
| "Enables or disables the live-build functionality", | |||||
| CommandCategories::general, | |||||
| (isLiveBuildEnabled() ? ApplicationCommandInfo::isTicked : 0)); | |||||
| break; | |||||
| case CommandIDs::enableGUIEditor: | |||||
| result.setInfo ("GUI Editor Enabled", | |||||
| "Enables or disables the GUI editor functionality", | |||||
| CommandCategories::general, | |||||
| (isGUIEditorEnabled() ? ApplicationCommandInfo::isTicked : 0)); | |||||
| break; | |||||
| case CommandIDs::showAboutWindow: | case CommandIDs::showAboutWindow: | ||||
| result.setInfo ("About Projucer", "Shows the Projucer's 'About' page.", CommandCategories::general, 0); | result.setInfo ("About Projucer", "Shows the Projucer's 'About' page.", CommandCategories::general, 0); | ||||
| break; | break; | ||||
| @@ -998,6 +1073,13 @@ void ProjucerApplication::getCommandInfo (CommandID commandID, ApplicationComman | |||||
| result.setInfo ("Check for New Version...", "Checks the web server for a new version of JUCE", CommandCategories::general, 0); | result.setInfo ("Check for New Version...", "Checks the web server for a new version of JUCE", CommandCategories::general, 0); | ||||
| break; | break; | ||||
| case CommandIDs::enableNewVersionCheck: | |||||
| result.setInfo ("Automatically Check for New Versions", | |||||
| "Enables automatic background checking for new versions of JUCE.", | |||||
| CommandCategories::general, | |||||
| (isAutomaticVersionCheckingEnabled() ? ApplicationCommandInfo::isTicked : 0)); | |||||
| break; | |||||
| case CommandIDs::showForum: | case CommandIDs::showForum: | ||||
| result.setInfo ("JUCE Community Forum", "Shows the JUCE community forum in a browser", CommandCategories::general, 0); | result.setInfo ("JUCE Community Forum", "Shows the JUCE community forum in a browser", CommandCategories::general, 0); | ||||
| break; | break; | ||||
| @@ -1014,6 +1096,19 @@ void ProjucerApplication::getCommandInfo (CommandID commandID, ApplicationComman | |||||
| result.setInfo ("JUCE Tutorials", "Shows the JUCE tutorials in a browser", CommandCategories::general, 0); | result.setInfo ("JUCE Tutorials", "Shows the JUCE tutorials in a browser", CommandCategories::general, 0); | ||||
| break; | break; | ||||
| case CommandIDs::loginLogout: | |||||
| { | |||||
| auto licenseState = licenseController->getCurrentState(); | |||||
| if (licenseState.isGPL()) | |||||
| result.setInfo ("Disable GPL mode", "Disables GPL mode", CommandCategories::general, 0); | |||||
| else | |||||
| result.setInfo (licenseState.isValid() ? String ("Sign out ") + licenseState.username + "..." : String ("Sign in..."), | |||||
| "Log out of your JUCE account", | |||||
| CommandCategories::general, 0); | |||||
| break; | |||||
| } | |||||
| default: | default: | ||||
| JUCEApplication::getCommandInfo (commandID, result); | JUCEApplication::getCommandInfo (commandID, result); | ||||
| break; | break; | ||||
| @@ -1031,17 +1126,21 @@ bool ProjucerApplication::perform (const InvocationInfo& info) | |||||
| case CommandIDs::launchDemoRunner: launchDemoRunner(); break; | case CommandIDs::launchDemoRunner: launchDemoRunner(); break; | ||||
| case CommandIDs::saveAll: saveAllDocuments(); break; | case CommandIDs::saveAll: saveAllDocuments(); break; | ||||
| case CommandIDs::closeAllWindows: closeAllMainWindowsAndQuitIfNeeded(); break; | case CommandIDs::closeAllWindows: closeAllMainWindowsAndQuitIfNeeded(); break; | ||||
| case CommandIDs::closeAllDocuments: closeAllDocuments (true); break; | |||||
| case CommandIDs::closeAllDocuments: closeAllDocuments (OpenDocumentManager::SaveIfNeeded::yes); break; | |||||
| case CommandIDs::clearRecentFiles: clearRecentFiles(); break; | case CommandIDs::clearRecentFiles: clearRecentFiles(); break; | ||||
| case CommandIDs::showUTF8Tool: showUTF8ToolWindow(); break; | case CommandIDs::showUTF8Tool: showUTF8ToolWindow(); break; | ||||
| case CommandIDs::showSVGPathTool: showSVGPathDataToolWindow(); break; | case CommandIDs::showSVGPathTool: showSVGPathDataToolWindow(); break; | ||||
| case CommandIDs::enableLiveBuild: enableOrDisableLiveBuild(); break; | |||||
| case CommandIDs::enableGUIEditor: enableOrDisableGUIEditor(); break; | |||||
| case CommandIDs::showGlobalPathsWindow: showPathsWindow (false); break; | case CommandIDs::showGlobalPathsWindow: showPathsWindow (false); break; | ||||
| case CommandIDs::showAboutWindow: showAboutWindow(); break; | case CommandIDs::showAboutWindow: showAboutWindow(); break; | ||||
| case CommandIDs::checkForNewVersion: LatestVersionCheckerAndUpdater::getInstance()->checkForNewVersion (true); break; | |||||
| case CommandIDs::checkForNewVersion: LatestVersionCheckerAndUpdater::getInstance()->checkForNewVersion (false); break; | |||||
| case CommandIDs::enableNewVersionCheck: setAutomaticVersionCheckingEnabled (! isAutomaticVersionCheckingEnabled()); break; | |||||
| case CommandIDs::showForum: launchForumBrowser(); break; | case CommandIDs::showForum: launchForumBrowser(); break; | ||||
| case CommandIDs::showAPIModules: launchModulesBrowser(); break; | case CommandIDs::showAPIModules: launchModulesBrowser(); break; | ||||
| case CommandIDs::showAPIClasses: launchClassesBrowser(); break; | case CommandIDs::showAPIClasses: launchClassesBrowser(); break; | ||||
| case CommandIDs::showTutorials: launchTutorialsBrowser(); break; | case CommandIDs::showTutorials: launchTutorialsBrowser(); break; | ||||
| case CommandIDs::loginLogout: doLoginOrLogout(); break; | |||||
| default: return JUCEApplication::perform (info); | default: return JUCEApplication::perform (info); | ||||
| } | } | ||||
| @@ -1104,7 +1203,7 @@ void ProjucerApplication::saveAllDocuments() | |||||
| pcc->refreshProjectTreeFileStatuses(); | pcc->refreshProjectTreeFileStatuses(); | ||||
| } | } | ||||
| bool ProjucerApplication::closeAllDocuments (bool askUserToSave) | |||||
| bool ProjucerApplication::closeAllDocuments (OpenDocumentManager::SaveIfNeeded askUserToSave) | |||||
| { | { | ||||
| return openDocumentManager.closeAll (askUserToSave); | return openDocumentManager.closeAll (askUserToSave); | ||||
| } | } | ||||
| @@ -1154,6 +1253,26 @@ void ProjucerApplication::showSVGPathDataToolWindow() | |||||
| 500, 500, 300, 300, 1000, 1000); | 500, 500, 300, 300, 1000, 1000); | ||||
| } | } | ||||
| bool ProjucerApplication::isLiveBuildEnabled() const | |||||
| { | |||||
| return getGlobalProperties().getBoolValue (Ids::liveBuildEnabled); | |||||
| } | |||||
| void ProjucerApplication::enableOrDisableLiveBuild() | |||||
| { | |||||
| getGlobalProperties().setValue (Ids::liveBuildEnabled, ! isLiveBuildEnabled()); | |||||
| } | |||||
| bool ProjucerApplication::isGUIEditorEnabled() const | |||||
| { | |||||
| return getGlobalProperties().getBoolValue (Ids::guiEditorEnabled); | |||||
| } | |||||
| void ProjucerApplication::enableOrDisableGUIEditor() | |||||
| { | |||||
| getGlobalProperties().setValue (Ids::guiEditorEnabled, ! isGUIEditorEnabled()); | |||||
| } | |||||
| void ProjucerApplication::showAboutWindow() | void ProjucerApplication::showAboutWindow() | ||||
| { | { | ||||
| if (aboutWindow != nullptr) | if (aboutWindow != nullptr) | ||||
| @@ -1230,6 +1349,26 @@ void ProjucerApplication::launchTutorialsBrowser() | |||||
| tutorialsLink.launchInDefaultBrowser(); | tutorialsLink.launchInDefaultBrowser(); | ||||
| } | } | ||||
| void ProjucerApplication::doLoginOrLogout() | |||||
| { | |||||
| if (licenseController->getCurrentState().type == LicenseState::Type::none) | |||||
| { | |||||
| if (auto* window = mainWindowList.getMainWindowWithLoginFormOpen()) | |||||
| { | |||||
| window->toFront (true); | |||||
| } | |||||
| else | |||||
| { | |||||
| mainWindowList.createWindowIfNoneAreOpen(); | |||||
| mainWindowList.getFrontmostWindow()->showLoginFormOverlay(); | |||||
| } | |||||
| } | |||||
| else | |||||
| { | |||||
| licenseController->resetState(); | |||||
| } | |||||
| } | |||||
| //============================================================================== | //============================================================================== | ||||
| struct FileWithTime | struct FileWithTime | ||||
| { | { | ||||
| @@ -1286,13 +1425,6 @@ PropertiesFile::Options ProjucerApplication::getPropertyFileOptionsFor (const St | |||||
| return options; | return options; | ||||
| } | } | ||||
| void ProjucerApplication::updateAllBuildTabs() | |||||
| { | |||||
| for (int i = 0; i < mainWindowList.windows.size(); ++i) | |||||
| if (ProjectContentComponent* p = mainWindowList.windows.getUnchecked(i)->getProjectContentComponent()) | |||||
| p->rebuildProjectTabs(); | |||||
| } | |||||
| void ProjucerApplication::initCommandManager() | void ProjucerApplication::initCommandManager() | ||||
| { | { | ||||
| commandManager.reset (new ApplicationCommandManager()); | commandManager.reset (new ApplicationCommandManager()); | ||||
| @@ -1307,28 +1439,7 @@ void ProjucerApplication::initCommandManager() | |||||
| registerGUIEditorCommands(); | registerGUIEditorCommands(); | ||||
| } | } | ||||
| void ProjucerApplication::showSetJUCEPathAlert() | |||||
| { | |||||
| auto& lf = Desktop::getInstance().getDefaultLookAndFeel(); | |||||
| pathAlert.reset (lf.createAlertWindow ("Set JUCE Path", "Your global JUCE path is invalid. This path is used to access the JUCE examples and demo project - " | |||||
| "would you like to set it now?", | |||||
| "Set path", "Cancel", "Don't ask again", | |||||
| AlertWindow::WarningIcon, 3, | |||||
| mainWindowList.getFrontmostWindow (false))); | |||||
| pathAlert->enterModalState (true, ModalCallbackFunction::create ([this] (int retVal) | |||||
| { | |||||
| pathAlert.reset (nullptr); | |||||
| if (retVal == 1) | |||||
| showPathsWindow (true); | |||||
| else if (retVal == 0) | |||||
| settings->setDontAskAboutJUCEPathAgain(); | |||||
| })); | |||||
| } | |||||
| void rescanModules (AvailableModuleList& list, const Array<File>& paths, bool async) | |||||
| void rescanModules (AvailableModulesList& list, const Array<File>& paths, bool async) | |||||
| { | { | ||||
| if (async) | if (async) | ||||
| list.scanPathsAsync (paths); | list.scanPathsAsync (paths); | ||||
| @@ -1338,12 +1449,32 @@ void rescanModules (AvailableModuleList& list, const Array<File>& paths, bool as | |||||
| void ProjucerApplication::rescanJUCEPathModules() | void ProjucerApplication::rescanJUCEPathModules() | ||||
| { | { | ||||
| rescanModules (jucePathModuleList, { getAppSettings().getStoredPath (Ids::defaultJuceModulePath, TargetOS::getThisOS()).get().toString() }, ! isRunningCommandLine); | |||||
| rescanModules (jucePathModulesList, { getAppSettings().getStoredPath (Ids::defaultJuceModulePath, TargetOS::getThisOS()).get().toString() }, ! isRunningCommandLine); | |||||
| } | } | ||||
| void ProjucerApplication::rescanUserPathModules() | void ProjucerApplication::rescanUserPathModules() | ||||
| { | { | ||||
| rescanModules (userPathsModuleList, { getAppSettings().getStoredPath (Ids::defaultUserModulePath, TargetOS::getThisOS()).get().toString() }, ! isRunningCommandLine); | |||||
| rescanModules (userPathsModulesList, { getAppSettings().getStoredPath (Ids::defaultUserModulePath, TargetOS::getThisOS()).get().toString() }, ! isRunningCommandLine); | |||||
| } | |||||
| bool ProjucerApplication::isAutomaticVersionCheckingEnabled() const | |||||
| { | |||||
| return ! getGlobalProperties().getBoolValue (Ids::dontQueryForUpdate); | |||||
| } | |||||
| void ProjucerApplication::setAutomaticVersionCheckingEnabled (bool enabled) | |||||
| { | |||||
| getGlobalProperties().setValue (Ids::dontQueryForUpdate, ! enabled); | |||||
| } | |||||
| bool ProjucerApplication::shouldPromptUserAboutIncorrectJUCEPath() const | |||||
| { | |||||
| return ! getGlobalProperties().getBoolValue (Ids::dontAskAboutJUCEPath); | |||||
| } | |||||
| void ProjucerApplication::setShouldPromptUserAboutIncorrectJUCEPath (bool shouldPrompt) | |||||
| { | |||||
| getGlobalProperties().setValue (Ids::dontAskAboutJUCEPath, ! shouldPrompt); | |||||
| } | } | ||||
| void ProjucerApplication::selectEditorColourSchemeWithName (const String& schemeName) | void ProjucerApplication::selectEditorColourSchemeWithName (const String& schemeName) | ||||
| @@ -1383,7 +1514,7 @@ void ProjucerApplication::setColourScheme (int index, bool saveSetting) | |||||
| if (saveSetting) | if (saveSetting) | ||||
| { | { | ||||
| auto& properties = settings->getGlobalProperties(); | |||||
| auto& properties = getGlobalProperties(); | |||||
| properties.setValue ("COLOUR SCHEME", index); | properties.setValue ("COLOUR SCHEME", index); | ||||
| } | } | ||||
| @@ -1403,7 +1534,7 @@ void ProjucerApplication::setEditorColourScheme (int index, bool saveSetting) | |||||
| if (saveSetting) | if (saveSetting) | ||||
| { | { | ||||
| auto& properties = settings->getGlobalProperties(); | |||||
| auto& properties = getGlobalProperties(); | |||||
| properties.setValue ("EDITOR COLOUR SCHEME", index); | properties.setValue ("EDITOR COLOUR SCHEME", index); | ||||
| } | } | ||||
| @@ -1412,13 +1543,13 @@ void ProjucerApplication::setEditorColourScheme (int index, bool saveSetting) | |||||
| getCommandManager().commandStatusChanged(); | getCommandManager().commandStatusChanged(); | ||||
| } | } | ||||
| bool ProjucerApplication::isEditorColourSchemeADefaultScheme (const StringArray& schemes, int editorColourSchemeIndex) | |||||
| bool isEditorColourSchemeADefaultScheme (const StringArray& schemes, int editorColourSchemeIndex) | |||||
| { | { | ||||
| auto& schemeName = schemes[editorColourSchemeIndex]; | auto& schemeName = schemes[editorColourSchemeIndex]; | ||||
| return (schemeName == "Default (Dark)" || schemeName == "Default (Light)"); | return (schemeName == "Default (Dark)" || schemeName == "Default (Light)"); | ||||
| } | } | ||||
| int ProjucerApplication::getEditorColourSchemeForGUIColourScheme (const StringArray& schemes, int guiColourSchemeIndex) | |||||
| int getEditorColourSchemeForGUIColourScheme (const StringArray& schemes, int guiColourSchemeIndex) | |||||
| { | { | ||||
| auto defaultDarkEditorIndex = schemes.indexOf ("Default (Dark)"); | auto defaultDarkEditorIndex = schemes.indexOf ("Default (Dark)"); | ||||
| auto defaultLightEditorIndex = schemes.indexOf ("Default (Light)"); | auto defaultLightEditorIndex = schemes.indexOf ("Default (Light)"); | ||||
| @@ -18,8 +18,9 @@ | |||||
| #pragma once | #pragma once | ||||
| #include "UserAccount/jucer_LicenseController.h" | |||||
| #include "jucer_MainWindow.h" | #include "jucer_MainWindow.h" | ||||
| #include "../Project/jucer_Module.h" | |||||
| #include "../Project/Modules/jucer_Modules.h" | |||||
| #include "jucer_AutoUpdater.h" | #include "jucer_AutoUpdater.h" | ||||
| #include "../CodeEditor/jucer_SourceCodeEditor.h" | #include "../CodeEditor/jucer_SourceCodeEditor.h" | ||||
| #include "../Utility/UI/jucer_ProjucerLookAndFeel.h" | #include "../Utility/UI/jucer_ProjucerLookAndFeel.h" | ||||
| @@ -31,7 +32,7 @@ class ProjucerApplication : public JUCEApplication, | |||||
| private AsyncUpdater | private AsyncUpdater | ||||
| { | { | ||||
| public: | public: | ||||
| ProjucerApplication(); | |||||
| ProjucerApplication() = default; | |||||
| static ProjucerApplication& getApp(); | static ProjucerApplication& getApp(); | ||||
| static ApplicationCommandManager& getCommandManager(); | static ApplicationCommandManager& getCommandManager(); | ||||
| @@ -42,7 +43,6 @@ public: | |||||
| void systemRequestedQuit() override; | void systemRequestedQuit() override; | ||||
| void deleteLogger(); | void deleteLogger(); | ||||
| //============================================================================== | |||||
| const String getApplicationName() override { return "Projucer"; } | const String getApplicationName() override { return "Projucer"; } | ||||
| const String getApplicationVersion() override { return ProjectInfo::versionString; } | const String getApplicationVersion() override { return ProjectInfo::versionString; } | ||||
| @@ -53,67 +53,34 @@ public: | |||||
| //============================================================================== | //============================================================================== | ||||
| MenuBarModel* getMenuModel(); | MenuBarModel* getMenuModel(); | ||||
| StringArray getMenuNames(); | |||||
| void createMenu (PopupMenu&, const String& menuName); | |||||
| void createFileMenu (PopupMenu&); | |||||
| void createEditMenu (PopupMenu&); | |||||
| void createViewMenu (PopupMenu&); | |||||
| void createBuildMenu (PopupMenu&); | |||||
| void createColourSchemeItems (PopupMenu&); | |||||
| void createWindowMenu (PopupMenu&); | |||||
| void createDocumentMenu (PopupMenu&); | |||||
| void createToolsMenu (PopupMenu&); | |||||
| void createHelpMenu (PopupMenu&); | |||||
| void createExtraAppleMenuItems (PopupMenu&); | |||||
| void handleMainMenuCommand (int menuItemID); | |||||
| //============================================================================== | |||||
| void getAllCommands (Array<CommandID>&) override; | void getAllCommands (Array<CommandID>&) override; | ||||
| void getCommandInfo (CommandID commandID, ApplicationCommandInfo&) override; | void getCommandInfo (CommandID commandID, ApplicationCommandInfo&) override; | ||||
| bool perform (const InvocationInfo&) override; | bool perform (const InvocationInfo&) override; | ||||
| //============================================================================== | |||||
| void createNewProject(); | |||||
| void createNewProjectFromClipboard(); | |||||
| void createNewPIP(); | |||||
| void askUserToOpenFile(); | |||||
| bool openFile (const File&); | |||||
| void saveAllDocuments(); | |||||
| bool closeAllDocuments (bool askUserToSave); | |||||
| bool closeAllMainWindows(); | |||||
| void closeAllMainWindowsAndQuitIfNeeded(); | |||||
| void clearRecentFiles(); | |||||
| PropertiesFile::Options getPropertyFileOptionsFor (const String& filename, bool isProjectSettings); | |||||
| bool isLiveBuildEnabled() const; | |||||
| bool isGUIEditorEnabled() const; | |||||
| //============================================================================== | //============================================================================== | ||||
| void showUTF8ToolWindow(); | |||||
| void showSVGPathDataToolWindow(); | |||||
| void showAboutWindow(); | |||||
| bool openFile (const File&); | |||||
| void showPathsWindow (bool highlightJUCEPath = false); | void showPathsWindow (bool highlightJUCEPath = false); | ||||
| void showEditorColourSchemeWindow(); | |||||
| void showPIPCreatorWindow(); | |||||
| void launchForumBrowser(); | |||||
| void launchModulesBrowser(); | |||||
| void launchClassesBrowser(); | |||||
| void launchTutorialsBrowser(); | |||||
| void updateAllBuildTabs(); | |||||
| //============================================================================== | |||||
| PropertiesFile::Options getPropertyFileOptionsFor (const String& filename, bool isProjectSettings); | |||||
| void selectEditorColourSchemeWithName (const String& schemeName); | void selectEditorColourSchemeWithName (const String& schemeName); | ||||
| static bool isEditorColourSchemeADefaultScheme (const StringArray& schemes, int editorColourSchemeIndex); | |||||
| static int getEditorColourSchemeForGUIColourScheme (const StringArray& schemes, int guiColourSchemeIndex); | |||||
| //============================================================================== | //============================================================================== | ||||
| void rescanJUCEPathModules(); | void rescanJUCEPathModules(); | ||||
| void rescanUserPathModules(); | void rescanUserPathModules(); | ||||
| AvailableModuleList& getJUCEPathModuleList() { return jucePathModuleList; } | |||||
| AvailableModuleList& getUserPathsModuleList() { return userPathsModuleList; } | |||||
| AvailableModulesList& getJUCEPathModulesList() { return jucePathModulesList; } | |||||
| AvailableModulesList& getUserPathsModulesList() { return userPathsModulesList; } | |||||
| LicenseController& getLicenseController() { return *licenseController; } | |||||
| bool isAutomaticVersionCheckingEnabled() const; | |||||
| void setAutomaticVersionCheckingEnabled (bool shouldBeEnabled); | |||||
| bool shouldPromptUserAboutIncorrectJUCEPath() const; | |||||
| void setShouldPromptUserAboutIncorrectJUCEPath (bool shouldPrompt); | |||||
| //============================================================================== | //============================================================================== | ||||
| ProjucerLookAndFeel lookAndFeel; | ProjucerLookAndFeel lookAndFeel; | ||||
| @@ -128,23 +95,42 @@ public: | |||||
| OpenDocumentManager openDocumentManager; | OpenDocumentManager openDocumentManager; | ||||
| std::unique_ptr<ApplicationCommandManager> commandManager; | std::unique_ptr<ApplicationCommandManager> commandManager; | ||||
| std::unique_ptr<Component> utf8Window, svgPathWindow, aboutWindow, pathsWindow, | |||||
| editorColourSchemeWindow, pipCreatorWindow; | |||||
| std::unique_ptr<FileLogger> logger; | |||||
| bool isRunningCommandLine; | |||||
| bool isRunningCommandLine = false; | |||||
| std::unique_ptr<ChildProcessCache> childProcessCache; | std::unique_ptr<ChildProcessCache> childProcessCache; | ||||
| private: | private: | ||||
| //============================================================================== | //============================================================================== | ||||
| void handleAsyncUpdate() override; | void handleAsyncUpdate() override; | ||||
| void initCommandManager(); | |||||
| void initCommandManager(); | |||||
| bool initialiseLogger (const char* filePrefix); | bool initialiseLogger (const char* filePrefix); | ||||
| void initialiseWindows (const String& commandLine); | void initialiseWindows (const String& commandLine); | ||||
| void createExamplesPopupMenu (PopupMenu&) noexcept; | |||||
| void createNewProject(); | |||||
| void createNewProjectFromClipboard(); | |||||
| void createNewPIP(); | |||||
| void askUserToOpenFile(); | |||||
| void saveAllDocuments(); | |||||
| bool closeAllDocuments (OpenDocumentManager::SaveIfNeeded askUserToSave); | |||||
| bool closeAllMainWindows(); | |||||
| void closeAllMainWindowsAndQuitIfNeeded(); | |||||
| void clearRecentFiles(); | |||||
| StringArray getMenuNames(); | |||||
| PopupMenu createMenu (const String& menuName); | |||||
| PopupMenu createFileMenu(); | |||||
| PopupMenu createEditMenu(); | |||||
| PopupMenu createViewMenu(); | |||||
| PopupMenu createBuildMenu(); | |||||
| void createColourSchemeItems (PopupMenu&); | |||||
| PopupMenu createWindowMenu(); | |||||
| PopupMenu createDocumentMenu(); | |||||
| PopupMenu createToolsMenu(); | |||||
| PopupMenu createHelpMenu(); | |||||
| PopupMenu createExtraAppleMenuItems(); | |||||
| void handleMainMenuCommand (int menuItemID); | |||||
| PopupMenu createExamplesPopupMenu() noexcept; | |||||
| Array<File> getSortedExampleDirectories() noexcept; | Array<File> getSortedExampleDirectories() noexcept; | ||||
| Array<File> getSortedExampleFilesInDirectory (const File&) const noexcept; | Array<File> getSortedExampleFilesInDirectory (const File&) const noexcept; | ||||
| bool findWindowAndOpenPIP (const File&); | bool findWindowAndOpenPIP (const File&); | ||||
| @@ -155,22 +141,74 @@ private: | |||||
| File tryToFindDemoRunnerProject(); | File tryToFindDemoRunnerProject(); | ||||
| void launchDemoRunner(); | void launchDemoRunner(); | ||||
| void showSetJUCEPathAlert(); | |||||
| void setColourScheme (int index, bool saveSetting); | void setColourScheme (int index, bool saveSetting); | ||||
| void setEditorColourScheme (int index, bool saveSetting); | void setEditorColourScheme (int index, bool saveSetting); | ||||
| void updateEditorColourSchemeIfNeeded(); | void updateEditorColourSchemeIfNeeded(); | ||||
| void showUTF8ToolWindow(); | |||||
| void showSVGPathDataToolWindow(); | |||||
| void showAboutWindow(); | |||||
| void showEditorColourSchemeWindow(); | |||||
| void showPIPCreatorWindow(); | |||||
| void launchForumBrowser(); | |||||
| void launchModulesBrowser(); | |||||
| void launchClassesBrowser(); | |||||
| void launchTutorialsBrowser(); | |||||
| void doLoginOrLogout(); | |||||
| void showLoginForm(); | |||||
| void enableOrDisableLiveBuild(); | |||||
| void enableOrDisableGUIEditor(); | |||||
| //============================================================================== | //============================================================================== | ||||
| void* server = nullptr; | |||||
| #if JUCE_MAC | |||||
| class AppleMenuRebuildListener : private MenuBarModel::Listener | |||||
| { | |||||
| public: | |||||
| AppleMenuRebuildListener() | |||||
| { | |||||
| if (auto* model = ProjucerApplication::getApp().getMenuModel()) | |||||
| model->addListener (this); | |||||
| } | |||||
| ~AppleMenuRebuildListener() override | |||||
| { | |||||
| if (auto* model = ProjucerApplication::getApp().getMenuModel()) | |||||
| model->removeListener (this); | |||||
| } | |||||
| private: | |||||
| void menuBarItemsChanged (MenuBarModel*) override {} | |||||
| void menuCommandInvoked (MenuBarModel*, | |||||
| const ApplicationCommandTarget::InvocationInfo& info) override | |||||
| { | |||||
| if (info.commandID == CommandIDs::enableNewVersionCheck) | |||||
| Timer::callAfterDelay (50, [] { ProjucerApplication::getApp().rebuildAppleMenu(); }); | |||||
| } | |||||
| }; | |||||
| void rebuildAppleMenu(); | |||||
| std::unique_ptr<AppleMenuRebuildListener> appleMenuRebuildListener; | |||||
| #endif | |||||
| //============================================================================== | |||||
| std::unique_ptr<LicenseController> licenseController; | |||||
| void* server = nullptr; | |||||
| std::unique_ptr<TooltipWindow> tooltipWindow; | std::unique_ptr<TooltipWindow> tooltipWindow; | ||||
| AvailableModulesList jucePathModulesList, userPathsModulesList; | |||||
| AvailableModuleList jucePathModuleList, userPathsModuleList; | |||||
| std::unique_ptr<Component> utf8Window, svgPathWindow, aboutWindow, pathsWindow, | |||||
| editorColourSchemeWindow, pipCreatorWindow; | |||||
| std::unique_ptr<FileLogger> logger; | |||||
| int numExamples = 0; | int numExamples = 0; | ||||
| std::unique_ptr<AlertWindow> demoRunnerAlert; | std::unique_ptr<AlertWindow> demoRunnerAlert; | ||||
| std::unique_ptr<AlertWindow> pathAlert; | |||||
| bool hasScannedForDemoRunnerExecutable = false, hasScannedForDemoRunnerProject = false; | bool hasScannedForDemoRunnerExecutable = false, hasScannedForDemoRunnerProject = false; | ||||
| File lastJUCEPath, lastDemoRunnerExectuableFile, lastDemoRunnerProjectFile; | File lastJUCEPath, lastDemoRunnerExectuableFile, lastDemoRunnerProjectFile; | ||||
| #if JUCE_LINUX | #if JUCE_LINUX | ||||
| @@ -32,11 +32,11 @@ LatestVersionCheckerAndUpdater::~LatestVersionCheckerAndUpdater() | |||||
| clearSingletonInstance(); | clearSingletonInstance(); | ||||
| } | } | ||||
| void LatestVersionCheckerAndUpdater::checkForNewVersion (bool showAlerts) | |||||
| void LatestVersionCheckerAndUpdater::checkForNewVersion (bool background) | |||||
| { | { | ||||
| if (! isThreadRunning()) | if (! isThreadRunning()) | ||||
| { | { | ||||
| showAlertWindows = showAlerts; | |||||
| backgroundCheck = background; | |||||
| startThread (3); | startThread (3); | ||||
| } | } | ||||
| } | } | ||||
| @@ -48,7 +48,7 @@ void LatestVersionCheckerAndUpdater::run() | |||||
| if (info == nullptr) | if (info == nullptr) | ||||
| { | { | ||||
| if (showAlertWindows) | |||||
| if (! backgroundCheck) | |||||
| AlertWindow::showMessageBoxAsync (AlertWindow::WarningIcon, | AlertWindow::showMessageBoxAsync (AlertWindow::WarningIcon, | ||||
| "Update Server Communication Error", | "Update Server Communication Error", | ||||
| "Failed to communicate with the JUCE update server.\n" | "Failed to communicate with the JUCE update server.\n" | ||||
| @@ -60,7 +60,7 @@ void LatestVersionCheckerAndUpdater::run() | |||||
| if (! info->isNewerVersionThanCurrent()) | if (! info->isNewerVersionThanCurrent()) | ||||
| { | { | ||||
| if (showAlertWindows) | |||||
| if (! backgroundCheck) | |||||
| AlertWindow::showMessageBoxAsync (AlertWindow::InfoIcon, | AlertWindow::showMessageBoxAsync (AlertWindow::InfoIcon, | ||||
| "No New Version Available", | "No New Version Available", | ||||
| "Your JUCE version is up to date."); | "Your JUCE version is up to date."); | ||||
| @@ -99,7 +99,7 @@ void LatestVersionCheckerAndUpdater::run() | |||||
| } | } | ||||
| } | } | ||||
| if (showAlertWindows) | |||||
| if (! backgroundCheck) | |||||
| AlertWindow::showMessageBoxAsync (AlertWindow::WarningIcon, | AlertWindow::showMessageBoxAsync (AlertWindow::WarningIcon, | ||||
| "Failed to find any new downloads", | "Failed to find any new downloads", | ||||
| "Please try again in a few minutes."); | "Please try again in a few minutes."); | ||||
| @@ -137,15 +137,11 @@ public: | |||||
| addAndMakeVisible (cancelButton); | addAndMakeVisible (cancelButton); | ||||
| cancelButton.onClick = [this] | cancelButton.onClick = [this] | ||||
| { | { | ||||
| if (dontAskAgainButton.getToggleState()) | |||||
| getGlobalProperties().setValue (Ids::dontQueryForUpdate.toString(), 1); | |||||
| else | |||||
| getGlobalProperties().removeValue (Ids::dontQueryForUpdate); | |||||
| ProjucerApplication::getApp().setAutomaticVersionCheckingEnabled (! dontAskAgainButton.getToggleState()); | |||||
| exitModalStateWithResult (-1); | exitModalStateWithResult (-1); | ||||
| }; | }; | ||||
| dontAskAgainButton.setToggleState (getGlobalProperties().getValue (Ids::dontQueryForUpdate, {}).isNotEmpty(), dontSendNotification); | |||||
| dontAskAgainButton.setToggleState (! ProjucerApplication::getApp().isAutomaticVersionCheckingEnabled(), dontSendNotification); | |||||
| addAndMakeVisible (dontAskAgainButton); | addAndMakeVisible (dontAskAgainButton); | ||||
| juceIcon = Drawable::createFromImageData (BinaryData::juce_icon_png, | juceIcon = Drawable::createFromImageData (BinaryData::juce_icon_png, | ||||
| @@ -287,10 +283,21 @@ void LatestVersionCheckerAndUpdater::askUserForLocationToDownload (const Version | |||||
| void LatestVersionCheckerAndUpdater::askUserAboutNewVersion (const String& newVersionString, | void LatestVersionCheckerAndUpdater::askUserAboutNewVersion (const String& newVersionString, | ||||
| const String& releaseNotes, | const String& releaseNotes, | ||||
| const VersionInfo::Asset& asset) | const VersionInfo::Asset& asset) | ||||
| { | |||||
| if (backgroundCheck) | |||||
| addNotificationToOpenProjects (asset); | |||||
| else | |||||
| showDialogWindow (newVersionString, releaseNotes, asset); | |||||
| } | |||||
| void LatestVersionCheckerAndUpdater::showDialogWindow (const String& newVersionString, | |||||
| const String& releaseNotes, | |||||
| const VersionInfo::Asset& asset) | |||||
| { | { | ||||
| dialogWindow = UpdateDialog::launchDialog (newVersionString, releaseNotes); | dialogWindow = UpdateDialog::launchDialog (newVersionString, releaseNotes); | ||||
| if (auto* mm = ModalComponentManager::getInstance()) | if (auto* mm = ModalComponentManager::getInstance()) | ||||
| { | |||||
| mm->attachCallback (dialogWindow.get(), | mm->attachCallback (dialogWindow.get(), | ||||
| ModalCallbackFunction::create ([this, asset] (int result) | ModalCallbackFunction::create ([this, asset] (int result) | ||||
| { | { | ||||
| @@ -299,6 +306,35 @@ void LatestVersionCheckerAndUpdater::askUserAboutNewVersion (const String& newVe | |||||
| dialogWindow.reset(); | dialogWindow.reset(); | ||||
| })); | })); | ||||
| } | |||||
| } | |||||
| void LatestVersionCheckerAndUpdater::addNotificationToOpenProjects (const VersionInfo::Asset& asset) | |||||
| { | |||||
| for (auto* window : ProjucerApplication::getApp().mainWindowList.windows) | |||||
| { | |||||
| if (auto* project = window->getProject()) | |||||
| { | |||||
| Component::SafePointer<MainWindow> safeWindow (window); | |||||
| auto ignore = [safeWindow] | |||||
| { | |||||
| if (safeWindow != nullptr) | |||||
| safeWindow->getProject()->removeProjectMessage (ProjectMessages::Ids::newVersionAvailable); | |||||
| }; | |||||
| auto dontAskAgain = [ignore] | |||||
| { | |||||
| ignore(); | |||||
| ProjucerApplication::getApp().setAutomaticVersionCheckingEnabled (false); | |||||
| }; | |||||
| project->addProjectMessage (ProjectMessages::Ids::newVersionAvailable, | |||||
| { { "Download", [this, asset] { askUserForLocationToDownload (asset); } }, | |||||
| { "Ignore", std::move (ignore) }, | |||||
| { "Don't ask again", std::move (dontAskAgain) } }); | |||||
| } | |||||
| } | |||||
| } | } | ||||
| //============================================================================== | //============================================================================== | ||||
| @@ -29,7 +29,7 @@ public: | |||||
| LatestVersionCheckerAndUpdater(); | LatestVersionCheckerAndUpdater(); | ||||
| ~LatestVersionCheckerAndUpdater() override; | ~LatestVersionCheckerAndUpdater() override; | ||||
| void checkForNewVersion (bool showAlerts); | |||||
| void checkForNewVersion (bool isBackgroundCheck); | |||||
| //============================================================================== | //============================================================================== | ||||
| JUCE_DECLARE_SINGLETON_SINGLETHREADED_MINIMAL (LatestVersionCheckerAndUpdater) | JUCE_DECLARE_SINGLETON_SINGLETHREADED_MINIMAL (LatestVersionCheckerAndUpdater) | ||||
| @@ -41,8 +41,11 @@ private: | |||||
| void askUserForLocationToDownload (const VersionInfo::Asset&); | void askUserForLocationToDownload (const VersionInfo::Asset&); | ||||
| void downloadAndInstall (const VersionInfo::Asset&, const File&); | void downloadAndInstall (const VersionInfo::Asset&, const File&); | ||||
| void showDialogWindow (const String&, const String&, const VersionInfo::Asset&); | |||||
| void addNotificationToOpenProjects (const VersionInfo::Asset&); | |||||
| //============================================================================== | //============================================================================== | ||||
| bool showAlertWindows = false; | |||||
| bool backgroundCheck = false; | |||||
| std::unique_ptr<DownloadAndInstallThread> installer; | std::unique_ptr<DownloadAndInstallThread> installer; | ||||
| std::unique_ptr<Component> dialogWindow; | std::unique_ptr<Component> dialogWindow; | ||||
| @@ -48,6 +48,9 @@ namespace CommandIDs | |||||
| showSVGPathTool = 0x300023, | showSVGPathTool = 0x300023, | ||||
| showAboutWindow = 0x300024, | showAboutWindow = 0x300024, | ||||
| checkForNewVersion = 0x300025, | checkForNewVersion = 0x300025, | ||||
| enableNewVersionCheck = 0x300026, | |||||
| enableLiveBuild = 0x300027, | |||||
| enableGUIEditor = 0x300028, | |||||
| showProjectSettings = 0x300030, | showProjectSettings = 0x300030, | ||||
| showProjectTab = 0x300031, | showProjectTab = 0x300031, | ||||
| @@ -91,10 +94,14 @@ namespace CommandIDs | |||||
| nextError = 0x300080, | nextError = 0x300080, | ||||
| prevError = 0x300081, | prevError = 0x300081, | ||||
| showForum = 0x300090, | |||||
| showAPIModules = 0x300091, | |||||
| showAPIClasses = 0x300092, | |||||
| showTutorials = 0x300093, | |||||
| loginLogout = 0x300090, | |||||
| showForum = 0x300100, | |||||
| showAPIModules = 0x300101, | |||||
| showAPIClasses = 0x300102, | |||||
| showTutorials = 0x300103, | |||||
| addNewGUIFile = 0x300200, | |||||
| lastCommandIDEntry | lastCommandIDEntry | ||||
| }; | }; | ||||
| @@ -90,8 +90,8 @@ namespace | |||||
| if (! justSaveResources) | if (! justSaveResources) | ||||
| rescanModulePathsIfNecessary(); | rescanModulePathsIfNecessary(); | ||||
| auto error = justSaveResources ? project->saveResourcesOnly (project->getFile()) | |||||
| : project->saveProject (project->getFile(), true); | |||||
| auto error = justSaveResources ? project->saveResourcesOnly() | |||||
| : project->saveProject(); | |||||
| project.reset(); | project.reset(); | ||||
| @@ -230,7 +230,7 @@ namespace | |||||
| << "Name: " << proj.project->getProjectNameString() << std::endl | << "Name: " << proj.project->getProjectNameString() << std::endl | ||||
| << "UID: " << proj.project->getProjectUIDString() << std::endl; | << "UID: " << proj.project->getProjectUIDString() << std::endl; | ||||
| EnabledModuleList& modules = proj.project->getEnabledModules(); | |||||
| auto& modules = proj.project->getEnabledModules(); | |||||
| if (int numModules = modules.getNumModules()) | if (int numModules = modules.getNumModules()) | ||||
| { | { | ||||
| @@ -307,7 +307,7 @@ namespace | |||||
| var moduleInfo (new DynamicObject()); | var moduleInfo (new DynamicObject()); | ||||
| moduleInfo.getDynamicObject()->setProperty ("file", getModulePackageName (module)); | moduleInfo.getDynamicObject()->setProperty ("file", getModulePackageName (module)); | ||||
| moduleInfo.getDynamicObject()->setProperty ("info", module.moduleInfo.moduleInfo); | |||||
| moduleInfo.getDynamicObject()->setProperty ("info", module.moduleInfo.getModuleInfo()); | |||||
| infoList.append (moduleInfo); | infoList.append (moduleInfo); | ||||
| } | } | ||||
| } | } | ||||
| @@ -22,6 +22,7 @@ | |||||
| #include "../CodeEditor/jucer_OpenDocumentManager.h" | #include "../CodeEditor/jucer_OpenDocumentManager.h" | ||||
| #include "../CodeEditor/jucer_SourceCodeEditor.h" | #include "../CodeEditor/jucer_SourceCodeEditor.h" | ||||
| #include "../Utility/UI/PropertyComponents/jucer_FilePathPropertyComponent.h" | #include "../Utility/UI/PropertyComponents/jucer_FilePathPropertyComponent.h" | ||||
| #include "../Project/UI/jucer_ProjectContentComponent.h" | |||||
| #include "../Project/UI/Sidebar/jucer_TreeItemTypes.h" | #include "../Project/UI/Sidebar/jucer_TreeItemTypes.h" | ||||
| #include "Windows/jucer_UTF8WindowComponent.h" | #include "Windows/jucer_UTF8WindowComponent.h" | ||||
| #include "Windows/jucer_SVGPathDataWindowComponent.h" | #include "Windows/jucer_SVGPathDataWindowComponent.h" | ||||
| @@ -22,6 +22,90 @@ | |||||
| #include "../Wizards/jucer_NewProjectWizardClasses.h" | #include "../Wizards/jucer_NewProjectWizardClasses.h" | ||||
| #include "../Utility/UI/jucer_JucerTreeViewBase.h" | #include "../Utility/UI/jucer_JucerTreeViewBase.h" | ||||
| #include "../ProjectSaving/jucer_ProjectSaver.h" | #include "../ProjectSaving/jucer_ProjectSaver.h" | ||||
| #include "UserAccount/jucer_LoginFormComponent.h" | |||||
| #include "../Project/UI/jucer_ProjectContentComponent.h" | |||||
| //============================================================================== | |||||
| class BlurOverlayWithComponent : public Component, | |||||
| private ComponentMovementWatcher, | |||||
| private AsyncUpdater | |||||
| { | |||||
| public: | |||||
| BlurOverlayWithComponent (MainWindow& window, std::unique_ptr<Component> comp) | |||||
| : ComponentMovementWatcher (&window), | |||||
| mainWindow (window), | |||||
| componentToShow (std::move (comp)) | |||||
| { | |||||
| kernel.createGaussianBlur (1.25f); | |||||
| addAndMakeVisible (*componentToShow); | |||||
| setAlwaysOnTop (true); | |||||
| setOpaque (true); | |||||
| setVisible (true); | |||||
| static_cast<Component&> (mainWindow).addChildComponent (this); | |||||
| componentMovedOrResized (true, true); | |||||
| } | |||||
| void resized() override | |||||
| { | |||||
| setBounds (mainWindow.getLocalBounds()); | |||||
| componentToShow->centreWithSize (componentToShow->getWidth(), componentToShow->getHeight()); | |||||
| refreshBackgroundImage(); | |||||
| } | |||||
| void paint (Graphics& g) override | |||||
| { | |||||
| g.drawImage (componentImage, getLocalBounds().toFloat()); | |||||
| } | |||||
| private: | |||||
| void componentPeerChanged() override {} | |||||
| void componentVisibilityChanged() override {} | |||||
| using ComponentMovementWatcher::componentVisibilityChanged; | |||||
| void componentMovedOrResized (bool, bool) override { triggerAsyncUpdate(); } | |||||
| using ComponentMovementWatcher::componentMovedOrResized; | |||||
| void handleAsyncUpdate() override { resized(); } | |||||
| void mouseUp (const MouseEvent& event) override | |||||
| { | |||||
| if (event.eventComponent == this) | |||||
| mainWindow.hideLoginFormOverlay(); | |||||
| } | |||||
| void lookAndFeelChanged() override | |||||
| { | |||||
| refreshBackgroundImage(); | |||||
| repaint(); | |||||
| } | |||||
| void refreshBackgroundImage() | |||||
| { | |||||
| setVisible (false); | |||||
| auto parentBounds = mainWindow.getBounds(); | |||||
| componentImage = mainWindow.createComponentSnapshot (mainWindow.getLocalBounds()) | |||||
| .rescaled (roundToInt (parentBounds.getWidth() / 1.75f), roundToInt (parentBounds.getHeight() / 1.75f)); | |||||
| kernel.applyToImage (componentImage, componentImage, getLocalBounds()); | |||||
| setVisible (true); | |||||
| } | |||||
| //============================================================================== | |||||
| MainWindow& mainWindow; | |||||
| std::unique_ptr<Component> componentToShow; | |||||
| ImageConvolutionKernel kernel { 3 }; | |||||
| Image componentImage; | |||||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (BlurOverlayWithComponent) | |||||
| }; | |||||
| //============================================================================== | //============================================================================== | ||||
| MainWindow::MainWindow() | MainWindow::MainWindow() | ||||
| @@ -32,6 +116,8 @@ MainWindow::MainWindow() | |||||
| false) | false) | ||||
| { | { | ||||
| setUsingNativeTitleBar (true); | setUsingNativeTitleBar (true); | ||||
| setResizable (true, false); | |||||
| setResizeLimits (600, 500, 32000, 32000); | |||||
| #if ! JUCE_MAC | #if ! JUCE_MAC | ||||
| setMenuBar (ProjucerApplication::getApp().getMenuModel()); | setMenuBar (ProjucerApplication::getApp().getMenuModel()); | ||||
| @@ -39,9 +125,6 @@ MainWindow::MainWindow() | |||||
| createProjectContentCompIfNeeded(); | createProjectContentCompIfNeeded(); | ||||
| setResizable (true, false); | |||||
| centreWithSize (800, 600); | |||||
| auto& commandManager = ProjucerApplication::getCommandManager(); | auto& commandManager = ProjucerApplication::getCommandManager(); | ||||
| auto registerAllAppCommands = [&] | auto registerAllAppCommands = [&] | ||||
| @@ -65,9 +148,10 @@ MainWindow::MainWindow() | |||||
| setWantsKeyboardFocus (false); | setWantsKeyboardFocus (false); | ||||
| getLookAndFeel().setColour (ColourSelector::backgroundColourId, Colours::transparentBlack); | getLookAndFeel().setColour (ColourSelector::backgroundColourId, Colours::transparentBlack); | ||||
| projectNameValue.addListener (this); | projectNameValue.addListener (this); | ||||
| setResizeLimits (600, 500, 32000, 32000); | |||||
| centreWithSize (800, 600); | |||||
| } | } | ||||
| MainWindow::~MainWindow() | MainWindow::~MainWindow() | ||||
| @@ -77,10 +161,11 @@ MainWindow::~MainWindow() | |||||
| #endif | #endif | ||||
| removeKeyListener (ProjucerApplication::getCommandManager().getKeyMappings()); | removeKeyListener (ProjucerApplication::getCommandManager().getKeyMappings()); | ||||
| // save the current size and position to our settings file.. | |||||
| getGlobalProperties().setValue ("lastMainWindowPos", getWindowStateAsString()); | getGlobalProperties().setValue ("lastMainWindowPos", getWindowStateAsString()); | ||||
| clearContentComponent(); | clearContentComponent(); | ||||
| currentProject.reset(); | |||||
| } | } | ||||
| void MainWindow::createProjectContentCompIfNeeded() | void MainWindow::createProjectContentCompIfNeeded() | ||||
| @@ -127,7 +212,7 @@ void MainWindow::closeButtonPressed() | |||||
| ProjucerApplication::getApp().mainWindowList.closeWindow (this); | ProjucerApplication::getApp().mainWindowList.closeWindow (this); | ||||
| } | } | ||||
| bool MainWindow::closeCurrentProject (bool askUserToSave) | |||||
| bool MainWindow::closeCurrentProject (OpenDocumentManager::SaveIfNeeded askUserToSave) | |||||
| { | { | ||||
| if (currentProject == nullptr) | if (currentProject == nullptr) | ||||
| return true; | return true; | ||||
| @@ -144,7 +229,8 @@ bool MainWindow::closeCurrentProject (bool askUserToSave) | |||||
| if (ProjucerApplication::getApp().openDocumentManager | if (ProjucerApplication::getApp().openDocumentManager | ||||
| .closeAllDocumentsUsingProject (*currentProject, askUserToSave)) | .closeAllDocumentsUsingProject (*currentProject, askUserToSave)) | ||||
| { | { | ||||
| if (! askUserToSave || (currentProject->saveIfNeededAndUserAgrees() == FileBasedDocument::savedOk)) | |||||
| if (askUserToSave == OpenDocumentManager::SaveIfNeeded::no | |||||
| || (currentProject->saveIfNeededAndUserAgrees() == FileBasedDocument::savedOk)) | |||||
| { | { | ||||
| setProject (nullptr); | setProject (nullptr); | ||||
| return true; | return true; | ||||
| @@ -154,19 +240,16 @@ bool MainWindow::closeCurrentProject (bool askUserToSave) | |||||
| return false; | return false; | ||||
| } | } | ||||
| void MainWindow::moveProject (File newProjectFileToOpen) | |||||
| void MainWindow::moveProject (File newProjectFileToOpen, OpenInIDE openInIDE) | |||||
| { | { | ||||
| auto openInIDE = currentProject->shouldOpenInIDEAfterSaving(); | |||||
| closeCurrentProject (false); | |||||
| closeCurrentProject (OpenDocumentManager::SaveIfNeeded::no); | |||||
| openFile (newProjectFileToOpen); | openFile (newProjectFileToOpen); | ||||
| if (currentProject != nullptr) | if (currentProject != nullptr) | ||||
| { | |||||
| ProjucerApplication::getApp().getCommandManager().invokeDirectly (openInIDE ? CommandIDs::saveAndOpenInIDE | |||||
| : CommandIDs::saveProject, | |||||
| false); | |||||
| } | |||||
| ProjucerApplication::getApp().getCommandManager() | |||||
| .invokeDirectly (openInIDE == OpenInIDE::yes ? CommandIDs::saveAndOpenInIDE | |||||
| : CommandIDs::saveProject, | |||||
| false); | |||||
| } | } | ||||
| void MainWindow::setProject (std::unique_ptr<Project> newProject) | void MainWindow::setProject (std::unique_ptr<Project> newProject) | ||||
| @@ -174,7 +257,7 @@ void MainWindow::setProject (std::unique_ptr<Project> newProject) | |||||
| if (newProject == nullptr) | if (newProject == nullptr) | ||||
| { | { | ||||
| getProjectContentComponent()->setProject (nullptr); | getProjectContentComponent()->setProject (nullptr); | ||||
| projectNameValue.referTo (Value()); | |||||
| projectNameValue.referTo ({}); | |||||
| currentProject.reset(); | currentProject.reset(); | ||||
| } | } | ||||
| @@ -222,7 +305,7 @@ bool MainWindow::openFile (const File& file) | |||||
| auto newDoc = std::make_unique<Project> (file); | auto newDoc = std::make_unique<Project> (file); | ||||
| auto result = newDoc->loadFrom (file, true); | auto result = newDoc->loadFrom (file, true); | ||||
| if (result.wasOk() && closeCurrentProject (true)) | |||||
| if (result.wasOk() && closeCurrentProject (OpenDocumentManager::SaveIfNeeded::yes)) | |||||
| { | { | ||||
| setProject (std::move (newDoc)); | setProject (std::move (newDoc)); | ||||
| currentProject->setChangedFlag (false); | currentProject->setChangedFlag (false); | ||||
| @@ -350,7 +433,7 @@ void MainWindow::openPIP (PIPGenerator& generator) | |||||
| { | { | ||||
| project->setTemporaryDirectory (generator.getOutputDirectory()); | project->setTemporaryDirectory (generator.getOutputDirectory()); | ||||
| ProjectSaver liveBuildSaver (*project, project->getFile()); | |||||
| ProjectSaver liveBuildSaver (*project); | |||||
| liveBuildSaver.saveContentNeededForLiveBuild(); | liveBuildSaver.saveContentNeededForLiveBuild(); | ||||
| if (auto* pcc = window->getProjectContentComponent()) | if (auto* pcc = window->getProjectContentComponent()) | ||||
| @@ -440,49 +523,6 @@ void MainWindow::activeWindowStatusChanged() | |||||
| pcc->updateMissingFileStatuses(); | pcc->updateMissingFileStatuses(); | ||||
| ProjucerApplication::getApp().openDocumentManager.reloadModifiedFiles(); | ProjucerApplication::getApp().openDocumentManager.reloadModifiedFiles(); | ||||
| if (auto* p = getProject()) | |||||
| { | |||||
| if (p->hasProjectBeenModified()) | |||||
| { | |||||
| Component::SafePointer<Component> safePointer (this); | |||||
| MessageManager::callAsync ([=] () | |||||
| { | |||||
| if (safePointer == nullptr) | |||||
| return; // bail out if the window has been deleted | |||||
| auto result = AlertWindow::showOkCancelBox (AlertWindow::QuestionIcon, | |||||
| TRANS ("The .jucer file has been modified since the last save."), | |||||
| TRANS ("Do you want to keep the current project or re-load from disk?"), | |||||
| TRANS ("Keep"), | |||||
| TRANS ("Re-load from disk")); | |||||
| if (safePointer == nullptr) | |||||
| return; | |||||
| if (result == 0) | |||||
| { | |||||
| if (auto* project = getProject()) | |||||
| { | |||||
| auto oldTemporaryDirectory = project->getTemporaryDirectory(); | |||||
| auto projectFile = project->getFile(); | |||||
| setProject (nullptr); | |||||
| openFile (projectFile); | |||||
| if (oldTemporaryDirectory != File()) | |||||
| if (auto* newProject = getProject()) | |||||
| newProject->setTemporaryDirectory (oldTemporaryDirectory); | |||||
| } | |||||
| } | |||||
| else | |||||
| { | |||||
| ProjucerApplication::getApp().getCommandManager().invokeDirectly (CommandIDs::saveProject, true); | |||||
| } | |||||
| }); | |||||
| } | |||||
| } | |||||
| } | } | ||||
| void MainWindow::showStartPage() | void MainWindow::showStartPage() | ||||
| @@ -498,6 +538,18 @@ void MainWindow::showStartPage() | |||||
| getContentComponent()->grabKeyboardFocus(); | getContentComponent()->grabKeyboardFocus(); | ||||
| } | } | ||||
| void MainWindow::showLoginFormOverlay() | |||||
| { | |||||
| blurOverlayComponent = std::make_unique<BlurOverlayWithComponent> (*this, std::make_unique<LoginFormComponent> (*this)); | |||||
| loginFormOpen = true; | |||||
| } | |||||
| void MainWindow::hideLoginFormOverlay() | |||||
| { | |||||
| blurOverlayComponent.reset(); | |||||
| loginFormOpen = false; | |||||
| } | |||||
| //============================================================================== | //============================================================================== | ||||
| ApplicationCommandTarget* MainWindow::getNextCommandTarget() | ApplicationCommandTarget* MainWindow::getNextCommandTarget() | ||||
| { | { | ||||
| @@ -565,12 +617,11 @@ bool MainWindow::perform (const InvocationInfo& info) | |||||
| return true; | return true; | ||||
| } | } | ||||
| void MainWindow::valueChanged (Value&) | |||||
| void MainWindow::valueChanged (Value& value) | |||||
| { | { | ||||
| if (currentProject != nullptr) | |||||
| setName (currentProject->getProjectNameString() + " - Projucer"); | |||||
| else | |||||
| setName ("Projucer"); | |||||
| if (value == projectNameValue) | |||||
| setName (currentProject != nullptr ? currentProject->getProjectNameString() + " - Projucer" | |||||
| : "Projucer"); | |||||
| } | } | ||||
| //============================================================================== | //============================================================================== | ||||
| @@ -589,7 +640,7 @@ bool MainWindowList::askAllWindowsToClose() | |||||
| while (windows.size() > 0) | while (windows.size() > 0) | ||||
| { | { | ||||
| if (! windows[0]->closeCurrentProject (true)) | |||||
| if (! windows[0]->closeCurrentProject (OpenDocumentManager::SaveIfNeeded::yes)) | |||||
| return false; | return false; | ||||
| windows.remove (0); | windows.remove (0); | ||||
| @@ -616,7 +667,7 @@ void MainWindowList::closeWindow (MainWindow* w) | |||||
| else | else | ||||
| #endif | #endif | ||||
| { | { | ||||
| if (w->closeCurrentProject (true)) | |||||
| if (w->closeCurrentProject (OpenDocumentManager::SaveIfNeeded::yes)) | |||||
| { | { | ||||
| windows.removeObject (w); | windows.removeObject (w); | ||||
| saveCurrentlyOpenProjectList(); | saveCurrentlyOpenProjectList(); | ||||
| @@ -766,6 +817,15 @@ MainWindow* MainWindowList::getMainWindowForFile (const File& file) | |||||
| return nullptr; | return nullptr; | ||||
| } | } | ||||
| MainWindow* MainWindowList::getMainWindowWithLoginFormOpen() | |||||
| { | |||||
| for (auto* window : windows) | |||||
| if (window->isShowingLoginForm()) | |||||
| return window; | |||||
| return nullptr; | |||||
| } | |||||
| void MainWindowList::checkWindowBounds (MainWindow& windowToCheck) | void MainWindowList::checkWindowBounds (MainWindow& windowToCheck) | ||||
| { | { | ||||
| auto avoidSuperimposedWindows = [&] | auto avoidSuperimposedWindows = [&] | ||||
| @@ -18,8 +18,11 @@ | |||||
| #pragma once | #pragma once | ||||
| #include "../Project/UI/jucer_ProjectContentComponent.h" | |||||
| #include "../Utility/PIPs/jucer_PIPGenerator.h" | #include "../Utility/PIPs/jucer_PIPGenerator.h" | ||||
| #include "../Project/jucer_Project.h" | |||||
| #include "../CodeEditor/jucer_OpenDocumentManager.h" | |||||
| class ProjectContentComponent; | |||||
| //============================================================================== | //============================================================================== | ||||
| /** | /** | ||||
| @@ -36,6 +39,8 @@ public: | |||||
| MainWindow(); | MainWindow(); | ||||
| ~MainWindow() override; | ~MainWindow() override; | ||||
| enum class OpenInIDE { no, yes }; | |||||
| //============================================================================== | //============================================================================== | ||||
| void closeButtonPressed() override; | void closeButtonPressed() override; | ||||
| @@ -50,11 +55,15 @@ public: | |||||
| void makeVisible(); | void makeVisible(); | ||||
| void restoreWindowPosition(); | void restoreWindowPosition(); | ||||
| bool closeCurrentProject (bool askToSave); | |||||
| void moveProject (File newProjectFile); | |||||
| bool closeCurrentProject (OpenDocumentManager::SaveIfNeeded askToSave); | |||||
| void moveProject (File newProjectFile, OpenInIDE openInIDE); | |||||
| void showStartPage(); | void showStartPage(); | ||||
| void showLoginFormOverlay(); | |||||
| void hideLoginFormOverlay(); | |||||
| bool isShowingLoginForm() const noexcept { return loginFormOpen; } | |||||
| bool isInterestedInFileDrag (const StringArray& files) override; | bool isInterestedInFileDrag (const StringArray& files) override; | ||||
| void filesDropped (const StringArray& filenames, int mouseX, int mouseY) override; | void filesDropped (const StringArray& filenames, int mouseX, int mouseY) override; | ||||
| @@ -71,16 +80,18 @@ public: | |||||
| bool shouldDropFilesWhenDraggedExternally (const DragAndDropTarget::SourceDetails& sourceDetails, | bool shouldDropFilesWhenDraggedExternally (const DragAndDropTarget::SourceDetails& sourceDetails, | ||||
| StringArray& files, bool& canMoveFiles) override; | StringArray& files, bool& canMoveFiles) override; | ||||
| private: | private: | ||||
| std::unique_ptr<Project> currentProject; | |||||
| Value projectNameValue; | |||||
| void valueChanged (Value&) override; | |||||
| static const char* getProjectWindowPosName() { return "projectWindowPos"; } | static const char* getProjectWindowPosName() { return "projectWindowPos"; } | ||||
| void createProjectContentCompIfNeeded(); | void createProjectContentCompIfNeeded(); | ||||
| void setTitleBarIcon(); | void setTitleBarIcon(); | ||||
| void openPIP (PIPGenerator&); | void openPIP (PIPGenerator&); | ||||
| void valueChanged (Value&) override; | |||||
| std::unique_ptr<Project> currentProject; | |||||
| Value projectNameValue; | |||||
| std::unique_ptr<Component> blurOverlayComponent; | |||||
| bool loginFormOpen = false; | |||||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MainWindow) | JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MainWindow) | ||||
| }; | }; | ||||
| @@ -105,6 +116,7 @@ public: | |||||
| MainWindow* getFrontmostWindow (bool createIfNotFound = true); | MainWindow* getFrontmostWindow (bool createIfNotFound = true); | ||||
| MainWindow* getOrCreateEmptyWindow(); | MainWindow* getOrCreateEmptyWindow(); | ||||
| MainWindow* getMainWindowForFile (const File&); | MainWindow* getMainWindowForFile (const File&); | ||||
| MainWindow* getMainWindowWithLoginFormOpen(); | |||||
| Project* getFrontmostProject(); | Project* getFrontmostProject(); | ||||
| @@ -1,50 +0,0 @@ | |||||
| <?xml version="1.0" encoding="utf-8"?> | |||||
| <!-- Generator: Adobe Illustrator 21.0.2, SVG Export Plug-In . SVG Version: 6.00 Build 0) --> | |||||
| <svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" | |||||
| viewBox="0 0 169.7 205.2" style="enable-background:new 0 0 169.7 205.2;" xml:space="preserve"> | |||||
| <style type="text/css"> | |||||
| .st0{fill:#A65A95;} | |||||
| .st1{fill:#001946;} | |||||
| </style> | |||||
| <g> | |||||
| <g> | |||||
| <path class="st0" d="M45.2,167.8c-3,0-5.4-1.1-7.8-4.3l3.6-3.1c1.6,2.1,2.7,2.7,4.2,2.7c2.6,0,4.4-2,4.4-5v-17.2h5V158 | |||||
| C54.6,163.9,50.7,167.8,45.2,167.8z"/> | |||||
| <path class="st0" d="M70.7,167.8c-5.8,0-10.8-4.2-10.8-11.2v-15.8h5v15.6c0,4.1,2.4,6.7,5.8,6.7c3.4,0,5.8-2.6,5.8-6.7v-15.6h5 | |||||
| v15.8C81.5,163.5,76.5,167.8,70.7,167.8z"/> | |||||
| <path class="st0" d="M99,167.8c-7.6,0-13.9-6.1-13.9-13.6c0-7.6,6.3-13.6,13.9-13.6c3.4,0,6.3,1.2,9.1,3.4l-2.9,3.6 | |||||
| c-2.6-1.8-4.1-2.4-6.2-2.4c-4.8,0-8.8,3.9-8.8,9c0,5,3.9,9,8.8,9c2,0,3.6-0.6,6.1-2.4l3,3.7C104.6,167,102,167.8,99,167.8z"/> | |||||
| <path class="st0" d="M111.3,167.4v-26.6h16.6v4.5h-11.6v6.4h11.2v4.5h-11.2v6.7h11.6v4.5H111.3z"/> | |||||
| </g> | |||||
| <g> | |||||
| <circle class="st1" cx="84.9" cy="74.9" r="37.4"/> | |||||
| <circle class="st0" cx="84.9" cy="74.9" r="28"/> | |||||
| <circle class="st1" cx="84.9" cy="67.9" r="2.1"/> | |||||
| <circle class="st1" cx="91.4" cy="72.6" r="2.1"/> | |||||
| <circle class="st1" cx="88.9" cy="80.3" r="2.1"/> | |||||
| <circle class="st1" cx="80.8" cy="80.3" r="2.1"/> | |||||
| <circle class="st1" cx="78.3" cy="72.6" r="2.1"/> | |||||
| </g> | |||||
| <g> | |||||
| <path class="st1" d="M48.2,131.7v-4.6h-4.3v4.6h-2V121h2v4.3h4.3V121h2v10.8H48.2z"/> | |||||
| <path class="st1" d="M56.7,131.7v-0.5c-0.5,0.4-1.3,0.7-2.1,0.7c-1.9,0-3.2-1.3-3.2-3.4v-5h2v4.8c0,1.2,0.7,1.8,1.7,1.8 | |||||
| c1,0,1.7-0.7,1.7-1.8v-4.8h2v8.3H56.7z"/> | |||||
| <path class="st1" d="M63.8,131.9c-2.4,0-4.3-1.9-4.3-4.3c0-2.4,1.9-4.3,4.3-4.3c1.1,0,2.1,0.4,3.3,1.5l-1.3,1.2 | |||||
| c-0.7-0.7-1.3-1-2-1c-1.3,0-2.3,1.1-2.3,2.5c0,1.3,1,2.5,2.3,2.5c0.6,0,1.3-0.2,2-0.9l1.3,1.2C66,131.5,65,131.9,63.8,131.9z"/> | |||||
| <path class="st1" d="M73.8,131.7l-3.9-3.9v3.9h-2v-11.3h2v6.8l3.6-3.7h2.5l-3.9,4l4.3,4.3H73.8z"/> | |||||
| <path class="st1" d="M77.1,131.7v-11.3h2v11.3H77.1z"/> | |||||
| <path class="st1" d="M88.6,128.4h-6.2c0.1,1,1.1,1.7,2.1,1.7c0.7,0,1.5-0.3,2.3-1.1l1.3,1.2c-1.1,1.2-2.3,1.7-3.6,1.7 | |||||
| c-2.4,0-4.3-1.9-4.3-4.3c0-2.4,1.9-4.3,4.3-4.3c2.4,0,4.1,1.9,4.1,4.2C88.7,127.9,88.6,128.4,88.6,128.4z M84.5,125.1 | |||||
| c-1,0-1.9,0.6-2.1,1.5h4.1C86.3,125.8,85.5,125.1,84.5,125.1z"/> | |||||
| <path class="st1" d="M93.9,131.9c-0.9,0-1.7-0.3-2.3-0.8v0.6h-2v-11.3h2v3.7c0.6-0.5,1.4-0.8,2.3-0.8c2.3,0,4.2,1.9,4.2,4.3 | |||||
| C98.1,129.9,96.2,131.9,93.9,131.9z M93.9,125.1c-1.3,0-2.2,1.1-2.2,2.5c0,1.4,1,2.5,2.2,2.5c1.3,0,2.2-1.1,2.2-2.5 | |||||
| C96.2,126.2,95.2,125.1,93.9,125.1z"/> | |||||
| <path class="st1" d="M107.4,128.4h-6.2c0.1,1,1.1,1.7,2.1,1.7c0.7,0,1.5-0.3,2.3-1.1l1.3,1.2c-1.1,1.2-2.3,1.7-3.6,1.7 | |||||
| c-2.4,0-4.3-1.9-4.3-4.3c0-2.4,1.9-4.3,4.3-4.3c2.4,0,4.1,1.9,4.1,4.2C107.5,127.9,107.4,128.4,107.4,128.4z M103.3,125.1 | |||||
| c-1,0-1.9,0.6-2.1,1.5h4.1C105.1,125.8,104.3,125.1,103.3,125.1z"/> | |||||
| <path class="st1" d="M110.4,127.8v3.9h-2v-8.3h2v0.8c0.7-0.6,1.4-0.9,2.6-1v1.8C111,125.4,110.4,126.6,110.4,127.8z"/> | |||||
| <path class="st1" d="M116,127.8v3.9h-2v-8.3h2v0.8c0.7-0.6,1.4-0.9,2.6-1v1.8C116.6,125.4,116,126.6,116,127.8z"/> | |||||
| <path class="st1" d="M122.2,134.6h-2.1l2.2-4.2l-3.4-6.9h2.1l2.4,4.8l2.3-4.8h2.2L122.2,134.6z"/> | |||||
| </g> | |||||
| </g> | |||||
| </svg> | |||||
| @@ -19,7 +19,7 @@ | |||||
| #include "../Application/jucer_Headers.h" | #include "../Application/jucer_Headers.h" | ||||
| #include "jucer_DocumentEditorComponent.h" | #include "jucer_DocumentEditorComponent.h" | ||||
| #include "../Application/jucer_Application.h" | #include "../Application/jucer_Application.h" | ||||
| #include "../Project/UI/jucer_ProjectContentComponent.h" | |||||
| //============================================================================== | //============================================================================== | ||||
| DocumentEditorComponent::DocumentEditorComponent (OpenDocumentManager::Document* doc) | DocumentEditorComponent::DocumentEditorComponent (OpenDocumentManager::Document* doc) | ||||
| @@ -181,11 +181,11 @@ FileBasedDocument::SaveResult OpenDocumentManager::saveIfNeededAndUserAgrees (Op | |||||
| } | } | ||||
| bool OpenDocumentManager::closeDocument (int index, bool saveIfNeeded) | |||||
| bool OpenDocumentManager::closeDocument (int index, SaveIfNeeded saveIfNeeded) | |||||
| { | { | ||||
| if (Document* doc = documents [index]) | if (Document* doc = documents [index]) | ||||
| { | { | ||||
| if (saveIfNeeded) | |||||
| if (saveIfNeeded == SaveIfNeeded::yes) | |||||
| if (saveIfNeededAndUserAgrees (doc) != FileBasedDocument::savedOk) | if (saveIfNeededAndUserAgrees (doc) != FileBasedDocument::savedOk) | ||||
| return false; | return false; | ||||
| @@ -206,12 +206,12 @@ bool OpenDocumentManager::closeDocument (int index, bool saveIfNeeded) | |||||
| return true; | return true; | ||||
| } | } | ||||
| bool OpenDocumentManager::closeDocument (Document* document, bool saveIfNeeded) | |||||
| bool OpenDocumentManager::closeDocument (Document* document, SaveIfNeeded saveIfNeeded) | |||||
| { | { | ||||
| return closeDocument (documents.indexOf (document), saveIfNeeded); | return closeDocument (documents.indexOf (document), saveIfNeeded); | ||||
| } | } | ||||
| void OpenDocumentManager::closeFile (const File& f, bool saveIfNeeded) | |||||
| void OpenDocumentManager::closeFile (const File& f, SaveIfNeeded saveIfNeeded) | |||||
| { | { | ||||
| for (int i = documents.size(); --i >= 0;) | for (int i = documents.size(); --i >= 0;) | ||||
| if (Document* d = documents[i]) | if (Document* d = documents[i]) | ||||
| @@ -219,7 +219,7 @@ void OpenDocumentManager::closeFile (const File& f, bool saveIfNeeded) | |||||
| closeDocument (i, saveIfNeeded); | closeDocument (i, saveIfNeeded); | ||||
| } | } | ||||
| bool OpenDocumentManager::closeAll (bool askUserToSave) | |||||
| bool OpenDocumentManager::closeAll (SaveIfNeeded askUserToSave) | |||||
| { | { | ||||
| for (int i = getNumOpenDocuments(); --i >= 0;) | for (int i = getNumOpenDocuments(); --i >= 0;) | ||||
| if (! closeDocument (i, askUserToSave)) | if (! closeDocument (i, askUserToSave)) | ||||
| @@ -228,7 +228,7 @@ bool OpenDocumentManager::closeAll (bool askUserToSave) | |||||
| return true; | return true; | ||||
| } | } | ||||
| bool OpenDocumentManager::closeAllDocumentsUsingProject (Project& project, bool saveIfNeeded) | |||||
| bool OpenDocumentManager::closeAllDocumentsUsingProject (Project& project, SaveIfNeeded saveIfNeeded) | |||||
| { | { | ||||
| for (int i = documents.size(); --i >= 0;) | for (int i = documents.size(); --i >= 0;) | ||||
| if (Document* d = documents[i]) | if (Document* d = documents[i]) | ||||
| @@ -61,13 +61,15 @@ public: | |||||
| Document* getOpenDocument (int index) const; | Document* getOpenDocument (int index) const; | ||||
| void clear(); | void clear(); | ||||
| enum class SaveIfNeeded { no, yes }; | |||||
| bool canOpenFile (const File& file); | bool canOpenFile (const File& file); | ||||
| Document* openFile (Project* project, const File& file); | Document* openFile (Project* project, const File& file); | ||||
| bool closeDocument (int index, bool saveIfNeeded); | |||||
| bool closeDocument (Document* document, bool saveIfNeeded); | |||||
| bool closeAll (bool askUserToSave); | |||||
| bool closeAllDocumentsUsingProject (Project& project, bool saveIfNeeded); | |||||
| void closeFile (const File& f, bool saveIfNeeded); | |||||
| bool closeDocument (int index, SaveIfNeeded saveIfNeeded); | |||||
| bool closeDocument (Document* document, SaveIfNeeded saveIfNeeded); | |||||
| bool closeAll (SaveIfNeeded askUserToSave); | |||||
| bool closeAllDocumentsUsingProject (Project& project, SaveIfNeeded saveIfNeeded); | |||||
| void closeFile (const File& f, SaveIfNeeded saveIfNeeded); | |||||
| bool anyFilesNeedSaving() const; | bool anyFilesNeedSaving() const; | ||||
| bool saveAll(); | bool saveAll(); | ||||
| FileBasedDocument::SaveResult saveIfNeededAndUserAgrees (Document* doc); | FileBasedDocument::SaveResult saveIfNeededAndUserAgrees (Document* doc); | ||||
| @@ -1210,11 +1210,14 @@ Image JucerDocumentEditor::createComponentLayerSnapshot() const | |||||
| const int gridSnapMenuItemBase = 0x8723620; | const int gridSnapMenuItemBase = 0x8723620; | ||||
| const int snapSizes[] = { 2, 3, 4, 5, 6, 8, 10, 12, 16, 20, 24, 32 }; | const int snapSizes[] = { 2, 3, 4, 5, 6, 8, 10, 12, 16, 20, 24, 32 }; | ||||
| void createGUIEditorMenu (PopupMenu&); | |||||
| void createGUIEditorMenu (PopupMenu& menu) | |||||
| PopupMenu createGUIEditorMenu() | |||||
| { | { | ||||
| PopupMenu menu; | |||||
| auto* commandManager = &ProjucerApplication::getCommandManager(); | auto* commandManager = &ProjucerApplication::getCommandManager(); | ||||
| menu.addCommandItem (commandManager, CommandIDs::addNewGUIFile); | |||||
| menu.addSeparator(); | |||||
| menu.addCommandItem (commandManager, JucerCommandIDs::editCompLayout); | menu.addCommandItem (commandManager, JucerCommandIDs::editCompLayout); | ||||
| menu.addCommandItem (commandManager, JucerCommandIDs::editCompGraphics); | menu.addCommandItem (commandManager, JucerCommandIDs::editCompGraphics); | ||||
| menu.addSeparator(); | menu.addSeparator(); | ||||
| @@ -1283,6 +1286,7 @@ void createGUIEditorMenu (PopupMenu& menu) | |||||
| menu.addSubMenu ("Component Overlay", overlays, holder != nullptr); | menu.addSubMenu ("Component Overlay", overlays, holder != nullptr); | ||||
| } | } | ||||
| return menu; | |||||
| } | } | ||||
| void handleGUIEditorMenuCommand (int); | void handleGUIEditorMenuCommand (int); | ||||
| @@ -697,7 +697,7 @@ public: | |||||
| { | { | ||||
| if (header->save()) | if (header->save()) | ||||
| { | { | ||||
| odm.closeFile (getFile().withFileExtension(".h"), false); | |||||
| odm.closeFile (getFile().withFileExtension(".h"), OpenDocumentManager::SaveIfNeeded::no); | |||||
| return true; | return true; | ||||
| } | } | ||||
| } | } | ||||
| @@ -707,10 +707,13 @@ public: | |||||
| Component* createEditor() override | Component* createEditor() override | ||||
| { | { | ||||
| std::unique_ptr<JucerDocument> jucerDoc (JucerDocument::createForCppFile (getProject(), getFile())); | |||||
| if (ProjucerApplication::getApp().isGUIEditorEnabled()) | |||||
| { | |||||
| std::unique_ptr<JucerDocument> jucerDoc (JucerDocument::createForCppFile (getProject(), getFile())); | |||||
| if (jucerDoc != nullptr) | |||||
| return new JucerDocumentEditor (jucerDoc.release()); | |||||
| if (jucerDoc != nullptr) | |||||
| return new JucerDocumentEditor (jucerDoc.release()); | |||||
| } | |||||
| return SourceCodeDocument::createEditor(); | return SourceCodeDocument::createEditor(); | ||||
| } | } | ||||
| @@ -766,8 +769,8 @@ struct NewGUIComponentWizard : public NewFileWizard::Type | |||||
| cpp->save(); | cpp->save(); | ||||
| header->save(); | header->save(); | ||||
| odm.closeDocument (cpp, true); | |||||
| odm.closeDocument (header, true); | |||||
| odm.closeDocument (cpp, OpenDocumentManager::SaveIfNeeded::yes); | |||||
| odm.closeDocument (header, OpenDocumentManager::SaveIfNeeded::yes); | |||||
| parent.addFileRetainingSortOrder (headerFile, true); | parent.addFileRetainingSortOrder (headerFile, true); | ||||
| parent.addFileRetainingSortOrder (cppFile, true); | parent.addFileRetainingSortOrder (cppFile, true); | ||||
| @@ -81,13 +81,10 @@ namespace MessageTypes | |||||
| { | { | ||||
| inline bool send (MessageHandler& target, const ValueTree& v) | inline bool send (MessageHandler& target, const ValueTree& v) | ||||
| { | { | ||||
| //DBG ("Send: " << v.getType().toString()); | |||||
| bool result = target.sendMessage (v); | bool result = target.sendMessage (v); | ||||
| if (! result) | if (! result) | ||||
| { | |||||
| DBG ("*** Message failed: " << v.getType().toString()); | |||||
| } | |||||
| Logger::outputDebugString ("*** Message failed: " + v.getType().toString()); | |||||
| return result; | return result; | ||||
| } | } | ||||
| @@ -29,6 +29,7 @@ | |||||
| #include "jucer_CompileEngineClient.h" | #include "jucer_CompileEngineClient.h" | ||||
| #include "jucer_CompileEngineServer.h" | #include "jucer_CompileEngineServer.h" | ||||
| #include "jucer_CompileEngineSettings.h" | #include "jucer_CompileEngineSettings.h" | ||||
| #include "../Project/UI/jucer_ProjectContentComponent.h" | |||||
| #ifndef RUN_CLANG_IN_CHILD_PROCESS | #ifndef RUN_CLANG_IN_CHILD_PROCESS | ||||
| #error | #error | ||||
| @@ -208,8 +209,6 @@ public: | |||||
| if (isRunningApp && server != nullptr) | if (isRunningApp && server != nullptr) | ||||
| server->killServerWithoutMercy(); | server->killServerWithoutMercy(); | ||||
| server.reset(); | |||||
| } | } | ||||
| void restartServer() | void restartServer() | ||||
| @@ -511,9 +510,6 @@ CompileEngineChildProcess::CompileEngineChildProcess (Project& p) | |||||
| CompileEngineChildProcess::~CompileEngineChildProcess() | CompileEngineChildProcess::~CompileEngineChildProcess() | ||||
| { | { | ||||
| ProjucerApplication::getApp().openDocumentManager.removeListener (this); | ProjucerApplication::getApp().openDocumentManager.removeListener (this); | ||||
| process.reset(); | |||||
| lastComponentList.clear(); | |||||
| } | } | ||||
| void CompileEngineChildProcess::createProcess() | void CompileEngineChildProcess::createProcess() | ||||
| @@ -18,10 +18,11 @@ | |||||
| #pragma once | #pragma once | ||||
| #define DECLARE_ID(name) static const Identifier name (#name) | |||||
| namespace MessageTypes | namespace MessageTypes | ||||
| { | { | ||||
| #define DECLARE_ID(name) const Identifier name (#name) | |||||
| DECLARE_ID (PING); | DECLARE_ID (PING); | ||||
| DECLARE_ID (BUILDINFO); | DECLARE_ID (BUILDINFO); | ||||
| DECLARE_ID (COMPILEUNIT); | DECLARE_ID (COMPILEUNIT); | ||||
| @@ -50,6 +51,6 @@ namespace MessageTypes | |||||
| DECLARE_ID (LAUNCH_APP); | DECLARE_ID (LAUNCH_APP); | ||||
| DECLARE_ID (FOREGROUND); | DECLARE_ID (FOREGROUND); | ||||
| DECLARE_ID (QUIT_SERVER); | DECLARE_ID (QUIT_SERVER); | ||||
| } | |||||
| #undef DECLARE_ID | |||||
| #undef DECLARE_ID | |||||
| } | |||||
| @@ -0,0 +1,195 @@ | |||||
| /* | |||||
| ============================================================================== | |||||
| This file is part of the JUCE 6 technical preview. | |||||
| Copyright (c) 2017 - ROLI Ltd. | |||||
| You may use this code under the terms of the GPL v3 | |||||
| (see www.gnu.org/licenses). | |||||
| For this technical preview, this file is not subject to commercial licensing. | |||||
| 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 "jucer_ModuleDescription.h" | |||||
| //============================================================================== | |||||
| class AvailableModulesList | |||||
| { | |||||
| public: | |||||
| using ModuleIDAndFolder = std::pair<String, File>; | |||||
| using ModuleIDAndFolderList = std::vector<ModuleIDAndFolder>; | |||||
| AvailableModulesList() = default; | |||||
| //============================================================================== | |||||
| void scanPaths (const Array<File>& paths) | |||||
| { | |||||
| auto job = createScannerJob (paths); | |||||
| auto& ref = *job; | |||||
| removePendingAndAddJob (std::move (job)); | |||||
| scanPool.waitForJobToFinish (&ref, -1); | |||||
| } | |||||
| void scanPathsAsync (const Array<File>& paths) | |||||
| { | |||||
| removePendingAndAddJob (createScannerJob (paths)); | |||||
| } | |||||
| //============================================================================== | |||||
| ModuleIDAndFolderList getAllModules() const | |||||
| { | |||||
| const ScopedLock readLock (lock); | |||||
| return modulesList; | |||||
| } | |||||
| ModuleIDAndFolder getModuleWithID (const String& id) const | |||||
| { | |||||
| const ScopedLock readLock (lock); | |||||
| for (auto& mod : modulesList) | |||||
| if (mod.first == id) | |||||
| return mod; | |||||
| return {}; | |||||
| } | |||||
| //============================================================================== | |||||
| void removeDuplicates (const ModuleIDAndFolderList& other) | |||||
| { | |||||
| const ScopedLock readLock (lock); | |||||
| const auto predicate = [&] (const ModuleIDAndFolder& entry) | |||||
| { | |||||
| return std::find (other.begin(), other.end(), entry) != other.end(); | |||||
| }; | |||||
| modulesList.erase (std::remove_if (modulesList.begin(), modulesList.end(), predicate), | |||||
| modulesList.end()); | |||||
| } | |||||
| //============================================================================== | |||||
| struct Listener | |||||
| { | |||||
| virtual ~Listener() = default; | |||||
| virtual void availableModulesChanged (AvailableModulesList* listThatHasChanged) = 0; | |||||
| }; | |||||
| void addListener (Listener* listenerToAdd) { listeners.add (listenerToAdd); } | |||||
| void removeListener (Listener* listenerToRemove) { listeners.remove (listenerToRemove); } | |||||
| private: | |||||
| //============================================================================== | |||||
| struct ModuleScannerJob : public ThreadPoolJob | |||||
| { | |||||
| ModuleScannerJob (const Array<File>& paths, | |||||
| std::function<void (const ModuleIDAndFolderList&)>&& callback) | |||||
| : ThreadPoolJob ("ModuleScannerJob"), | |||||
| pathsToScan (paths), | |||||
| completionCallback (std::move (callback)) | |||||
| { | |||||
| } | |||||
| JobStatus runJob() override | |||||
| { | |||||
| ModuleIDAndFolderList list; | |||||
| for (auto& p : pathsToScan) | |||||
| addAllModulesInFolder (p, list); | |||||
| if (! shouldExit()) | |||||
| { | |||||
| std::sort (list.begin(), list.end(), [] (const ModuleIDAndFolder& m1, | |||||
| const ModuleIDAndFolder& m2) | |||||
| { | |||||
| return m1.first.compareIgnoreCase (m2.first) < 0; | |||||
| }); | |||||
| completionCallback (list); | |||||
| } | |||||
| return jobHasFinished; | |||||
| } | |||||
| static bool tryToAddModuleFromFolder (const File& path, ModuleIDAndFolderList& list) | |||||
| { | |||||
| ModuleDescription m (path); | |||||
| if (m.isValid()) | |||||
| { | |||||
| list.push_back ({ m.getID(), path }); | |||||
| return true; | |||||
| } | |||||
| return false; | |||||
| } | |||||
| static void addAllModulesInSubfoldersRecursively (const File& path, int depth, ModuleIDAndFolderList& list) | |||||
| { | |||||
| if (depth > 0) | |||||
| { | |||||
| for (const auto& iter : RangedDirectoryIterator (path, false, "*", File::findDirectories)) | |||||
| { | |||||
| if (auto* job = ThreadPoolJob::getCurrentThreadPoolJob()) | |||||
| if (job->shouldExit()) | |||||
| return; | |||||
| auto childPath = iter.getFile(); | |||||
| if (! tryToAddModuleFromFolder (childPath, list)) | |||||
| addAllModulesInSubfoldersRecursively (childPath, depth - 1, list); | |||||
| } | |||||
| } | |||||
| } | |||||
| static void addAllModulesInFolder (const File& path, ModuleIDAndFolderList& list) | |||||
| { | |||||
| if (! tryToAddModuleFromFolder (path, list)) | |||||
| { | |||||
| constexpr int subfolders = 3; | |||||
| addAllModulesInSubfoldersRecursively (path, subfolders, list); | |||||
| } | |||||
| } | |||||
| Array<File> pathsToScan; | |||||
| std::function<void (const ModuleIDAndFolderList&)> completionCallback; | |||||
| }; | |||||
| //============================================================================== | |||||
| std::unique_ptr<ThreadPoolJob> createScannerJob (const Array<File>& paths) | |||||
| { | |||||
| return std::make_unique<ModuleScannerJob> (paths, [this] (ModuleIDAndFolderList scannedModulesList) | |||||
| { | |||||
| { | |||||
| const ScopedLock swapLock (lock); | |||||
| modulesList.swap (scannedModulesList); | |||||
| } | |||||
| listeners.call ([this] (Listener& l) { MessageManager::callAsync ([&] { l.availableModulesChanged (this); }); }); | |||||
| }); | |||||
| } | |||||
| void removePendingAndAddJob (std::unique_ptr<ThreadPoolJob> jobToAdd) | |||||
| { | |||||
| scanPool.removeAllJobs (false, 100); | |||||
| scanPool.addJob (jobToAdd.release(), true); | |||||
| } | |||||
| //============================================================================== | |||||
| ThreadPool scanPool { 1 }; | |||||
| ModuleIDAndFolderList modulesList; | |||||
| ListenerList<Listener> listeners; | |||||
| CriticalSection lock; | |||||
| //============================================================================== | |||||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AvailableModulesList) | |||||
| }; | |||||
| @@ -0,0 +1,86 @@ | |||||
| /* | |||||
| ============================================================================== | |||||
| This file is part of the JUCE 6 technical preview. | |||||
| Copyright (c) 2017 - ROLI Ltd. | |||||
| You may use this code under the terms of the GPL v3 | |||||
| (see www.gnu.org/licenses). | |||||
| For this technical preview, this file is not subject to commercial licensing. | |||||
| 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 ModuleDescription | |||||
| { | |||||
| public: | |||||
| ModuleDescription() = default; | |||||
| ModuleDescription (const File& folder) | |||||
| : moduleFolder (folder), | |||||
| moduleInfo (parseJUCEHeaderMetadata (getHeader())) | |||||
| { | |||||
| } | |||||
| bool isValid() const { return getID().isNotEmpty(); } | |||||
| String getID() const { return moduleInfo [Ids::ID_uppercase].toString(); } | |||||
| String getVendor() const { return moduleInfo [Ids::vendor].toString(); } | |||||
| String getVersion() const { return moduleInfo [Ids::version].toString(); } | |||||
| String getName() const { return moduleInfo [Ids::name].toString(); } | |||||
| String getDescription() const { return moduleInfo [Ids::description].toString(); } | |||||
| String getLicense() const { return moduleInfo [Ids::license].toString(); } | |||||
| String getMinimumCppStandard() const { return moduleInfo [Ids::minimumCppStandard].toString(); } | |||||
| String getPreprocessorDefs() const { return moduleInfo [Ids::defines].toString(); } | |||||
| String getExtraSearchPaths() const { return moduleInfo [Ids::searchpaths].toString(); } | |||||
| var getModuleInfo() const { return moduleInfo; } | |||||
| File getModuleFolder() const { return moduleFolder; } | |||||
| File getFolder() const | |||||
| { | |||||
| jassert (moduleFolder != File()); | |||||
| return moduleFolder; | |||||
| } | |||||
| File getHeader() const | |||||
| { | |||||
| if (moduleFolder != File()) | |||||
| { | |||||
| static const char* extensions[] = { ".h", ".hpp", ".hxx" }; | |||||
| for (auto e : extensions) | |||||
| { | |||||
| auto header = moduleFolder.getChildFile (moduleFolder.getFileName() + e); | |||||
| if (header.existsAsFile()) | |||||
| return header; | |||||
| } | |||||
| } | |||||
| return {}; | |||||
| } | |||||
| StringArray getDependencies() const | |||||
| { | |||||
| auto moduleDependencies = StringArray::fromTokens (moduleInfo ["dependencies"].toString(), " \t;,", "\"'"); | |||||
| moduleDependencies.trim(); | |||||
| moduleDependencies.removeEmptyStrings(); | |||||
| return moduleDependencies; | |||||
| } | |||||
| private: | |||||
| File moduleFolder; | |||||
| var moduleInfo; | |||||
| URL url; | |||||
| }; | |||||
| @@ -16,182 +16,10 @@ | |||||
| ============================================================================== | ============================================================================== | ||||
| */ | */ | ||||
| #include "../Application/jucer_Headers.h" | |||||
| #include "../ProjectSaving/jucer_ProjectSaver.h" | |||||
| #include "../ProjectSaving/jucer_ProjectExport_Xcode.h" | |||||
| #include "../Application/jucer_Application.h" | |||||
| //============================================================================== | |||||
| ModuleDescription::ModuleDescription (const File& folder) | |||||
| : moduleFolder (folder), | |||||
| moduleInfo (parseJUCEHeaderMetadata (getHeader())) | |||||
| { | |||||
| } | |||||
| File ModuleDescription::getHeader() const | |||||
| { | |||||
| if (moduleFolder != File()) | |||||
| { | |||||
| static const char* extensions[] = { ".h", ".hpp", ".hxx" }; | |||||
| for (auto e : extensions) | |||||
| { | |||||
| auto header = moduleFolder.getChildFile (moduleFolder.getFileName() + e); | |||||
| if (header.existsAsFile()) | |||||
| return header; | |||||
| } | |||||
| } | |||||
| return {}; | |||||
| } | |||||
| StringArray ModuleDescription::getDependencies() const | |||||
| { | |||||
| auto moduleDependencies = StringArray::fromTokens (moduleInfo ["dependencies"].toString(), " \t;,", "\"'"); | |||||
| moduleDependencies.trim(); | |||||
| moduleDependencies.removeEmptyStrings(); | |||||
| return moduleDependencies; | |||||
| } | |||||
| //============================================================================== | |||||
| static bool tryToAddModuleFromFolder (const File& path, AvailableModuleList::ModuleIDAndFolderList& list) | |||||
| { | |||||
| ModuleDescription m (path); | |||||
| if (m.isValid()) | |||||
| { | |||||
| list.push_back ({ m.getID(), path }); | |||||
| return true; | |||||
| } | |||||
| return false; | |||||
| } | |||||
| static void addAllModulesInSubfoldersRecursively (const File& path, int depth, AvailableModuleList::ModuleIDAndFolderList& list) | |||||
| { | |||||
| if (depth > 0) | |||||
| { | |||||
| for (const auto& iter : RangedDirectoryIterator (path, false, "*", File::findDirectories)) | |||||
| { | |||||
| if (auto* job = ThreadPoolJob::getCurrentThreadPoolJob()) | |||||
| if (job->shouldExit()) | |||||
| return; | |||||
| auto childPath = iter.getFile(); | |||||
| if (! tryToAddModuleFromFolder (childPath, list)) | |||||
| addAllModulesInSubfoldersRecursively (childPath, depth - 1, list); | |||||
| } | |||||
| } | |||||
| } | |||||
| static void addAllModulesInFolder (const File& path, AvailableModuleList::ModuleIDAndFolderList& list) | |||||
| { | |||||
| if (! tryToAddModuleFromFolder (path, list)) | |||||
| { | |||||
| static constexpr int subfolders = 3; | |||||
| addAllModulesInSubfoldersRecursively (path, subfolders, list); | |||||
| } | |||||
| } | |||||
| struct ModuleScannerJob : public ThreadPoolJob | |||||
| { | |||||
| ModuleScannerJob (const Array<File>& paths, | |||||
| std::function<void (const AvailableModuleList::ModuleIDAndFolderList&)>&& callback) | |||||
| : ThreadPoolJob ("ModuleScannerJob"), | |||||
| pathsToScan (paths), | |||||
| completionCallback (std::move (callback)) | |||||
| { | |||||
| } | |||||
| JobStatus runJob() override | |||||
| { | |||||
| AvailableModuleList::ModuleIDAndFolderList list; | |||||
| for (auto& p : pathsToScan) | |||||
| addAllModulesInFolder (p, list); | |||||
| if (! shouldExit()) | |||||
| { | |||||
| std::sort (list.begin(), list.end(), [] (const AvailableModuleList::ModuleIDAndFolder& m1, | |||||
| const AvailableModuleList::ModuleIDAndFolder& m2) | |||||
| { | |||||
| return m1.first.compareIgnoreCase (m2.first) < 0; | |||||
| }); | |||||
| completionCallback (list); | |||||
| } | |||||
| return jobHasFinished; | |||||
| } | |||||
| Array<File> pathsToScan; | |||||
| std::function<void (const AvailableModuleList::ModuleIDAndFolderList&)> completionCallback; | |||||
| }; | |||||
| ThreadPoolJob* AvailableModuleList::createScannerJob (const Array<File>& paths) | |||||
| { | |||||
| return new ModuleScannerJob (paths, [this] (AvailableModuleList::ModuleIDAndFolderList scannedModuleList) | |||||
| { | |||||
| { | |||||
| const ScopedLock swapLock (lock); | |||||
| moduleList.swap (scannedModuleList); | |||||
| } | |||||
| listeners.call ([] (Listener& l) { MessageManager::callAsync ([&] { l.availableModulesChanged(); }); }); | |||||
| }); | |||||
| } | |||||
| void AvailableModuleList::removePendingAndAddJob (ThreadPoolJob* jobToAdd) | |||||
| { | |||||
| scanPool.removeAllJobs (false, 100); | |||||
| scanPool.addJob (jobToAdd, true); | |||||
| } | |||||
| void AvailableModuleList::scanPaths (const Array<File>& paths) | |||||
| { | |||||
| auto* job = createScannerJob (paths); | |||||
| removePendingAndAddJob (job); | |||||
| scanPool.waitForJobToFinish (job, -1); | |||||
| } | |||||
| void AvailableModuleList::scanPathsAsync (const Array<File>& paths) | |||||
| { | |||||
| removePendingAndAddJob (createScannerJob (paths)); | |||||
| } | |||||
| AvailableModuleList::ModuleIDAndFolderList AvailableModuleList::getAllModules() const | |||||
| { | |||||
| const ScopedLock readLock (lock); | |||||
| return moduleList; | |||||
| } | |||||
| AvailableModuleList::ModuleIDAndFolder AvailableModuleList::getModuleWithID (const String& id) const | |||||
| { | |||||
| const ScopedLock readLock (lock); | |||||
| for (auto& mod : moduleList) | |||||
| if (mod.first == id) | |||||
| return mod; | |||||
| return {}; | |||||
| } | |||||
| void AvailableModuleList::removeDuplicates (const ModuleIDAndFolderList& other) | |||||
| { | |||||
| const ScopedLock readLock (lock); | |||||
| for (auto& m : other) | |||||
| { | |||||
| auto pos = std::find (moduleList.begin(), moduleList.end(), m); | |||||
| if (pos != moduleList.end()) | |||||
| moduleList.erase (pos); | |||||
| } | |||||
| } | |||||
| #include "../../Application/jucer_Headers.h" | |||||
| #include "../../ProjectSaving/jucer_ProjectSaver.h" | |||||
| #include "../../ProjectSaving/jucer_ProjectExport_Xcode.h" | |||||
| #include "../../Application/jucer_Application.h" | |||||
| //============================================================================== | //============================================================================== | ||||
| LibraryModule::LibraryModule (const ModuleDescription& d) | LibraryModule::LibraryModule (const ModuleDescription& d) | ||||
| @@ -201,7 +29,7 @@ LibraryModule::LibraryModule (const ModuleDescription& d) | |||||
| void LibraryModule::writeIncludes (ProjectSaver& projectSaver, OutputStream& out) | void LibraryModule::writeIncludes (ProjectSaver& projectSaver, OutputStream& out) | ||||
| { | { | ||||
| auto& project = projectSaver.project; | |||||
| auto& project = projectSaver.getProject(); | |||||
| auto& modules = project.getEnabledModules(); | auto& modules = project.getEnabledModules(); | ||||
| auto moduleID = getID(); | auto moduleID = getID(); | ||||
| @@ -215,7 +43,7 @@ void LibraryModule::writeIncludes (ProjectSaver& projectSaver, OutputStream& out | |||||
| projectSaver.copyFolder (juceModuleFolder, localModuleFolder); | projectSaver.copyFolder (juceModuleFolder, localModuleFolder); | ||||
| } | } | ||||
| out << "#include <" << moduleInfo.moduleFolder.getFileName() << "/" | |||||
| out << "#include <" << moduleInfo.getModuleFolder().getFileName() << "/" | |||||
| << moduleInfo.getHeader().getFileName() | << moduleInfo.getHeader().getFileName() | ||||
| << ">" << newLine; | << ">" << newLine; | ||||
| } | } | ||||
| @@ -300,26 +128,26 @@ void LibraryModule::addLibsToExporter (ProjectExporter& exporter) const | |||||
| xcodeExporter.xcodeFrameworks.add ("AudioUnit"); | xcodeExporter.xcodeFrameworks.add ("AudioUnit"); | ||||
| } | } | ||||
| auto frameworks = moduleInfo.moduleInfo [xcodeExporter.isOSX() ? "OSXFrameworks" : "iOSFrameworks"].toString(); | |||||
| auto frameworks = moduleInfo.getModuleInfo() [xcodeExporter.isOSX() ? "OSXFrameworks" : "iOSFrameworks"].toString(); | |||||
| xcodeExporter.xcodeFrameworks.addTokens (frameworks, ", ", {}); | xcodeExporter.xcodeFrameworks.addTokens (frameworks, ", ", {}); | ||||
| parseAndAddLibsToList (xcodeExporter.xcodeLibs, moduleInfo.moduleInfo [exporter.isOSX() ? "OSXLibs" : "iOSLibs"].toString()); | |||||
| parseAndAddLibsToList (xcodeExporter.xcodeLibs, moduleInfo.getModuleInfo() [exporter.isOSX() ? "OSXLibs" : "iOSLibs"].toString()); | |||||
| } | } | ||||
| else if (exporter.isLinux()) | else if (exporter.isLinux()) | ||||
| { | { | ||||
| parseAndAddLibsToList (exporter.linuxLibs, moduleInfo.moduleInfo ["linuxLibs"].toString()); | |||||
| parseAndAddLibsToList (exporter.linuxPackages, moduleInfo.moduleInfo ["linuxPackages"].toString()); | |||||
| parseAndAddLibsToList (exporter.linuxLibs, moduleInfo.getModuleInfo() ["linuxLibs"].toString()); | |||||
| parseAndAddLibsToList (exporter.linuxPackages, moduleInfo.getModuleInfo() ["linuxPackages"].toString()); | |||||
| } | } | ||||
| else if (exporter.isWindows()) | else if (exporter.isWindows()) | ||||
| { | { | ||||
| if (exporter.isCodeBlocks()) | if (exporter.isCodeBlocks()) | ||||
| parseAndAddLibsToList (exporter.mingwLibs, moduleInfo.moduleInfo ["mingwLibs"].toString()); | |||||
| parseAndAddLibsToList (exporter.mingwLibs, moduleInfo.getModuleInfo() ["mingwLibs"].toString()); | |||||
| else | else | ||||
| parseAndAddLibsToList (exporter.windowsLibs, moduleInfo.moduleInfo ["windowsLibs"].toString()); | |||||
| parseAndAddLibsToList (exporter.windowsLibs, moduleInfo.getModuleInfo() ["windowsLibs"].toString()); | |||||
| } | } | ||||
| else if (exporter.isAndroid()) | else if (exporter.isAndroid()) | ||||
| { | { | ||||
| parseAndAddLibsToList (exporter.androidLibs, moduleInfo.moduleInfo ["androidLibs"].toString()); | |||||
| parseAndAddLibsToList (exporter.androidLibs, moduleInfo.getModuleInfo() ["androidLibs"].toString()); | |||||
| } | } | ||||
| } | } | ||||
| @@ -544,12 +372,12 @@ void LibraryModule::addBrowseableCode (ProjectExporter& exporter, const Array<Fi | |||||
| } | } | ||||
| //============================================================================== | //============================================================================== | ||||
| EnabledModuleList::EnabledModuleList (Project& p, const ValueTree& s) | |||||
| EnabledModulesList::EnabledModulesList (Project& p, const ValueTree& s) | |||||
| : project (p), state (s) | : project (p), state (s) | ||||
| { | { | ||||
| } | } | ||||
| StringArray EnabledModuleList::getAllModules() const | |||||
| StringArray EnabledModulesList::getAllModules() const | |||||
| { | { | ||||
| StringArray moduleIDs; | StringArray moduleIDs; | ||||
| @@ -559,13 +387,13 @@ StringArray EnabledModuleList::getAllModules() const | |||||
| return moduleIDs; | return moduleIDs; | ||||
| } | } | ||||
| void EnabledModuleList::createRequiredModules (OwnedArray<LibraryModule>& modules) | |||||
| void EnabledModulesList::createRequiredModules (OwnedArray<LibraryModule>& modules) | |||||
| { | { | ||||
| for (int i = 0; i < getNumModules(); ++i) | for (int i = 0; i < getNumModules(); ++i) | ||||
| modules.add (new LibraryModule (getModuleInfo (getModuleID (i)))); | modules.add (new LibraryModule (getModuleInfo (getModuleID (i)))); | ||||
| } | } | ||||
| void EnabledModuleList::sortAlphabetically() | |||||
| void EnabledModulesList::sortAlphabetically() | |||||
| { | { | ||||
| struct ModuleTreeSorter | struct ModuleTreeSorter | ||||
| { | { | ||||
| @@ -579,14 +407,14 @@ void EnabledModuleList::sortAlphabetically() | |||||
| state.sort (sorter, getUndoManager(), false); | state.sort (sorter, getUndoManager(), false); | ||||
| } | } | ||||
| File EnabledModuleList::getDefaultModulesFolder() const | |||||
| File EnabledModulesList::getDefaultModulesFolder() const | |||||
| { | { | ||||
| File globalPath (getAppSettings().getStoredPath (Ids::defaultJuceModulePath, TargetOS::getThisOS()).get().toString()); | File globalPath (getAppSettings().getStoredPath (Ids::defaultJuceModulePath, TargetOS::getThisOS()).get().toString()); | ||||
| if (globalPath.exists()) | if (globalPath.exists()) | ||||
| return globalPath; | return globalPath; | ||||
| for (auto& exporterPathModule : project.getExporterPathsModuleList().getAllModules()) | |||||
| for (auto& exporterPathModule : project.getExporterPathsModulesList().getAllModules()) | |||||
| { | { | ||||
| auto f = exporterPathModule.second; | auto f = exporterPathModule.second; | ||||
| @@ -597,12 +425,12 @@ File EnabledModuleList::getDefaultModulesFolder() const | |||||
| return File::getCurrentWorkingDirectory(); | return File::getCurrentWorkingDirectory(); | ||||
| } | } | ||||
| ModuleDescription EnabledModuleList::getModuleInfo (const String& moduleID) | |||||
| ModuleDescription EnabledModulesList::getModuleInfo (const String& moduleID) const | |||||
| { | { | ||||
| return ModuleDescription (project.getModuleWithID (moduleID).second); | return ModuleDescription (project.getModuleWithID (moduleID).second); | ||||
| } | } | ||||
| bool EnabledModuleList::isModuleEnabled (const String& moduleID) const | |||||
| bool EnabledModulesList::isModuleEnabled (const String& moduleID) const | |||||
| { | { | ||||
| return state.getChildWithProperty (Ids::ID, moduleID).isValid(); | return state.getChildWithProperty (Ids::ID, moduleID).isValid(); | ||||
| } | } | ||||
| @@ -621,7 +449,7 @@ static void getDependencies (Project& project, const String& moduleID, StringArr | |||||
| } | } | ||||
| } | } | ||||
| StringArray EnabledModuleList::getExtraDependenciesNeeded (const String& moduleID) const | |||||
| StringArray EnabledModulesList::getExtraDependenciesNeeded (const String& moduleID) const | |||||
| { | { | ||||
| StringArray dependencies, extraDepsNeeded; | StringArray dependencies, extraDepsNeeded; | ||||
| getDependencies (project, moduleID, dependencies); | getDependencies (project, moduleID, dependencies); | ||||
| @@ -633,11 +461,31 @@ StringArray EnabledModuleList::getExtraDependenciesNeeded (const String& moduleI | |||||
| return extraDepsNeeded; | return extraDepsNeeded; | ||||
| } | } | ||||
| bool EnabledModuleList::doesModuleHaveHigherCppStandardThanProject (const String& moduleID) | |||||
| bool EnabledModulesList::tryToFixMissingDependencies (const String& moduleID) | |||||
| { | |||||
| auto copyLocally = areMostModulesCopiedLocally(); | |||||
| auto useGlobalPath = areMostModulesUsingGlobalPath(); | |||||
| StringArray missing; | |||||
| for (auto missingModule : getExtraDependenciesNeeded (moduleID)) | |||||
| { | |||||
| auto mod = project.getModuleWithID (missingModule); | |||||
| if (mod.second != File()) | |||||
| addModule (mod.second, copyLocally, useGlobalPath); | |||||
| else | |||||
| missing.add (missingModule); | |||||
| } | |||||
| return (missing.size() == 0); | |||||
| } | |||||
| bool EnabledModulesList::doesModuleHaveHigherCppStandardThanProject (const String& moduleID) const | |||||
| { | { | ||||
| auto projectCppStandard = project.getCppStandardString(); | auto projectCppStandard = project.getCppStandardString(); | ||||
| if (projectCppStandard == "latest") | |||||
| if (projectCppStandard == Project::getCppStandardVars().getLast().toString()) | |||||
| return false; | return false; | ||||
| auto moduleCppStandard = getModuleInfo (moduleID).getMinimumCppStandard(); | auto moduleCppStandard = getModuleInfo (moduleID).getMinimumCppStandard(); | ||||
| @@ -645,40 +493,40 @@ bool EnabledModuleList::doesModuleHaveHigherCppStandardThanProject (const String | |||||
| return (moduleCppStandard.getIntValue() > projectCppStandard.getIntValue()); | return (moduleCppStandard.getIntValue() > projectCppStandard.getIntValue()); | ||||
| } | } | ||||
| bool EnabledModuleList::shouldUseGlobalPath (const String& moduleID) const | |||||
| bool EnabledModulesList::shouldUseGlobalPath (const String& moduleID) const | |||||
| { | { | ||||
| return (bool) shouldUseGlobalPathValue (moduleID).getValue(); | return (bool) shouldUseGlobalPathValue (moduleID).getValue(); | ||||
| } | } | ||||
| Value EnabledModuleList::shouldUseGlobalPathValue (const String& moduleID) const | |||||
| Value EnabledModulesList::shouldUseGlobalPathValue (const String& moduleID) const | |||||
| { | { | ||||
| return state.getChildWithProperty (Ids::ID, moduleID) | return state.getChildWithProperty (Ids::ID, moduleID) | ||||
| .getPropertyAsValue (Ids::useGlobalPath, getUndoManager()); | .getPropertyAsValue (Ids::useGlobalPath, getUndoManager()); | ||||
| } | } | ||||
| bool EnabledModuleList::shouldShowAllModuleFilesInProject (const String& moduleID) const | |||||
| bool EnabledModulesList::shouldShowAllModuleFilesInProject (const String& moduleID) const | |||||
| { | { | ||||
| return (bool) shouldShowAllModuleFilesInProjectValue (moduleID).getValue(); | return (bool) shouldShowAllModuleFilesInProjectValue (moduleID).getValue(); | ||||
| } | } | ||||
| Value EnabledModuleList::shouldShowAllModuleFilesInProjectValue (const String& moduleID) const | |||||
| Value EnabledModulesList::shouldShowAllModuleFilesInProjectValue (const String& moduleID) const | |||||
| { | { | ||||
| return state.getChildWithProperty (Ids::ID, moduleID) | return state.getChildWithProperty (Ids::ID, moduleID) | ||||
| .getPropertyAsValue (Ids::showAllCode, getUndoManager()); | .getPropertyAsValue (Ids::showAllCode, getUndoManager()); | ||||
| } | } | ||||
| bool EnabledModuleList::shouldCopyModuleFilesLocally (const String& moduleID) const | |||||
| bool EnabledModulesList::shouldCopyModuleFilesLocally (const String& moduleID) const | |||||
| { | { | ||||
| return (bool) shouldCopyModuleFilesLocallyValue (moduleID).getValue(); | return (bool) shouldCopyModuleFilesLocallyValue (moduleID).getValue(); | ||||
| } | } | ||||
| Value EnabledModuleList::shouldCopyModuleFilesLocallyValue (const String& moduleID) const | |||||
| Value EnabledModulesList::shouldCopyModuleFilesLocallyValue (const String& moduleID) const | |||||
| { | { | ||||
| return state.getChildWithProperty (Ids::ID, moduleID) | return state.getChildWithProperty (Ids::ID, moduleID) | ||||
| .getPropertyAsValue (Ids::useLocalCopy, getUndoManager()); | .getPropertyAsValue (Ids::useLocalCopy, getUndoManager()); | ||||
| } | } | ||||
| bool EnabledModuleList::areMostModulesUsingGlobalPath() const | |||||
| bool EnabledModulesList::areMostModulesUsingGlobalPath() const | |||||
| { | { | ||||
| int numYes = 0, numNo = 0; | int numYes = 0, numNo = 0; | ||||
| @@ -693,7 +541,7 @@ bool EnabledModuleList::areMostModulesUsingGlobalPath() const | |||||
| return numYes > numNo; | return numYes > numNo; | ||||
| } | } | ||||
| bool EnabledModuleList::areMostModulesCopiedLocally() const | |||||
| bool EnabledModulesList::areMostModulesCopiedLocally() const | |||||
| { | { | ||||
| int numYes = 0, numNo = 0; | int numYes = 0, numNo = 0; | ||||
| @@ -708,7 +556,47 @@ bool EnabledModuleList::areMostModulesCopiedLocally() const | |||||
| return numYes > numNo; | return numYes > numNo; | ||||
| } | } | ||||
| void EnabledModuleList::addModule (const File& moduleFolder, bool copyLocally, bool useGlobalPath) | |||||
| StringArray EnabledModulesList::getModulesWithHigherCppStandardThanProject() const | |||||
| { | |||||
| StringArray list; | |||||
| for (auto& module : getAllModules()) | |||||
| if (doesModuleHaveHigherCppStandardThanProject (module)) | |||||
| list.add (module); | |||||
| return list; | |||||
| } | |||||
| StringArray EnabledModulesList::getModulesWithMissingDependencies() const | |||||
| { | |||||
| StringArray list; | |||||
| for (auto& module : getAllModules()) | |||||
| if (getExtraDependenciesNeeded (module).size() > 0) | |||||
| list.add (module); | |||||
| return list; | |||||
| } | |||||
| String EnabledModulesList::getHighestModuleCppStandard() const | |||||
| { | |||||
| auto highestCppStandard = Project::getCppStandardVars()[0].toString(); | |||||
| for (auto& mod : getAllModules()) | |||||
| { | |||||
| auto moduleCppStandard = getModuleInfo (mod).getMinimumCppStandard(); | |||||
| if (moduleCppStandard == "latest") | |||||
| return moduleCppStandard; | |||||
| if (moduleCppStandard.getIntValue() > highestCppStandard.getIntValue()) | |||||
| highestCppStandard = moduleCppStandard; | |||||
| } | |||||
| return highestCppStandard; | |||||
| } | |||||
| void EnabledModulesList::addModule (const File& moduleFolder, bool copyLocally, bool useGlobalPath) | |||||
| { | { | ||||
| ModuleDescription info (moduleFolder); | ModuleDescription info (moduleFolder); | ||||
| @@ -741,7 +629,7 @@ void EnabledModuleList::addModule (const File& moduleFolder, bool copyLocally, b | |||||
| } | } | ||||
| } | } | ||||
| void EnabledModuleList::addModuleInteractive (const String& moduleID) | |||||
| void EnabledModulesList::addModuleInteractive (const String& moduleID) | |||||
| { | { | ||||
| auto f = project.getModuleWithID (moduleID).second; | auto f = project.getModuleWithID (moduleID).second; | ||||
| @@ -754,7 +642,7 @@ void EnabledModuleList::addModuleInteractive (const String& moduleID) | |||||
| addModuleFromUserSelectedFile(); | addModuleFromUserSelectedFile(); | ||||
| } | } | ||||
| void EnabledModuleList::addModuleFromUserSelectedFile() | |||||
| void EnabledModulesList::addModuleFromUserSelectedFile() | |||||
| { | { | ||||
| auto lastLocation = getDefaultModulesFolder(); | auto lastLocation = getDefaultModulesFolder(); | ||||
| @@ -767,7 +655,7 @@ void EnabledModuleList::addModuleFromUserSelectedFile() | |||||
| } | } | ||||
| } | } | ||||
| void EnabledModuleList::addModuleOfferingToCopy (const File& f, bool isFromUserSpecifiedFolder) | |||||
| void EnabledModulesList::addModuleOfferingToCopy (const File& f, bool isFromUserSpecifiedFolder) | |||||
| { | { | ||||
| ModuleDescription m (f); | ModuleDescription m (f); | ||||
| @@ -785,11 +673,11 @@ void EnabledModuleList::addModuleOfferingToCopy (const File& f, bool isFromUserS | |||||
| return; | return; | ||||
| } | } | ||||
| addModule (m.moduleFolder, areMostModulesCopiedLocally(), | |||||
| addModule (m.getModuleFolder(), areMostModulesCopiedLocally(), | |||||
| isFromUserSpecifiedFolder ? false : areMostModulesUsingGlobalPath()); | isFromUserSpecifiedFolder ? false : areMostModulesUsingGlobalPath()); | ||||
| } | } | ||||
| void EnabledModuleList::removeModule (String moduleID) // must be pass-by-value, and not a const ref! | |||||
| void EnabledModulesList::removeModule (String moduleID) // must be pass-by-value, and not a const ref! | |||||
| { | { | ||||
| for (auto i = state.getNumChildren(); --i >= 0;) | for (auto i = state.getNumChildren(); --i >= 0;) | ||||
| if (state.getChild(i) [Ids::ID] == moduleID) | if (state.getChild(i) [Ids::ID] == moduleID) | ||||
| @@ -18,37 +18,11 @@ | |||||
| #pragma once | #pragma once | ||||
| #include "jucer_Project.h" | |||||
| #include "../jucer_Project.h" | |||||
| class ProjectExporter; | class ProjectExporter; | ||||
| class ProjectSaver; | class ProjectSaver; | ||||
| //============================================================================== | |||||
| struct ModuleDescription | |||||
| { | |||||
| ModuleDescription() = default; | |||||
| ModuleDescription (const File& folder); | |||||
| bool isValid() const { return getID().isNotEmpty(); } | |||||
| String getID() const { return moduleInfo [Ids::ID_uppercase].toString(); } | |||||
| String getVendor() const { return moduleInfo [Ids::vendor].toString(); } | |||||
| String getVersion() const { return moduleInfo [Ids::version].toString(); } | |||||
| String getName() const { return moduleInfo [Ids::name].toString(); } | |||||
| String getDescription() const { return moduleInfo [Ids::description].toString(); } | |||||
| String getLicense() const { return moduleInfo [Ids::license].toString(); } | |||||
| String getMinimumCppStandard() const { return moduleInfo [Ids::minimumCppStandard].toString(); } | |||||
| String getPreprocessorDefs() const { return moduleInfo [Ids::defines].toString(); } | |||||
| String getExtraSearchPaths() const { return moduleInfo [Ids::searchpaths].toString(); } | |||||
| StringArray getDependencies() const; | |||||
| File getFolder() const { jassert (moduleFolder != File()); return moduleFolder; } | |||||
| File getHeader() const; | |||||
| File moduleFolder; | |||||
| var moduleInfo; | |||||
| URL url; | |||||
| }; | |||||
| //============================================================================== | //============================================================================== | ||||
| class LibraryModule | class LibraryModule | ||||
| { | { | ||||
| @@ -102,51 +76,10 @@ private: | |||||
| }; | }; | ||||
| //============================================================================== | //============================================================================== | ||||
| class AvailableModuleList | |||||
| class EnabledModulesList | |||||
| { | { | ||||
| public: | public: | ||||
| using ModuleIDAndFolder = std::pair<String, File>; | |||||
| using ModuleIDAndFolderList = std::vector<ModuleIDAndFolder>; | |||||
| AvailableModuleList() = default; | |||||
| void scanPaths (const Array<File>&); | |||||
| void scanPathsAsync (const Array<File>&); | |||||
| ModuleIDAndFolderList getAllModules() const; | |||||
| ModuleIDAndFolder getModuleWithID (const String&) const; | |||||
| void removeDuplicates (const ModuleIDAndFolderList& other); | |||||
| //============================================================================== | |||||
| struct Listener | |||||
| { | |||||
| virtual ~Listener() {} | |||||
| virtual void availableModulesChanged() = 0; | |||||
| }; | |||||
| void addListener (Listener* listenerToAdd) { listeners.add (listenerToAdd); } | |||||
| void removeListener (Listener* listenerToRemove) { listeners.remove (listenerToRemove); } | |||||
| private: | |||||
| ThreadPoolJob* createScannerJob (const Array<File>&); | |||||
| void removePendingAndAddJob (ThreadPoolJob*); | |||||
| ThreadPool scanPool { 1 }; | |||||
| ModuleIDAndFolderList moduleList; | |||||
| ListenerList<Listener> listeners; | |||||
| CriticalSection lock; | |||||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AvailableModuleList) | |||||
| }; | |||||
| //============================================================================== | |||||
| class EnabledModuleList | |||||
| { | |||||
| public: | |||||
| EnabledModuleList (Project&, const ValueTree&); | |||||
| EnabledModulesList (Project&, const ValueTree&); | |||||
| //============================================================================== | //============================================================================== | ||||
| ValueTree getState() const { return state; } | ValueTree getState() const { return state; } | ||||
| @@ -160,11 +93,14 @@ public: | |||||
| int getNumModules() const { return state.getNumChildren(); } | int getNumModules() const { return state.getNumChildren(); } | ||||
| String getModuleID (int index) const { return state.getChild (index) [Ids::ID].toString(); } | String getModuleID (int index) const { return state.getChild (index) [Ids::ID].toString(); } | ||||
| ModuleDescription getModuleInfo (const String& moduleID); | |||||
| ModuleDescription getModuleInfo (const String& moduleID) const; | |||||
| bool isModuleEnabled (const String& moduleID) const; | bool isModuleEnabled (const String& moduleID) const; | ||||
| StringArray getExtraDependenciesNeeded (const String& moduleID) const; | StringArray getExtraDependenciesNeeded (const String& moduleID) const; | ||||
| bool doesModuleHaveHigherCppStandardThanProject (const String& moduleID); | |||||
| bool tryToFixMissingDependencies (const String& moduleID); | |||||
| bool doesModuleHaveHigherCppStandardThanProject (const String& moduleID) const; | |||||
| bool shouldUseGlobalPath (const String& moduleID) const; | bool shouldUseGlobalPath (const String& moduleID) const; | ||||
| Value shouldUseGlobalPathValue (const String& moduleID) const; | Value shouldUseGlobalPathValue (const String& moduleID) const; | ||||
| @@ -178,6 +114,11 @@ public: | |||||
| bool areMostModulesUsingGlobalPath() const; | bool areMostModulesUsingGlobalPath() const; | ||||
| bool areMostModulesCopiedLocally() const; | bool areMostModulesCopiedLocally() const; | ||||
| StringArray getModulesWithHigherCppStandardThanProject() const; | |||||
| StringArray getModulesWithMissingDependencies() const; | |||||
| String getHighestModuleCppStandard() const; | |||||
| //============================================================================== | //============================================================================== | ||||
| void addModule (const File& moduleManifestFile, bool copyLocally, bool useGlobalPath); | void addModule (const File& moduleManifestFile, bool copyLocally, bool useGlobalPath); | ||||
| void addModuleInteractive (const String& moduleID); | void addModuleInteractive (const String& moduleID); | ||||
| @@ -192,5 +133,5 @@ private: | |||||
| Project& project; | Project& project; | ||||
| ValueTree state; | ValueTree state; | ||||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (EnabledModuleList) | |||||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (EnabledModulesList) | |||||
| }; | }; | ||||
| @@ -108,18 +108,11 @@ public: | |||||
| void handlePopupMenuResult (int resultCode) override | void handlePopupMenuResult (int resultCode) override | ||||
| { | { | ||||
| if (resultCode == 1) | if (resultCode == 1) | ||||
| { | |||||
| exporter->addNewConfiguration (false); | exporter->addNewConfiguration (false); | ||||
| } | |||||
| else if (resultCode == 2) | else if (resultCode == 2) | ||||
| { | |||||
| const ScopedValueSetter<String> valueSetter (project.specifiedExporterToSave, exporter->getName(), {}); | |||||
| project.save (true, true); | |||||
| } | |||||
| project.saveProject (exporter.get()); | |||||
| else if (resultCode == 3) | else if (resultCode == 3) | ||||
| { | |||||
| deleteAllSelectedItems(); | deleteAllSelectedItems(); | ||||
| } | |||||
| } | } | ||||
| var getDragSourceDescription() override | var getDragSourceDescription() override | ||||
| @@ -110,7 +110,7 @@ public: | |||||
| { | { | ||||
| auto f = filesToTrash.getUnchecked(i); | auto f = filesToTrash.getUnchecked(i); | ||||
| om.closeFile (f, false); | |||||
| om.closeFile (f, OpenDocumentManager::SaveIfNeeded::no); | |||||
| if (! f.moveToTrash()) | if (! f.moveToTrash()) | ||||
| { | { | ||||
| @@ -129,7 +129,7 @@ public: | |||||
| pcc->hideEditor(); | pcc->hideEditor(); | ||||
| } | } | ||||
| om.closeFile (itemToRemove->getFile(), false); | |||||
| om.closeFile (itemToRemove->getFile(), OpenDocumentManager::SaveIfNeeded::no); | |||||
| itemToRemove->deleteItem(); | itemToRemove->deleteItem(); | ||||
| } | } | ||||
| } | } | ||||
| @@ -61,9 +61,8 @@ class LiveBuildTab : public Component, | |||||
| public: | public: | ||||
| LiveBuildTab (const CompileEngineChildProcess::Ptr& child, String lastErrorMessage) | LiveBuildTab (const CompileEngineChildProcess::Ptr& child, String lastErrorMessage) | ||||
| { | { | ||||
| settingsButton.reset (new IconButton ("Settings", &getIcons().settings)); | |||||
| addAndMakeVisible (settingsButton.get()); | |||||
| settingsButton->onClick = [this] | |||||
| addAndMakeVisible (settingsButton); | |||||
| settingsButton.onClick = [this] | |||||
| { | { | ||||
| if (auto* pcc = findParentComponentOfClass<ProjectContentComponent>()) | if (auto* pcc = findParentComponentOfClass<ProjectContentComponent>()) | ||||
| pcc->showLiveBuildSettings(); | pcc->showLiveBuildSettings(); | ||||
| @@ -122,9 +121,9 @@ public: | |||||
| { | { | ||||
| auto bounds = getLocalBounds(); | auto bounds = getLocalBounds(); | ||||
| settingsButton->setBounds (bounds.removeFromBottom (25) | |||||
| .removeFromRight (25) | |||||
| .reduced (3)); | |||||
| settingsButton.setBounds (bounds.removeFromBottom (25) | |||||
| .removeFromRight (25) | |||||
| .reduced (3)); | |||||
| if (errorMessageLabel != nullptr) | if (errorMessageLabel != nullptr) | ||||
| { | { | ||||
| @@ -155,7 +154,7 @@ public: | |||||
| private: | private: | ||||
| OwnedArray<ConcertinaHeader> headers; | OwnedArray<ConcertinaHeader> headers; | ||||
| ConcertinaPanel concertinaPanel; | ConcertinaPanel concertinaPanel; | ||||
| std::unique_ptr<IconButton> settingsButton; | |||||
| IconButton settingsButton { "Settings", getIcons().settings }; | |||||
| std::unique_ptr<TextButton> downloadButton, enableButton; | std::unique_ptr<TextButton> downloadButton, enableButton; | ||||
| std::unique_ptr<Label> errorMessageLabel; | std::unique_ptr<Label> errorMessageLabel; | ||||
| @@ -361,17 +361,21 @@ private: | |||||
| void fixDependencies() | void fixDependencies() | ||||
| { | { | ||||
| if (! tryToFix()) | |||||
| auto& enabledModules = project.getEnabledModules(); | |||||
| if (enabledModules.tryToFixMissingDependencies (moduleID)) | |||||
| { | |||||
| missingDependencies.clear(); | |||||
| } | |||||
| else | |||||
| { | { | ||||
| missingDependencies = enabledModules.getExtraDependenciesNeeded (moduleID); | |||||
| AlertWindow::showMessageBoxAsync (AlertWindow::WarningIcon, | AlertWindow::showMessageBoxAsync (AlertWindow::WarningIcon, | ||||
| "Adding Missing Dependencies", | "Adding Missing Dependencies", | ||||
| "Couldn't locate some of these modules - you'll need to find their " | "Couldn't locate some of these modules - you'll need to find their " | ||||
| "folders manually and add them to the list."); | "folders manually and add them to the list."); | ||||
| return; | |||||
| } | } | ||||
| refreshAndReselectItem(); | |||||
| } | } | ||||
| void resized() override | void resized() override | ||||
| @@ -381,59 +385,11 @@ private: | |||||
| private: | private: | ||||
| Project& project; | Project& project; | ||||
| String moduleID; | String moduleID; | ||||
| StringArray missingDependencies; | StringArray missingDependencies; | ||||
| TextButton fixButton { "Add Required Modules" }; | TextButton fixButton { "Add Required Modules" }; | ||||
| bool tryToFix() | |||||
| { | |||||
| auto& enabledModules = project.getEnabledModules(); | |||||
| auto copyLocally = enabledModules.areMostModulesCopiedLocally(); | |||||
| auto useGlobalPath = enabledModules.areMostModulesUsingGlobalPath(); | |||||
| StringArray missing; | |||||
| for (auto missingModule : missingDependencies) | |||||
| { | |||||
| auto mod = project.getModuleWithID (missingModule); | |||||
| if (mod.second != File()) | |||||
| enabledModules.addModule (mod.second, copyLocally, useGlobalPath); | |||||
| else | |||||
| missing.add (missingModule); | |||||
| } | |||||
| missingDependencies.swapWith (missing); | |||||
| return (missingDependencies.size() == 0); | |||||
| } | |||||
| void refreshAndReselectItem() | |||||
| { | |||||
| if (auto* settingsPanel = findParentComponentOfClass<ModuleSettingsPanel>()) | |||||
| { | |||||
| if (settingsPanel->modulesTree == nullptr) | |||||
| return; | |||||
| auto* rootItem = settingsPanel->modulesTree->getRootItem(); | |||||
| if (rootItem == nullptr) | |||||
| return; | |||||
| for (int i = 0; i < rootItem->getNumSubItems(); ++i) | |||||
| { | |||||
| if (auto* subItem = dynamic_cast<ProjectTreeItemBase*> (rootItem->getSubItem (i))) | |||||
| { | |||||
| if (subItem->getDisplayName() == moduleID) | |||||
| { | |||||
| subItem->setSelected (true, true); | |||||
| return; | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MissingDependenciesComponent) | JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MissingDependenciesComponent) | ||||
| }; | }; | ||||
| @@ -468,28 +424,30 @@ private: | |||||
| //============================================================================== | //============================================================================== | ||||
| class EnabledModulesItem : public ProjectTreeItemBase, | class EnabledModulesItem : public ProjectTreeItemBase, | ||||
| private Value::Listener, | private Value::Listener, | ||||
| private AvailableModuleList::Listener | |||||
| private AvailableModulesList::Listener | |||||
| { | { | ||||
| public: | public: | ||||
| EnabledModulesItem (Project& p) | EnabledModulesItem (Project& p) | ||||
| : project (p), | : project (p), | ||||
| moduleListTree (project.getEnabledModules().getState()) | |||||
| modulesListTree (project.getEnabledModules().getState()) | |||||
| { | { | ||||
| moduleListTree.addListener (this); | |||||
| modulesListTree.addListener (this); | |||||
| projectCppStandardValue.referTo (project.getProjectValue (Ids::cppLanguageStandard)); | projectCppStandardValue.referTo (project.getProjectValue (Ids::cppLanguageStandard)); | ||||
| projectCppStandardValue.addListener (this); | projectCppStandardValue.addListener (this); | ||||
| ProjucerApplication::getApp().getJUCEPathModuleList().addListener (this); | |||||
| ProjucerApplication::getApp().getUserPathsModuleList().addListener (this); | |||||
| project.getExporterPathsModuleList().addListener (this); | |||||
| ProjucerApplication::getApp().getJUCEPathModulesList().addListener (this); | |||||
| ProjucerApplication::getApp().getUserPathsModulesList().addListener (this); | |||||
| project.getExporterPathsModulesList().addListener (this); | |||||
| } | } | ||||
| ~EnabledModulesItem() override | ~EnabledModulesItem() override | ||||
| { | { | ||||
| ProjucerApplication::getApp().getJUCEPathModuleList().removeListener (this); | |||||
| ProjucerApplication::getApp().getUserPathsModuleList().removeListener (this); | |||||
| project.getExporterPathsModuleList().removeListener (this); | |||||
| ProjucerApplication::getApp().getJUCEPathModulesList().removeListener (this); | |||||
| ProjucerApplication::getApp().getUserPathsModulesList().removeListener (this); | |||||
| project.getExporterPathsModulesList().removeListener (this); | |||||
| } | } | ||||
| int getItemHeight() const override { return 22; } | int getItemHeight() const override { return 22; } | ||||
| @@ -539,7 +497,7 @@ public: | |||||
| } | } | ||||
| for (int i = 0; i < modules.size(); ++i) | for (int i = 0; i < modules.size(); ++i) | ||||
| project.getEnabledModules().addModule (modules.getReference(i).moduleFolder, | |||||
| project.getEnabledModules().addModule (modules.getReference (i).getModuleFolder(), | |||||
| project.getEnabledModules().areMostModulesCopiedLocally(), | project.getEnabledModules().areMostModulesCopiedLocally(), | ||||
| project.getEnabledModules().areMostModulesUsingGlobalPath()); | project.getEnabledModules().areMostModulesUsingGlobalPath()); | ||||
| } | } | ||||
| @@ -560,7 +518,7 @@ public: | |||||
| // JUCE path | // JUCE path | ||||
| PopupMenu jucePathModules; | PopupMenu jucePathModules; | ||||
| for (auto& mod : ProjucerApplication::getApp().getJUCEPathModuleList().getAllModules()) | |||||
| for (auto& mod : ProjucerApplication::getApp().getJUCEPathModulesList().getAllModules()) | |||||
| jucePathModules.addItem (index++, mod.first, ! enabledModules.isModuleEnabled (mod.first)); | jucePathModules.addItem (index++, mod.first, ! enabledModules.isModuleEnabled (mod.first)); | ||||
| jucePathModules.addSeparator(); | jucePathModules.addSeparator(); | ||||
| @@ -572,7 +530,7 @@ public: | |||||
| index = 200; | index = 200; | ||||
| PopupMenu userPathModules; | PopupMenu userPathModules; | ||||
| for (auto& mod : ProjucerApplication::getApp().getUserPathsModuleList().getAllModules()) | |||||
| for (auto& mod : ProjucerApplication::getApp().getUserPathsModulesList().getAllModules()) | |||||
| userPathModules.addItem (index++, mod.first, ! enabledModules.isModuleEnabled (mod.first)); | userPathModules.addItem (index++, mod.first, ! enabledModules.isModuleEnabled (mod.first)); | ||||
| userPathModules.addSeparator(); | userPathModules.addSeparator(); | ||||
| @@ -584,7 +542,7 @@ public: | |||||
| index = 300; | index = 300; | ||||
| PopupMenu exporterPathModules; | PopupMenu exporterPathModules; | ||||
| for (auto& mod : project.getExporterPathsModuleList().getAllModules()) | |||||
| for (auto& mod : project.getExporterPathsModulesList().getAllModules()) | |||||
| exporterPathModules.addItem (index++, mod.first, ! enabledModules.isModuleEnabled (mod.first)); | exporterPathModules.addItem (index++, mod.first, ! enabledModules.isModuleEnabled (mod.first)); | ||||
| exporterPathModules.addSeparator(); | exporterPathModules.addSeparator(); | ||||
| @@ -615,22 +573,22 @@ public: | |||||
| } | } | ||||
| else if (resultCode > 0) | else if (resultCode > 0) | ||||
| { | { | ||||
| std::vector<AvailableModuleList::ModuleIDAndFolder> list; | |||||
| std::vector<AvailableModulesList::ModuleIDAndFolder> list; | |||||
| int offset = -1; | int offset = -1; | ||||
| if (resultCode < 200) | if (resultCode < 200) | ||||
| { | { | ||||
| list = ProjucerApplication::getApp().getJUCEPathModuleList().getAllModules(); | |||||
| list = ProjucerApplication::getApp().getJUCEPathModulesList().getAllModules(); | |||||
| offset = 100; | offset = 100; | ||||
| } | } | ||||
| else if (resultCode < 300) | else if (resultCode < 300) | ||||
| { | { | ||||
| list = ProjucerApplication::getApp().getUserPathsModuleList().getAllModules(); | |||||
| list = ProjucerApplication::getApp().getUserPathsModulesList().getAllModules(); | |||||
| offset = 200; | offset = 200; | ||||
| } | } | ||||
| else if (resultCode < 400) | else if (resultCode < 400) | ||||
| { | { | ||||
| list = project.getExporterPathsModuleList().getAllModules(); | |||||
| list = project.getExporterPathsModulesList().getAllModules(); | |||||
| offset = 300; | offset = 300; | ||||
| } | } | ||||
| @@ -646,13 +604,20 @@ public: | |||||
| void refreshIfNeeded (ValueTree& changedTree) | void refreshIfNeeded (ValueTree& changedTree) | ||||
| { | { | ||||
| if (changedTree == moduleListTree) | |||||
| if (changedTree == modulesListTree) | |||||
| { | |||||
| auto selectedID = getSelectedItemID(); | |||||
| refreshSubItems(); | refreshSubItems(); | ||||
| if (selectedID.isNotEmpty()) | |||||
| setSelectedItem (selectedID); | |||||
| } | |||||
| } | } | ||||
| private: | private: | ||||
| Project& project; | Project& project; | ||||
| ValueTree moduleListTree; | |||||
| ValueTree modulesListTree; | |||||
| Value projectCppStandardValue; | Value projectCppStandardValue; | ||||
| //============================================================================== | //============================================================================== | ||||
| @@ -676,22 +641,47 @@ private: | |||||
| void removeDuplicateModules() | void removeDuplicateModules() | ||||
| { | { | ||||
| auto jucePathModuleList = ProjucerApplication::getApp().getJUCEPathModuleList().getAllModules(); | |||||
| auto jucePathModulesList = ProjucerApplication::getApp().getJUCEPathModulesList().getAllModules(); | |||||
| auto& userPathModules = ProjucerApplication::getApp().getUserPathsModuleList(); | |||||
| userPathModules.removeDuplicates (jucePathModuleList); | |||||
| auto& userPathModules = ProjucerApplication::getApp().getUserPathsModulesList(); | |||||
| userPathModules.removeDuplicates (jucePathModulesList); | |||||
| auto& exporterPathModules = project.getExporterPathsModuleList(); | |||||
| exporterPathModules.removeDuplicates (jucePathModuleList); | |||||
| auto& exporterPathModules = project.getExporterPathsModulesList(); | |||||
| exporterPathModules.removeDuplicates (jucePathModulesList); | |||||
| exporterPathModules.removeDuplicates (userPathModules.getAllModules()); | exporterPathModules.removeDuplicates (userPathModules.getAllModules()); | ||||
| } | } | ||||
| void availableModulesChanged() override | |||||
| void availableModulesChanged (AvailableModulesList*) override | |||||
| { | { | ||||
| removeDuplicateModules(); | removeDuplicateModules(); | ||||
| refreshSubItems(); | refreshSubItems(); | ||||
| } | } | ||||
| String getSelectedItemID() const | |||||
| { | |||||
| for (int i = 0; i < getNumSubItems(); ++i) | |||||
| if (auto* item = getSubItem (i)) | |||||
| if (item->isSelected()) | |||||
| return item->getUniqueName(); | |||||
| return {}; | |||||
| } | |||||
| void setSelectedItem (const String& itemID) | |||||
| { | |||||
| for (int i = 0; i < getNumSubItems(); ++i) | |||||
| { | |||||
| if (auto* item = getSubItem (i)) | |||||
| { | |||||
| if (item->getUniqueName() == itemID) | |||||
| { | |||||
| item->setSelected (true, true); | |||||
| return; | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| //============================================================================== | //============================================================================== | ||||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (EnabledModulesItem) | JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (EnabledModulesItem) | ||||
| }; | }; | ||||
| @@ -165,35 +165,27 @@ public: | |||||
| { | { | ||||
| if (hasAddButton) | if (hasAddButton) | ||||
| { | { | ||||
| addButton.reset (new IconButton ("Add", &getIcons().plus)); | |||||
| addButton = std::make_unique<IconButton> ("Add", getIcons().plus); | |||||
| addAndMakeVisible (addButton.get()); | addAndMakeVisible (addButton.get()); | ||||
| addButton->onClick = [this] { showAddMenu(); }; | addButton->onClick = [this] { showAddMenu(); }; | ||||
| } | } | ||||
| if (hasSettingsButton) | if (hasSettingsButton) | ||||
| { | { | ||||
| settingsButton.reset (new IconButton ("Settings", &getIcons().settings)); | |||||
| settingsButton = std::make_unique<IconButton> ("Settings", getIcons().settings); | |||||
| addAndMakeVisible (settingsButton.get()); | addAndMakeVisible (settingsButton.get()); | ||||
| settingsButton->onClick = [this] { showSettings(); }; | settingsButton->onClick = [this] { showSettings(); }; | ||||
| } | } | ||||
| if (hasFindPanel) | if (hasFindPanel) | ||||
| { | { | ||||
| findPanel.reset (new FindPanel ([this] (const String& filter) { treeToDisplay->rootItem->setSearchFilter (filter); })); | |||||
| findPanel = std::make_unique<FindPanel> ([this] (const String& filter) { treeToDisplay->rootItem->setSearchFilter (filter); }); | |||||
| addAndMakeVisible (findPanel.get()); | addAndMakeVisible (findPanel.get()); | ||||
| } | } | ||||
| addAndMakeVisible (treeToDisplay.get()); | addAndMakeVisible (treeToDisplay.get()); | ||||
| } | } | ||||
| ~ConcertinaTreeComponent() override | |||||
| { | |||||
| treeToDisplay.reset(); | |||||
| addButton.reset(); | |||||
| findPanel.reset(); | |||||
| settingsButton.reset(); | |||||
| } | |||||
| void resized() override | void resized() override | ||||
| { | { | ||||
| auto bounds = getLocalBounds(); | auto bounds = getLocalBounds(); | ||||
| @@ -30,17 +30,19 @@ | |||||
| #include "../../LiveBuildEngine/jucer_CompileEngineClient.h" | #include "../../LiveBuildEngine/jucer_CompileEngineClient.h" | ||||
| //============================================================================== | //============================================================================== | ||||
| HeaderComponent::HeaderComponent() | |||||
| HeaderComponent::HeaderComponent (ProjectContentComponent* pcc) | |||||
| : projectContentComponent (pcc) | |||||
| { | { | ||||
| addAndMakeVisible (configLabel); | addAndMakeVisible (configLabel); | ||||
| addAndMakeVisible (exporterBox); | addAndMakeVisible (exporterBox); | ||||
| exporterBox.onChange = [this] { updateExporterButton(); }; | exporterBox.onChange = [this] { updateExporterButton(); }; | ||||
| juceIcon.reset (new ImageComponent ("icon")); | |||||
| addAndMakeVisible (juceIcon.get()); | |||||
| juceIcon->setImage (ImageCache::getFromMemory (BinaryData::juce_icon_png, BinaryData::juce_icon_pngSize), | |||||
| RectanglePlacement::centred); | |||||
| juceIcon.setImage (ImageCache::getFromMemory (BinaryData::juce_icon_png, BinaryData::juce_icon_pngSize), RectanglePlacement::centred); | |||||
| addAndMakeVisible (juceIcon); | |||||
| addAndMakeVisible (userAvatar); | |||||
| userAvatar.addChangeListener (this); | |||||
| projectNameLabel.setText ({}, dontSendNotification); | projectNameLabel.setText ({}, dontSendNotification); | ||||
| addAndMakeVisible (projectNameLabel); | addAndMakeVisible (projectNameLabel); | ||||
| @@ -52,7 +54,7 @@ HeaderComponent::~HeaderComponent() | |||||
| { | { | ||||
| if (childProcess != nullptr) | if (childProcess != nullptr) | ||||
| { | { | ||||
| childProcess->activityList.removeChangeListener(this); | |||||
| childProcess->activityList.removeChangeListener (this); | |||||
| childProcess->errorList.removeChangeListener (this); | childProcess->errorList.removeChangeListener (this); | ||||
| } | } | ||||
| } | } | ||||
| @@ -63,33 +65,36 @@ void HeaderComponent::resized() | |||||
| auto bounds = getLocalBounds(); | auto bounds = getLocalBounds(); | ||||
| configLabel.setFont ({ bounds.getHeight() / 3.0f }); | configLabel.setFont ({ bounds.getHeight() / 3.0f }); | ||||
| //============================================================================== | |||||
| { | { | ||||
| auto headerBounds = bounds.removeFromLeft (tabsWidth); | auto headerBounds = bounds.removeFromLeft (tabsWidth); | ||||
| const int buttonSize = 25; | const int buttonSize = 25; | ||||
| auto buttonBounds = headerBounds.removeFromRight (buttonSize); | auto buttonBounds = headerBounds.removeFromRight (buttonSize); | ||||
| projectSettingsButton->setBounds (buttonBounds.removeFromBottom (buttonSize).reduced (2)); | |||||
| projectSettingsButton.setBounds (buttonBounds.removeFromBottom (buttonSize).reduced (2)); | |||||
| juceIcon->setBounds (headerBounds.removeFromLeft (headerBounds.getHeight()).reduced (2)); | |||||
| juceIcon.setBounds (headerBounds.removeFromLeft (headerBounds.getHeight()).reduced (2)); | |||||
| headerBounds.removeFromRight (5); | headerBounds.removeFromRight (5); | ||||
| projectNameLabel.setBounds (headerBounds); | projectNameLabel.setBounds (headerBounds); | ||||
| } | } | ||||
| //============================================================================== | |||||
| auto exporterWidth = jmin (400, bounds.getWidth() / 2); | |||||
| Rectangle<int> exporterBounds (0, 0, exporterWidth, bounds.getHeight()); | |||||
| { | |||||
| auto exporterWidth = jmin (400, bounds.getWidth() / 2); | |||||
| Rectangle<int> exporterBounds (0, 0, exporterWidth, bounds.getHeight()); | |||||
| exporterBounds.setCentre (bounds.getCentre()); | |||||
| exporterBounds.setCentre (bounds.getCentre()); | |||||
| runAppButton.setBounds (exporterBounds.removeFromRight (exporterBounds.getHeight()).reduced (2)); | |||||
| saveAndOpenInIDEButton.setBounds (exporterBounds.removeFromRight (exporterBounds.getHeight()).reduced (2)); | |||||
| runAppButton->setBounds (exporterBounds.removeFromRight (exporterBounds.getHeight()).reduced (2)); | |||||
| saveAndOpenInIDEButton->setBounds (exporterBounds.removeFromRight (exporterBounds.getHeight()).reduced (2)); | |||||
| exporterBounds.removeFromRight (5); | |||||
| exporterBox.setBounds (exporterBounds.removeFromBottom (roundToInt (exporterBounds.getHeight() / 1.8f))); | |||||
| configLabel.setBounds (exporterBounds); | |||||
| } | |||||
| exporterBounds.removeFromRight (5); | |||||
| exporterBox.setBounds (exporterBounds.removeFromBottom (roundToInt (exporterBounds.getHeight() / 1.8f))); | |||||
| configLabel.setBounds (exporterBounds); | |||||
| userAvatar.setBounds (bounds.removeFromRight (userAvatar.isDisplaingGPLLogo() ? roundToInt (bounds.getHeight() * 1.9f) | |||||
| : bounds.getHeight()).reduced (2)); | |||||
| } | } | ||||
| void HeaderComponent::paint (Graphics& g) | void HeaderComponent::paint (Graphics& g) | ||||
| @@ -98,48 +103,53 @@ void HeaderComponent::paint (Graphics& g) | |||||
| if (isBuilding) | if (isBuilding) | ||||
| getLookAndFeel().drawSpinningWaitAnimation (g, findColour (treeIconColourId), | getLookAndFeel().drawSpinningWaitAnimation (g, findColour (treeIconColourId), | ||||
| runAppButton->getX(), runAppButton->getY(), | |||||
| runAppButton->getWidth(), runAppButton->getHeight()); | |||||
| runAppButton.getX(), runAppButton.getY(), | |||||
| runAppButton.getWidth(), runAppButton.getHeight()); | |||||
| } | } | ||||
| //============================================================================== | //============================================================================== | ||||
| void HeaderComponent::setCurrentProject (Project* p) noexcept | |||||
| void HeaderComponent::setCurrentProject (Project* newProject) | |||||
| { | { | ||||
| project = p; | |||||
| exportersTree = project->getExporters(); | |||||
| exportersTree.addListener (this); | |||||
| updateExporters(); | |||||
| projectNameValue.referTo (project->getProjectValue (Ids::name)); | |||||
| projectNameValue.addListener (this); | |||||
| updateName(); | |||||
| isBuilding = false; | isBuilding = false; | ||||
| stopTimer(); | stopTimer(); | ||||
| repaint(); | repaint(); | ||||
| childProcess = ProjucerApplication::getApp().childProcessCache->getExisting (*project); | |||||
| projectNameLabel.setText ({}, dontSendNotification); | |||||
| if (childProcess != nullptr) | |||||
| { | |||||
| childProcess->activityList.addChangeListener (this); | |||||
| childProcess->errorList.addChangeListener (this); | |||||
| project = newProject; | |||||
| runAppButton->setTooltip ({}); | |||||
| runAppButton->setEnabled (true); | |||||
| } | |||||
| else | |||||
| if (project != nullptr) | |||||
| { | { | ||||
| runAppButton->setTooltip ("Enable live-build engine to launch application"); | |||||
| runAppButton->setEnabled (false); | |||||
| exportersTree = project->getExporters(); | |||||
| exportersTree.addListener (this); | |||||
| updateExporters(); | |||||
| projectNameValue.referTo (project->getProjectValue (Ids::name)); | |||||
| projectNameValue.addListener (this); | |||||
| updateName(); | |||||
| childProcess = ProjucerApplication::getApp().childProcessCache->getExisting (*project); | |||||
| if (childProcess != nullptr) | |||||
| { | |||||
| childProcess->activityList.addChangeListener (this); | |||||
| childProcess->errorList.addChangeListener (this); | |||||
| runAppButton.setTooltip ({}); | |||||
| runAppButton.setEnabled (true); | |||||
| } | |||||
| else | |||||
| { | |||||
| runAppButton.setTooltip ("Enable live-build engine to launch application"); | |||||
| runAppButton.setEnabled (false); | |||||
| } | |||||
| } | } | ||||
| } | } | ||||
| //============================================================================== | //============================================================================== | ||||
| void HeaderComponent::updateExporters() noexcept | |||||
| void HeaderComponent::updateExporters() | |||||
| { | { | ||||
| auto selectedName = getSelectedExporterName(); | |||||
| auto selectedExporter = getSelectedExporter(); | |||||
| exporterBox.clear(); | exporterBox.clear(); | ||||
| auto preferredExporterIndex = -1; | auto preferredExporterIndex = -1; | ||||
| @@ -149,7 +159,7 @@ void HeaderComponent::updateExporters() noexcept | |||||
| { | { | ||||
| exporterBox.addItem (exporter->getName(), i + 1); | exporterBox.addItem (exporter->getName(), i + 1); | ||||
| if (selectedName == exporter->getName()) | |||||
| if (selectedExporter != nullptr && exporter->getName() == selectedExporter->getName()) | |||||
| exporterBox.setSelectedId (i + 1); | exporterBox.setSelectedId (i + 1); | ||||
| if (exporter->getName().contains (ProjectExporter::getCurrentPlatformExporterName()) && preferredExporterIndex == -1) | if (exporter->getName().contains (ProjectExporter::getCurrentPlatformExporterName()) && preferredExporterIndex == -1) | ||||
| @@ -177,31 +187,56 @@ void HeaderComponent::updateExporters() noexcept | |||||
| updateExporterButton(); | updateExporterButton(); | ||||
| } | } | ||||
| String HeaderComponent::getSelectedExporterName() const noexcept | |||||
| std::unique_ptr<ProjectExporter> HeaderComponent::getSelectedExporter() const | |||||
| { | { | ||||
| return exporterBox.getItemText (exporterBox.getSelectedItemIndex()); | |||||
| if (project != nullptr) | |||||
| { | |||||
| int i = 0; | |||||
| auto selectedIndex = exporterBox.getSelectedItemIndex(); | |||||
| for (Project::ExporterIterator exporter (*project); exporter.next();) | |||||
| if (i++ == selectedIndex) | |||||
| return std::move (exporter.exporter); | |||||
| } | |||||
| return nullptr; | |||||
| } | } | ||||
| bool HeaderComponent::canCurrentExporterLaunchProject() const noexcept | |||||
| bool HeaderComponent::canCurrentExporterLaunchProject() const | |||||
| { | { | ||||
| for (Project::ExporterIterator exporter (*project); exporter.next();) | |||||
| if (exporter->getName() == getSelectedExporterName() && exporter->canLaunchProject()) | |||||
| return true; | |||||
| if (project != nullptr) | |||||
| { | |||||
| if (auto selectedExporter = getSelectedExporter()) | |||||
| { | |||||
| for (Project::ExporterIterator exporter (*project); exporter.next();) | |||||
| if (exporter->canLaunchProject() && exporter->getName() == selectedExporter->getName()) | |||||
| return true; | |||||
| } | |||||
| } | |||||
| return false; | return false; | ||||
| } | } | ||||
| //============================================================================== | //============================================================================== | ||||
| void HeaderComponent::sidebarTabsWidthChanged (int newWidth) noexcept | |||||
| void HeaderComponent::sidebarTabsWidthChanged (int newWidth) | |||||
| { | { | ||||
| tabsWidth = newWidth; | tabsWidth = newWidth; | ||||
| resized(); | resized(); | ||||
| } | } | ||||
| void HeaderComponent::liveBuildEnablementChanged (bool isEnabled) | |||||
| { | |||||
| runAppButton.setVisible (isEnabled); | |||||
| } | |||||
| //============================================================================== | //============================================================================== | ||||
| void HeaderComponent::changeListenerCallback (ChangeBroadcaster*) | |||||
| void HeaderComponent::changeListenerCallback (ChangeBroadcaster* source) | |||||
| { | { | ||||
| if (childProcess != nullptr) | |||||
| if (source == &userAvatar) | |||||
| { | |||||
| resized(); | |||||
| } | |||||
| else if (childProcess != nullptr && source == &childProcess->activityList) | |||||
| { | { | ||||
| if (childProcess->activityList.getNumActivities() > 0) | if (childProcess->activityList.getNumActivities() > 0) | ||||
| buildPing(); | buildPing(); | ||||
| @@ -221,30 +256,37 @@ void HeaderComponent::timerCallback() | |||||
| } | } | ||||
| //============================================================================== | //============================================================================== | ||||
| void HeaderComponent::initialiseButtons() noexcept | |||||
| void HeaderComponent::initialiseButtons() | |||||
| { | { | ||||
| auto& icons = getIcons(); | |||||
| addAndMakeVisible (projectSettingsButton); | |||||
| projectSettingsButton.onClick = [this] { projectContentComponent->showProjectSettings(); }; | |||||
| projectSettingsButton.reset (new IconButton ("Project Settings", &icons.settings)); | |||||
| addAndMakeVisible (projectSettingsButton.get()); | |||||
| projectSettingsButton->onClick = [this] | |||||
| addAndMakeVisible (saveAndOpenInIDEButton); | |||||
| saveAndOpenInIDEButton.setBackgroundColour (Colours::white); | |||||
| saveAndOpenInIDEButton.setIconInset (7); | |||||
| saveAndOpenInIDEButton.onClick = [this] | |||||
| { | { | ||||
| if (auto* pcc = findParentComponentOfClass<ProjectContentComponent>()) | |||||
| pcc->showProjectSettings(); | |||||
| }; | |||||
| if (project != nullptr) | |||||
| { | |||||
| if (project->hasIncompatibleLicenseTypeAndSplashScreenSetting()) | |||||
| { | |||||
| auto child = project->getProjectMessages().getChildWithName (ProjectMessages::Ids::warning) | |||||
| .getChildWithName (ProjectMessages::Ids::incompatibleLicense); | |||||
| saveAndOpenInIDEButton.reset (new IconButton ("Save and Open in IDE", nullptr)); | |||||
| addAndMakeVisible (saveAndOpenInIDEButton.get()); | |||||
| saveAndOpenInIDEButton->isIDEButton = true; | |||||
| saveAndOpenInIDEButton->onClick = [this] | |||||
| { | |||||
| if (auto* pcc = findParentComponentOfClass<ProjectContentComponent>()) | |||||
| pcc->openInSelectedIDE (true); | |||||
| if (child.isValid()) | |||||
| child.setProperty (ProjectMessages::Ids::isVisible, true, nullptr); | |||||
| } | |||||
| else | |||||
| { | |||||
| if (auto exporter = getSelectedExporter()) | |||||
| project->openProjectInIDE (*exporter, true); | |||||
| } | |||||
| } | |||||
| }; | }; | ||||
| runAppButton.reset (new IconButton ("Run Application", &icons.play)); | |||||
| addAndMakeVisible (runAppButton.get()); | |||||
| runAppButton->onClick = [this] | |||||
| addAndMakeVisible (runAppButton); | |||||
| runAppButton.setIconInset (7); | |||||
| runAppButton.onClick = [this] | |||||
| { | { | ||||
| if (childProcess != nullptr) | if (childProcess != nullptr) | ||||
| childProcess->launchApp(); | childProcess->launchApp(); | ||||
| @@ -253,22 +295,26 @@ void HeaderComponent::initialiseButtons() noexcept | |||||
| updateExporterButton(); | updateExporterButton(); | ||||
| } | } | ||||
| void HeaderComponent::updateName() noexcept | |||||
| void HeaderComponent::updateName() | |||||
| { | { | ||||
| projectNameLabel.setText (project->getDocumentTitle(), dontSendNotification); | |||||
| if (project != nullptr) | |||||
| projectNameLabel.setText (project->getDocumentTitle(), dontSendNotification); | |||||
| } | } | ||||
| void HeaderComponent::updateExporterButton() noexcept | |||||
| void HeaderComponent::updateExporterButton() | |||||
| { | { | ||||
| auto currentExporterName = getSelectedExporterName(); | |||||
| for (auto info : ProjectExporter::getExporterTypes()) | |||||
| if (auto selectedExporter = getSelectedExporter()) | |||||
| { | { | ||||
| if (currentExporterName.contains (info.name)) | |||||
| auto selectedName = selectedExporter->getName(); | |||||
| for (auto info : ProjectExporter::getExporterTypes()) | |||||
| { | { | ||||
| saveAndOpenInIDEButton->iconImage = info.getIcon(); | |||||
| saveAndOpenInIDEButton->repaint(); | |||||
| saveAndOpenInIDEButton->setEnabled (canCurrentExporterLaunchProject()); | |||||
| if (selectedName.contains (info.name)) | |||||
| { | |||||
| saveAndOpenInIDEButton.setImage (info.getIcon()); | |||||
| saveAndOpenInIDEButton.repaint(); | |||||
| saveAndOpenInIDEButton.setEnabled (canCurrentExporterLaunchProject()); | |||||
| } | |||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| @@ -279,8 +325,8 @@ void HeaderComponent::buildPing() | |||||
| if (! isTimerRunning()) | if (! isTimerRunning()) | ||||
| { | { | ||||
| isBuilding = true; | isBuilding = true; | ||||
| runAppButton->setEnabled (false); | |||||
| runAppButton->setTooltip ("Building..."); | |||||
| runAppButton.setEnabled (false); | |||||
| runAppButton.setTooltip ("Building..."); | |||||
| startTimer (50); | startTimer (50); | ||||
| } | } | ||||
| @@ -306,23 +352,23 @@ void HeaderComponent::setRunAppButtonState (bool buildWasSuccessful) | |||||
| { | { | ||||
| if (childProcess->isAppRunning() || (! childProcess->isAppRunning() && childProcess->canLaunchApp())) | if (childProcess->isAppRunning() || (! childProcess->isAppRunning() && childProcess->canLaunchApp())) | ||||
| { | { | ||||
| runAppButton->setTooltip ("Launch application"); | |||||
| runAppButton.setTooltip ("Launch application"); | |||||
| shouldEnableButton = true; | shouldEnableButton = true; | ||||
| } | } | ||||
| else | else | ||||
| { | { | ||||
| runAppButton->setTooltip ("Application can't be launched"); | |||||
| runAppButton.setTooltip ("Application can't be launched"); | |||||
| } | } | ||||
| } | } | ||||
| else | else | ||||
| { | { | ||||
| runAppButton->setTooltip ("Enable live-build engine to launch application"); | |||||
| runAppButton.setTooltip ("Enable live-build engine to launch application"); | |||||
| } | } | ||||
| } | } | ||||
| else | else | ||||
| { | { | ||||
| runAppButton->setTooltip ("Error building application"); | |||||
| runAppButton.setTooltip ("Error building application"); | |||||
| } | } | ||||
| runAppButton->setEnabled (shouldEnableButton); | |||||
| runAppButton.setEnabled (shouldEnableButton); | |||||
| } | } | ||||
| @@ -20,8 +20,11 @@ | |||||
| #include "../../Application/jucer_Headers.h" | #include "../../Application/jucer_Headers.h" | ||||
| #include "../../Utility/UI/jucer_IconButton.h" | #include "../../Utility/UI/jucer_IconButton.h" | ||||
| #include "jucer_UserAvatarComponent.h" | |||||
| class Project; | class Project; | ||||
| class ProjectContentComponent; | |||||
| class ProjectExporter; | |||||
| class CompileEngineChildProcess; | class CompileEngineChildProcess; | ||||
| //============================================================================== | //============================================================================== | ||||
| @@ -32,7 +35,7 @@ class HeaderComponent : public Component, | |||||
| private Timer | private Timer | ||||
| { | { | ||||
| public: | public: | ||||
| HeaderComponent(); | |||||
| HeaderComponent (ProjectContentComponent* projectContentComponent); | |||||
| ~HeaderComponent() override; | ~HeaderComponent() override; | ||||
| //============================================================================== | //============================================================================== | ||||
| @@ -40,13 +43,14 @@ public: | |||||
| void paint (Graphics&) override; | void paint (Graphics&) override; | ||||
| //============================================================================== | //============================================================================== | ||||
| void setCurrentProject (Project*) noexcept; | |||||
| void setCurrentProject (Project*); | |||||
| void updateExporters() noexcept; | |||||
| String getSelectedExporterName() const noexcept; | |||||
| bool canCurrentExporterLaunchProject() const noexcept; | |||||
| void updateExporters(); | |||||
| std::unique_ptr<ProjectExporter> getSelectedExporter() const; | |||||
| bool canCurrentExporterLaunchProject() const; | |||||
| void sidebarTabsWidthChanged (int newWidth) noexcept; | |||||
| void sidebarTabsWidthChanged (int newWidth); | |||||
| void liveBuildEnablementChanged (bool isEnabled); | |||||
| private: | private: | ||||
| //============================================================================== | //============================================================================== | ||||
| @@ -59,17 +63,17 @@ private: | |||||
| void valueTreeChildRemoved (ValueTree& parentTree, ValueTree&, int) override { updateIfNeeded (parentTree); } | void valueTreeChildRemoved (ValueTree& parentTree, ValueTree&, int) override { updateIfNeeded (parentTree); } | ||||
| void valueTreeChildOrderChanged (ValueTree& parentTree, int, int) override { updateIfNeeded (parentTree); } | void valueTreeChildOrderChanged (ValueTree& parentTree, int, int) override { updateIfNeeded (parentTree); } | ||||
| void updateIfNeeded (ValueTree tree) noexcept | |||||
| void updateIfNeeded (ValueTree tree) | |||||
| { | { | ||||
| if (tree == exportersTree) | if (tree == exportersTree) | ||||
| updateExporters(); | updateExporters(); | ||||
| } | } | ||||
| //============================================================================== | //============================================================================== | ||||
| void initialiseButtons() noexcept; | |||||
| void initialiseButtons(); | |||||
| void updateName() noexcept; | |||||
| void updateExporterButton() noexcept; | |||||
| void updateName(); | |||||
| void updateExporterButton(); | |||||
| //============================================================================== | //============================================================================== | ||||
| void buildPing(); | void buildPing(); | ||||
| @@ -80,17 +84,21 @@ private: | |||||
| int tabsWidth = 200; | int tabsWidth = 200; | ||||
| bool isBuilding = false; | bool isBuilding = false; | ||||
| ProjectContentComponent* projectContentComponent = nullptr; | |||||
| Project* project = nullptr; | Project* project = nullptr; | ||||
| ValueTree exportersTree; | ValueTree exportersTree; | ||||
| Value projectNameValue; | Value projectNameValue; | ||||
| ComboBox exporterBox; | ComboBox exporterBox; | ||||
| Label configLabel { "Config Label", "Selected exporter" }, | |||||
| projectNameLabel; | |||||
| Label configLabel { "Config Label", "Selected exporter" }, projectNameLabel; | |||||
| std::unique_ptr<ImageComponent> juceIcon; | |||||
| std::unique_ptr<IconButton> projectSettingsButton, saveAndOpenInIDEButton, runAppButton; | |||||
| ImageComponent juceIcon; | |||||
| UserAvatarComponent userAvatar { true, true }; | |||||
| IconButton projectSettingsButton { "Project Settings", getIcons().settings }, | |||||
| saveAndOpenInIDEButton { "Save and Open in IDE", Image() }, | |||||
| runAppButton { "Run Application", getIcons().play }; | |||||
| ReferenceCountedObjectPtr<CompileEngineChildProcess> childProcess; | ReferenceCountedObjectPtr<CompileEngineChildProcess> childProcess; | ||||
| @@ -275,13 +275,13 @@ private: | |||||
| m.addItem (PopupMenu::Item ("Copy the paths from the module '" + moduleToCopy + "' to all other modules") | m.addItem (PopupMenu::Item ("Copy the paths from the module '" + moduleToCopy + "' to all other modules") | ||||
| .setAction ([this, moduleToCopy] | .setAction ([this, moduleToCopy] | ||||
| { | { | ||||
| auto& moduleList = project.getEnabledModules(); | |||||
| auto& modulesList = project.getEnabledModules(); | |||||
| for (Project::ExporterIterator exporter (project); exporter.next();) | for (Project::ExporterIterator exporter (project); exporter.next();) | ||||
| { | { | ||||
| for (int i = 0; i < moduleList.getNumModules(); ++i) | |||||
| for (int i = 0; i < modulesList.getNumModules(); ++i) | |||||
| { | { | ||||
| auto modID = moduleList.getModuleID (i); | |||||
| auto modID = modulesList.getModuleID (i); | |||||
| if (modID != moduleToCopy) | if (modID != moduleToCopy) | ||||
| exporter->getPathForModuleValue (modID) = exporter->getPathForModuleValue (moduleToCopy).get(); | exporter->getPathForModuleValue (modID) = exporter->getPathForModuleValue (moduleToCopy).get(); | ||||
| @@ -21,44 +21,50 @@ | |||||
| #include "../../LiveBuildEngine/jucer_DownloadCompileEngineThread.h" | #include "../../LiveBuildEngine/jucer_DownloadCompileEngineThread.h" | ||||
| #include "../../LiveBuildEngine/jucer_CompileEngineSettings.h" | #include "../../LiveBuildEngine/jucer_CompileEngineSettings.h" | ||||
| #include "jucer_HeaderComponent.h" | |||||
| #include "Sidebar/jucer_TabComponents.h" | #include "Sidebar/jucer_TabComponents.h" | ||||
| #include "Sidebar/jucer_ProjectTab.h" | #include "Sidebar/jucer_ProjectTab.h" | ||||
| #include "Sidebar/jucer_LiveBuildTab.h" | #include "Sidebar/jucer_LiveBuildTab.h" | ||||
| NewFileWizard::Type* createGUIComponentWizard(); | |||||
| //============================================================================== | //============================================================================== | ||||
| struct LogoComponent : public Component | |||||
| ProjectContentComponent::LogoComponent::LogoComponent() | |||||
| { | { | ||||
| LogoComponent() | |||||
| { | |||||
| if (auto svg = parseXML (BinaryData::background_logo_svg)) | |||||
| logo = Drawable::createFromSVG (*svg); | |||||
| else | |||||
| jassertfalse; | |||||
| } | |||||
| if (auto svg = parseXML (BinaryData::background_logo_svg)) | |||||
| logo = Drawable::createFromSVG (*svg); | |||||
| } | |||||
| void paint (Graphics& g) override | |||||
| { | |||||
| g.setColour (findColour (defaultTextColourId)); | |||||
| void ProjectContentComponent::LogoComponent::paint (Graphics& g) | |||||
| { | |||||
| g.setColour (findColour (defaultTextColourId)); | |||||
| auto r = getLocalBounds(); | |||||
| auto r = getLocalBounds(); | |||||
| g.setFont (15.0f); | |||||
| g.drawFittedText (getVersionInfo(), r.removeFromBottom (50), Justification::centredBottom, 3); | |||||
| g.setFont (15.0f); | |||||
| g.drawFittedText (getVersionInfo(), r.removeFromBottom (50), Justification::centredBottom, 3); | |||||
| logo->drawWithin (g, r.withTrimmedBottom (r.getHeight() / 4).toFloat(), | |||||
| RectanglePlacement (RectanglePlacement::centred), 1.0f); | |||||
| } | |||||
| logo->drawWithin (g, r.withTrimmedBottom (r.getHeight() / 4).toFloat(), | |||||
| RectanglePlacement (RectanglePlacement::centred), 1.0f); | |||||
| } | |||||
| static String getVersionInfo() | |||||
| { | |||||
| return SystemStats::getJUCEVersion() | |||||
| + newLine | |||||
| + ProjucerApplication::getApp().getVersionDescription(); | |||||
| } | |||||
| String ProjectContentComponent::LogoComponent::getVersionInfo() | |||||
| { | |||||
| return SystemStats::getJUCEVersion() | |||||
| + newLine | |||||
| + ProjucerApplication::getApp().getVersionDescription(); | |||||
| } | |||||
| std::unique_ptr<Drawable> logo; | |||||
| }; | |||||
| //============================================================================== | |||||
| ProjectContentComponent::ContentViewport::ContentViewport (Component* content) | |||||
| { | |||||
| addAndMakeVisible (viewport); | |||||
| viewport.setViewedComponent (content, true); | |||||
| } | |||||
| void ProjectContentComponent::ContentViewport::resized() | |||||
| { | |||||
| viewport.setBounds (getLocalBounds()); | |||||
| } | |||||
| //============================================================================== | //============================================================================== | ||||
| ProjectContentComponent::ProjectContentComponent() | ProjectContentComponent::ProjectContentComponent() | ||||
| @@ -66,15 +72,12 @@ ProjectContentComponent::ProjectContentComponent() | |||||
| setOpaque (true); | setOpaque (true); | ||||
| setWantsKeyboardFocus (true); | setWantsKeyboardFocus (true); | ||||
| logo.reset (new LogoComponent()); | |||||
| addAndMakeVisible (logo.get()); | |||||
| header.reset (new HeaderComponent()); | |||||
| addAndMakeVisible (header.get()); | |||||
| addAndMakeVisible (logoComponent); | |||||
| addAndMakeVisible (headerComponent); | |||||
| addAndMakeVisible (projectMessagesComponent); | |||||
| fileNameLabel.reset (new Label()); | |||||
| addAndMakeVisible (fileNameLabel.get()); | |||||
| fileNameLabel->setJustificationType (Justification::centred); | |||||
| addAndMakeVisible (fileNameLabel); | |||||
| fileNameLabel.setJustificationType (Justification::centred); | |||||
| sidebarSizeConstrainer.setMinimumWidth (200); | sidebarSizeConstrainer.setMinimumWidth (200); | ||||
| sidebarSizeConstrainer.setMaximumWidth (500); | sidebarSizeConstrainer.setMaximumWidth (500); | ||||
| @@ -84,6 +87,10 @@ ProjectContentComponent::ProjectContentComponent() | |||||
| ProjucerApplication::getApp().openDocumentManager.addListener (this); | ProjucerApplication::getApp().openDocumentManager.addListener (this); | ||||
| isLiveBuildEnabled = getGlobalProperties().getBoolValue (Ids::liveBuildEnabled); | |||||
| getGlobalProperties().addChangeListener (this); | |||||
| liveBuildEnablementChanged (isLiveBuildEnabled); | |||||
| Desktop::getInstance().addFocusChangeListener (this); | Desktop::getInstance().addFocusChangeListener (this); | ||||
| startTimer (1600); | startTimer (1600); | ||||
| } | } | ||||
| @@ -93,15 +100,11 @@ ProjectContentComponent::~ProjectContentComponent() | |||||
| Desktop::getInstance().removeFocusChangeListener (this); | Desktop::getInstance().removeFocusChangeListener (this); | ||||
| killChildProcess(); | killChildProcess(); | ||||
| getGlobalProperties().removeChangeListener (this); | |||||
| ProjucerApplication::getApp().openDocumentManager.removeListener (this); | ProjucerApplication::getApp().openDocumentManager.removeListener (this); | ||||
| logo.reset(); | |||||
| header.reset(); | |||||
| setProject (nullptr); | setProject (nullptr); | ||||
| contentView.reset(); | |||||
| fileNameLabel.reset(); | |||||
| removeChildComponent (&bubbleMessage); | removeChildComponent (&bubbleMessage); | ||||
| jassert (getNumChildComponents() <= 1); | |||||
| } | } | ||||
| void ProjectContentComponent::paint (Graphics& g) | void ProjectContentComponent::paint (Graphics& g) | ||||
| @@ -115,11 +118,10 @@ void ProjectContentComponent::resized() | |||||
| r.removeFromRight (10); | r.removeFromRight (10); | ||||
| r.removeFromLeft (15); | r.removeFromLeft (15); | ||||
| r.removeFromBottom (40); | |||||
| r.removeFromTop (5); | r.removeFromTop (5); | ||||
| if (header != nullptr) | |||||
| header->setBounds (r.removeFromTop (40)); | |||||
| projectMessagesComponent.setBounds (r.removeFromBottom (40).withWidth (100).reduced (0, 5)); | |||||
| headerComponent.setBounds (r.removeFromTop (40)); | |||||
| r.removeFromTop (10); | r.removeFromTop (10); | ||||
| @@ -132,19 +134,15 @@ void ProjectContentComponent::resized() | |||||
| if (resizerBar != nullptr) | if (resizerBar != nullptr) | ||||
| resizerBar->setBounds (r.withWidth (4)); | resizerBar->setBounds (r.withWidth (4)); | ||||
| if (auto* h = dynamic_cast<HeaderComponent*> (header.get())) | |||||
| h->sidebarTabsWidthChanged (sidebarTabs.getWidth()); | |||||
| headerComponent.sidebarTabsWidthChanged (sidebarTabs.getWidth()); | |||||
| if (contentView != nullptr) | if (contentView != nullptr) | ||||
| { | { | ||||
| if (fileNameLabel != nullptr && fileNameLabel->isVisible()) | |||||
| fileNameLabel->setBounds (r.removeFromTop (15)); | |||||
| fileNameLabel.setBounds (r.removeFromTop (15)); | |||||
| contentView->setBounds (r); | contentView->setBounds (r); | ||||
| } | } | ||||
| if (logo != nullptr) | |||||
| logo->setBounds (r.reduced (r.getWidth() / 6, r.getHeight() / 6)); | |||||
| logoComponent.setBounds (r.reduced (r.getWidth() / 6, r.getHeight() / 6)); | |||||
| } | } | ||||
| void ProjectContentComponent::lookAndFeelChanged() | void ProjectContentComponent::lookAndFeelChanged() | ||||
| @@ -165,7 +163,7 @@ void ProjectContentComponent::setProject (Project* newProject) | |||||
| { | { | ||||
| if (project != newProject) | if (project != newProject) | ||||
| { | { | ||||
| lastCrashMessage = String(); | |||||
| lastCrashMessage = {}; | |||||
| killChildProcess(); | killChildProcess(); | ||||
| if (project != nullptr) | if (project != nullptr) | ||||
| @@ -179,6 +177,8 @@ void ProjectContentComponent::setProject (Project* newProject) | |||||
| if (project != nullptr) | if (project != nullptr) | ||||
| rebuildProjectTabs(); | rebuildProjectTabs(); | ||||
| projectMessagesComponent.setProject (newProject); | |||||
| } | } | ||||
| } | } | ||||
| @@ -201,20 +201,21 @@ void ProjectContentComponent::createProjectTabs() | |||||
| auto tabColour = Colours::transparentBlack; | auto tabColour = Colours::transparentBlack; | ||||
| auto* pTab = new ProjectTab (project); | |||||
| sidebarTabs.addTab ("Project", tabColour, pTab, true); | |||||
| CompileEngineChildProcess::Ptr childProc (getChildProcess()); | |||||
| sidebarTabs.addTab (getProjectTabName(), tabColour, new ProjectTab (project), true); | |||||
| sidebarTabs.addTab ("Build", tabColour, new LiveBuildTab (childProc, lastCrashMessage), true); | |||||
| if (childProc != nullptr) | |||||
| if (isLiveBuildEnabled) | |||||
| { | { | ||||
| childProc->crashHandler = [this] (const String& m) { this->handleCrash (m); }; | |||||
| CompileEngineChildProcess::Ptr childProc (getChildProcess()); | |||||
| sidebarTabs.addTab (getBuildTabName(), tabColour, new LiveBuildTab (childProc, lastCrashMessage), true); | |||||
| sidebarTabs.getTabbedButtonBar().getTabButton (1)->setExtraComponent (new BuildStatusTabComp (childProc->errorList, | |||||
| childProc->activityList), | |||||
| TabBarButton::afterText); | |||||
| if (childProc != nullptr) | |||||
| { | |||||
| childProc->crashHandler = [this] (const String& m) { this->handleCrash (m); }; | |||||
| sidebarTabs.getTabbedButtonBar().getTabButton (1)->setExtraComponent (new BuildStatusTabComp (childProc->errorList, | |||||
| childProc->activityList), | |||||
| TabBarButton::afterText); | |||||
| } | |||||
| } | } | ||||
| } | } | ||||
| @@ -255,7 +256,12 @@ void ProjectContentComponent::rebuildProjectTabs() | |||||
| sidebarTabs.setBounds (0, 0, lastTreeWidth, getHeight()); | sidebarTabs.setBounds (0, 0, lastTreeWidth, getHeight()); | ||||
| sidebarTabs.setCurrentTabIndex (settings.getValue ("lastViewedTabIndex", "0").getIntValue()); | |||||
| auto lastTabIndex = settings.getValue ("lastViewedTabIndex", "0").getIntValue(); | |||||
| if (lastTabIndex >= sidebarTabs.getNumTabs()) | |||||
| lastTabIndex = 0; | |||||
| sidebarTabs.setCurrentTabIndex (lastTabIndex); | |||||
| auto* projectTab = getProjectTab(); | auto* projectTab = getProjectTab(); | ||||
| for (int i = 2; i >= 0; --i) | for (int i = 2; i >= 0; --i) | ||||
| @@ -272,16 +278,13 @@ void ProjectContentComponent::rebuildProjectTabs() | |||||
| updateMissingFileStatuses(); | updateMissingFileStatuses(); | ||||
| if (auto* h = dynamic_cast<HeaderComponent*> (header.get())) | |||||
| { | |||||
| h->setVisible (true); | |||||
| h->setCurrentProject (project); | |||||
| } | |||||
| headerComponent.setVisible (true); | |||||
| headerComponent.setCurrentProject (project); | |||||
| } | } | ||||
| else | else | ||||
| { | { | ||||
| sidebarTabs.setVisible (false); | sidebarTabs.setVisible (false); | ||||
| header->setVisible (false); | |||||
| headerComponent.setVisible (false); | |||||
| } | } | ||||
| resized(); | resized(); | ||||
| @@ -320,9 +323,19 @@ bool ProjectContentComponent::documentAboutToClose (OpenDocumentManager::Documen | |||||
| return true; | return true; | ||||
| } | } | ||||
| void ProjectContentComponent::changeListenerCallback (ChangeBroadcaster*) | |||||
| void ProjectContentComponent::changeListenerCallback (ChangeBroadcaster* broadcaster) | |||||
| { | { | ||||
| updateMissingFileStatuses(); | |||||
| if (broadcaster == project) | |||||
| { | |||||
| updateMissingFileStatuses(); | |||||
| } | |||||
| else if (broadcaster == &getGlobalProperties()) | |||||
| { | |||||
| auto isEnabled = ProjucerApplication::getApp().isLiveBuildEnabled(); | |||||
| if (isLiveBuildEnabled != isEnabled) | |||||
| liveBuildEnablementChanged (isEnabled); | |||||
| } | |||||
| } | } | ||||
| void ProjectContentComponent::refreshProjectTreeFileStatuses() | void ProjectContentComponent::refreshProjectTreeFileStatuses() | ||||
| @@ -344,8 +357,7 @@ bool ProjectContentComponent::showEditorForFile (const File& f, bool grabFocus) | |||||
| if (getCurrentFile() == f | if (getCurrentFile() == f | ||||
| || showDocument (ProjucerApplication::getApp().openDocumentManager.openFile (project, f), grabFocus)) | || showDocument (ProjucerApplication::getApp().openDocumentManager.openFile (project, f), grabFocus)) | ||||
| { | { | ||||
| fileNameLabel->setText (f.getFileName(), dontSendNotification); | |||||
| fileNameLabel.setText (f.getFileName(), dontSendNotification); | |||||
| return true; | return true; | ||||
| } | } | ||||
| @@ -394,8 +406,7 @@ void ProjectContentComponent::hideEditor() | |||||
| currentDocument = nullptr; | currentDocument = nullptr; | ||||
| contentView.reset(); | contentView.reset(); | ||||
| if (fileNameLabel != nullptr) | |||||
| fileNameLabel->setVisible (false); | |||||
| fileNameLabel.setVisible (false); | |||||
| ProjucerApplication::getCommandManager().commandStatusChanged(); | ProjucerApplication::getCommandManager().commandStatusChanged(); | ||||
| resized(); | resized(); | ||||
| @@ -425,7 +436,7 @@ bool ProjectContentComponent::setEditorComponent (Component* editor, | |||||
| contentView.reset (viewport); | contentView.reset (viewport); | ||||
| currentDocument = nullptr; | currentDocument = nullptr; | ||||
| fileNameLabel->setVisible (false); | |||||
| fileNameLabel.setVisible (false); | |||||
| addAndMakeVisible (viewport); | addAndMakeVisible (viewport); | ||||
| } | } | ||||
| @@ -433,8 +444,8 @@ bool ProjectContentComponent::setEditorComponent (Component* editor, | |||||
| { | { | ||||
| contentView.reset (editor); | contentView.reset (editor); | ||||
| currentDocument = doc; | currentDocument = doc; | ||||
| fileNameLabel->setText (doc->getFile().getFileName(), dontSendNotification); | |||||
| fileNameLabel->setVisible (true); | |||||
| fileNameLabel.setText (doc->getFile().getFileName(), dontSendNotification); | |||||
| fileNameLabel.setVisible (true); | |||||
| addAndMakeVisible (editor); | addAndMakeVisible (editor); | ||||
| } | } | ||||
| @@ -460,7 +471,8 @@ Component* ProjectContentComponent::getEditorComponentContent() const | |||||
| void ProjectContentComponent::closeDocument() | void ProjectContentComponent::closeDocument() | ||||
| { | { | ||||
| if (currentDocument != nullptr) | if (currentDocument != nullptr) | ||||
| ProjucerApplication::getApp().openDocumentManager.closeDocument (currentDocument, true); | |||||
| ProjucerApplication::getApp().openDocumentManager | |||||
| .closeDocument (currentDocument, OpenDocumentManager::SaveIfNeeded::yes); | |||||
| else if (contentView != nullptr) | else if (contentView != nullptr) | ||||
| if (! goToPreviousFile()) | if (! goToPreviousFile()) | ||||
| hideEditor(); | hideEditor(); | ||||
| @@ -534,15 +546,10 @@ bool ProjectContentComponent::goToCounterpart() | |||||
| return false; | return false; | ||||
| } | } | ||||
| bool ProjectContentComponent::saveProject (bool shouldWait, bool openInIDE) | |||||
| bool ProjectContentComponent::saveProject() | |||||
| { | { | ||||
| if (project != nullptr) | if (project != nullptr) | ||||
| { | |||||
| const ScopedValueSetter<bool> valueSetter (project->shouldWaitAfterSaving, shouldWait, false); | |||||
| project->setOpenInIDEAfterSaving (openInIDE); | |||||
| return (project->save (true, true) == FileBasedDocument::savedOk); | return (project->save (true, true) == FileBasedDocument::savedOk); | ||||
| } | |||||
| return false; | return false; | ||||
| } | } | ||||
| @@ -550,7 +557,7 @@ bool ProjectContentComponent::saveProject (bool shouldWait, bool openInIDE) | |||||
| void ProjectContentComponent::closeProject() | void ProjectContentComponent::closeProject() | ||||
| { | { | ||||
| if (auto* mw = findParentComponentOfClass<MainWindow>()) | if (auto* mw = findParentComponentOfClass<MainWindow>()) | ||||
| mw->closeCurrentProject (true); | |||||
| mw->closeCurrentProject (OpenDocumentManager::SaveIfNeeded::yes); | |||||
| } | } | ||||
| void ProjectContentComponent::showProjectSettings() | void ProjectContentComponent::showProjectSettings() | ||||
| @@ -560,8 +567,8 @@ void ProjectContentComponent::showProjectSettings() | |||||
| void ProjectContentComponent::showCurrentExporterSettings() | void ProjectContentComponent::showCurrentExporterSettings() | ||||
| { | { | ||||
| if (auto* h = dynamic_cast<HeaderComponent*> (header.get())) | |||||
| showExporterSettings (h->getSelectedExporterName()); | |||||
| if (auto selected = headerComponent.getSelectedExporter()) | |||||
| showExporterSettings (selected->getName()); | |||||
| } | } | ||||
| void ProjectContentComponent::showExporterSettings (const String& exporterName) | void ProjectContentComponent::showExporterSettings (const String& exporterName) | ||||
| @@ -573,7 +580,7 @@ void ProjectContentComponent::showExporterSettings (const String& exporterName) | |||||
| if (auto* exportersPanel = getProjectTab()->getExportersTreePanel()) | if (auto* exportersPanel = getProjectTab()->getExportersTreePanel()) | ||||
| { | { | ||||
| if (auto* exporters = dynamic_cast<TreeItemTypes::ExportersTreeRoot*>(exportersPanel->rootItem.get())) | |||||
| if (auto* exporters = dynamic_cast<TreeItemTypes::ExportersTreeRoot*> (exportersPanel->rootItem.get())) | |||||
| { | { | ||||
| for (auto i = exporters->getNumSubItems(); i >= 0; --i) | for (auto i = exporters->getNumSubItems(); i >= 0; --i) | ||||
| { | { | ||||
| @@ -637,29 +644,8 @@ StringArray ProjectContentComponent::getExportersWhichCanLaunch() const | |||||
| void ProjectContentComponent::openInSelectedIDE (bool saveFirst) | void ProjectContentComponent::openInSelectedIDE (bool saveFirst) | ||||
| { | { | ||||
| if (project != nullptr) | if (project != nullptr) | ||||
| { | |||||
| if (auto* headerComp = dynamic_cast<HeaderComponent*> (header.get())) | |||||
| { | |||||
| auto selectedIDE = headerComp->getSelectedExporterName(); | |||||
| for (Project::ExporterIterator exporter (*project); exporter.next();) | |||||
| { | |||||
| if (exporter->canLaunchProject() && exporter->getName().contains (selectedIDE)) | |||||
| { | |||||
| auto tempProject = project->isTemporaryProject(); // store this before saving as it will always be false after | |||||
| if (saveFirst && ! saveProject (exporter->isXcode(), true)) | |||||
| return; | |||||
| if (tempProject) | |||||
| return; | |||||
| exporter->launchProject(); | |||||
| return; | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| if (auto selectedExporter = headerComponent.getSelectedExporter()) | |||||
| project->openProjectInIDE (*selectedExporter, saveFirst); | |||||
| } | } | ||||
| static void newExporterMenuCallback (int result, ProjectContentComponent* comp) | static void newExporterMenuCallback (int result, ProjectContentComponent* comp) | ||||
| @@ -801,7 +787,8 @@ void ProjectContentComponent::getAllCommands (Array <CommandID>& commands) | |||||
| CommandIDs::reinstantiateComp, | CommandIDs::reinstantiateComp, | ||||
| CommandIDs::showWarnings, | CommandIDs::showWarnings, | ||||
| CommandIDs::nextError, | CommandIDs::nextError, | ||||
| CommandIDs::prevError }); | |||||
| CommandIDs::prevError, | |||||
| CommandIDs::addNewGUIFile }); | |||||
| } | } | ||||
| void ProjectContentComponent::getCommandInfo (const CommandID commandID, ApplicationCommandInfo& result) | void ProjectContentComponent::getCommandInfo (const CommandID commandID, ApplicationCommandInfo& result) | ||||
| @@ -822,7 +809,7 @@ void ProjectContentComponent::getCommandInfo (const CommandID commandID, Applica | |||||
| result.setInfo ("Save Project", | result.setInfo ("Save Project", | ||||
| "Saves the current project", | "Saves the current project", | ||||
| CommandCategories::general, 0); | CommandCategories::general, 0); | ||||
| result.setActive (project != nullptr && ! project->isCurrentlySaving()); | |||||
| result.setActive (project != nullptr && ! project->isSaveAndExportDisabled() && ! project->isCurrentlySaving()); | |||||
| result.defaultKeypresses.add ({ 'p', ModifierKeys::commandModifier, 0 }); | result.defaultKeypresses.add ({ 'p', ModifierKeys::commandModifier, 0 }); | ||||
| break; | break; | ||||
| @@ -901,7 +888,7 @@ void ProjectContentComponent::getCommandInfo (const CommandID commandID, Applica | |||||
| result.setInfo ("Show Build Tab", | result.setInfo ("Show Build Tab", | ||||
| "Shows the tab containing the build panel", | "Shows the tab containing the build panel", | ||||
| CommandCategories::general, 0); | CommandCategories::general, 0); | ||||
| result.setActive (project != nullptr); | |||||
| result.setActive (project != nullptr && isLiveBuildEnabled); | |||||
| result.defaultKeypresses.add ({ 'b', cmdCtrl, 0 }); | result.defaultKeypresses.add ({ 'b', cmdCtrl, 0 }); | ||||
| break; | break; | ||||
| @@ -941,14 +928,14 @@ void ProjectContentComponent::getCommandInfo (const CommandID commandID, Applica | |||||
| result.setInfo ("Open in IDE...", | result.setInfo ("Open in IDE...", | ||||
| "Launches the project in an external IDE", | "Launches the project in an external IDE", | ||||
| CommandCategories::general, 0); | CommandCategories::general, 0); | ||||
| result.setActive (ProjectExporter::canProjectBeLaunched (project)); | |||||
| result.setActive (ProjectExporter::canProjectBeLaunched (project) && ! project->isSaveAndExportDisabled()); | |||||
| break; | break; | ||||
| case CommandIDs::saveAndOpenInIDE: | case CommandIDs::saveAndOpenInIDE: | ||||
| result.setInfo ("Save Project and Open in IDE...", | result.setInfo ("Save Project and Open in IDE...", | ||||
| "Saves the project and launches it in an external IDE", | "Saves the project and launches it in an external IDE", | ||||
| CommandCategories::general, 0); | CommandCategories::general, 0); | ||||
| result.setActive (ProjectExporter::canProjectBeLaunched (project) && ! project->isCurrentlySaving()); | |||||
| result.setActive (ProjectExporter::canProjectBeLaunched (project) && ! project->isSaveAndExportDisabled() && ! project->isCurrentlySaving()); | |||||
| result.defaultKeypresses.add ({ 'l', ModifierKeys::commandModifier | ModifierKeys::shiftModifier, 0 }); | result.defaultKeypresses.add ({ 'l', ModifierKeys::commandModifier | ModifierKeys::shiftModifier, 0 }); | ||||
| break; | break; | ||||
| @@ -1055,6 +1042,13 @@ void ProjectContentComponent::getCommandInfo (const CommandID commandID, Applica | |||||
| result.setActive (childProcess != nullptr && ! childProcess->errorList.isEmpty()); | result.setActive (childProcess != nullptr && ! childProcess->errorList.isEmpty()); | ||||
| break; | break; | ||||
| case CommandIDs::addNewGUIFile: | |||||
| result.setInfo ("Add new GUI Component...", | |||||
| "Adds a new GUI Component file to the project", | |||||
| CommandCategories::general, | |||||
| (! ProjucerApplication::getApp().isGUIEditorEnabled() ? ApplicationCommandInfo::isDisabled : 0)); | |||||
| break; | |||||
| default: | default: | ||||
| break; | break; | ||||
| } | } | ||||
| @@ -1131,6 +1125,8 @@ bool ProjectContentComponent::perform (const InvocationInfo& info) | |||||
| case CommandIDs::nextError: showNextError(); break; | case CommandIDs::nextError: showNextError(); break; | ||||
| case CommandIDs::prevError: showPreviousError(); break; | case CommandIDs::prevError: showPreviousError(); break; | ||||
| case CommandIDs::addNewGUIFile: addNewGUIFile(); break; | |||||
| default: | default: | ||||
| return false; | return false; | ||||
| } | } | ||||
| @@ -1175,7 +1171,7 @@ void ProjectContentComponent::setBuildEnabled (bool isEnabled, bool displayError | |||||
| void ProjectContentComponent::cleanAll() | void ProjectContentComponent::cleanAll() | ||||
| { | { | ||||
| lastCrashMessage = String(); | |||||
| lastCrashMessage = {}; | |||||
| if (childProcess != nullptr) | if (childProcess != nullptr) | ||||
| childProcess->cleanAll(); | childProcess->cleanAll(); | ||||
| @@ -1204,9 +1200,11 @@ bool ProjectContentComponent::isBuildEnabled() const | |||||
| void ProjectContentComponent::refreshTabsIfBuildStatusChanged() | void ProjectContentComponent::refreshTabsIfBuildStatusChanged() | ||||
| { | { | ||||
| if (project != nullptr | if (project != nullptr | ||||
| && (sidebarTabs.getNumTabs() < 2 | |||||
| || isBuildEnabled() != isBuildTabEnabled())) | |||||
| && isLiveBuildEnabled | |||||
| && (sidebarTabs.getNumTabs() < 2 || isBuildEnabled() != isBuildTabEnabled())) | |||||
| { | |||||
| rebuildProjectTabs(); | rebuildProjectTabs(); | ||||
| } | |||||
| } | } | ||||
| bool ProjectContentComponent::areWarningsEnabled() const | bool ProjectContentComponent::areWarningsEnabled() const | ||||
| @@ -1261,6 +1259,15 @@ void ProjectContentComponent::reinstantiateLivePreviewWindows() | |||||
| childProcess->reinstantiatePreviews(); | childProcess->reinstantiatePreviews(); | ||||
| } | } | ||||
| void ProjectContentComponent::addNewGUIFile() | |||||
| { | |||||
| if (project != nullptr) | |||||
| { | |||||
| std::unique_ptr<NewFileWizard::Type> wizard (createGUIComponentWizard()); | |||||
| wizard->createNewFile (*project, project->getMainGroup()); | |||||
| } | |||||
| } | |||||
| void ProjectContentComponent::launchApp() | void ProjectContentComponent::launchApp() | ||||
| { | { | ||||
| if (childProcess != nullptr) | if (childProcess != nullptr) | ||||
| @@ -1301,6 +1308,17 @@ void ProjectContentComponent::timerCallback() | |||||
| refreshTabsIfBuildStatusChanged(); | refreshTabsIfBuildStatusChanged(); | ||||
| } | } | ||||
| void ProjectContentComponent::liveBuildEnablementChanged (bool isEnabled) | |||||
| { | |||||
| isLiveBuildEnabled = isEnabled; | |||||
| if (! isLiveBuildEnabled) | |||||
| killChildProcess(); | |||||
| rebuildProjectTabs(); | |||||
| headerComponent.liveBuildEnablementChanged (isLiveBuildEnabled); | |||||
| } | |||||
| bool ProjectContentComponent::isContinuousRebuildEnabled() | bool ProjectContentComponent::isContinuousRebuildEnabled() | ||||
| { | { | ||||
| return project != nullptr && project->getCompileEngineSettings().isContinuousRebuildEnabled(); | return project != nullptr && project->getCompileEngineSettings().isContinuousRebuildEnabled(); | ||||
| @@ -19,11 +19,12 @@ | |||||
| #pragma once | #pragma once | ||||
| #include "../../CodeEditor/jucer_OpenDocumentManager.h" | #include "../../CodeEditor/jucer_OpenDocumentManager.h" | ||||
| #include "jucer_HeaderComponent.h" | |||||
| #include "jucer_ProjectMessagesComponent.h" | |||||
| class CompileEngineChildProcess; | class CompileEngineChildProcess; | ||||
| class ProjectTab; | class ProjectTab; | ||||
| class LiveBuildTab; | class LiveBuildTab; | ||||
| class HeaderComponent; | |||||
| //============================================================================== | //============================================================================== | ||||
| class ProjectContentComponent : public Component, | class ProjectContentComponent : public Component, | ||||
| @@ -39,7 +40,7 @@ public: | |||||
| ~ProjectContentComponent() override; | ~ProjectContentComponent() override; | ||||
| Project* getProject() const noexcept { return project; } | Project* getProject() const noexcept { return project; } | ||||
| virtual void setProject (Project*); | |||||
| void setProject (Project*); | |||||
| void saveTreeViewState(); | void saveTreeViewState(); | ||||
| void saveOpenDocumentList(); | void saveOpenDocumentList(); | ||||
| @@ -67,14 +68,14 @@ public: | |||||
| bool canGoToCounterpart() const; | bool canGoToCounterpart() const; | ||||
| bool goToCounterpart(); | bool goToCounterpart(); | ||||
| bool saveProject (bool shouldWait = false, bool openInIDE = false); | |||||
| bool saveProject(); | |||||
| void closeProject(); | void closeProject(); | ||||
| void openInSelectedIDE (bool saveFirst); | void openInSelectedIDE (bool saveFirst); | ||||
| void showNewExporterMenu(); | void showNewExporterMenu(); | ||||
| void showProjectTab() { sidebarTabs.setCurrentTabIndex (0); } | |||||
| void showBuildTab() { sidebarTabs.setCurrentTabIndex (1); } | |||||
| int getCurrentTabIndex() { return sidebarTabs.getCurrentTabIndex(); } | |||||
| void showProjectTab() { sidebarTabs.setCurrentTabIndex (0); } | |||||
| void showBuildTab() { sidebarTabs.setCurrentTabIndex (1); } | |||||
| int getCurrentTabIndex() { return sidebarTabs.getCurrentTabIndex(); } | |||||
| void showFilesPanel() { showProjectPanel (0); } | void showFilesPanel() { showProjectPanel (0); } | ||||
| void showModulesPanel() { showProjectPanel (1); } | void showModulesPanel() { showProjectPanel (1); } | ||||
| @@ -99,6 +100,7 @@ public: | |||||
| void showNextError(); | void showNextError(); | ||||
| void showPreviousError(); | void showPreviousError(); | ||||
| void reinstantiateLivePreviewWindows(); | void reinstantiateLivePreviewWindows(); | ||||
| void addNewGUIFile(); | |||||
| void showBubbleMessage (Rectangle<int>, const String&); | void showBubbleMessage (Rectangle<int>, const String&); | ||||
| @@ -129,28 +131,31 @@ public: | |||||
| void childBoundsChanged (Component*) override; | void childBoundsChanged (Component*) override; | ||||
| void lookAndFeelChanged() override; | void lookAndFeelChanged() override; | ||||
| String lastCrashMessage; | |||||
| ProjectMessagesComponent& getProjectMessagesComponent() { return projectMessagesComponent; } | |||||
| private: | |||||
| friend HeaderComponent; | |||||
| static String getProjectTabName() { return "Project"; } | |||||
| static String getBuildTabName() { return "Build"; } | |||||
| private: | |||||
| //============================================================================== | //============================================================================== | ||||
| Project* project = nullptr; | |||||
| OpenDocumentManager::Document* currentDocument = nullptr; | |||||
| RecentDocumentList recentDocumentList; | |||||
| std::unique_ptr<Component> logo, translationTool, contentView, header; | |||||
| struct LogoComponent : public Component | |||||
| { | |||||
| LogoComponent(); | |||||
| void paint (Graphics& g) override; | |||||
| static String getVersionInfo(); | |||||
| TabbedComponent sidebarTabs { TabbedButtonBar::TabsAtTop }; | |||||
| std::unique_ptr<ResizableEdgeComponent> resizerBar; | |||||
| ComponentBoundsConstrainer sidebarSizeConstrainer; | |||||
| std::unique_ptr<Drawable> logo; | |||||
| }; | |||||
| BubbleMessageComponent bubbleMessage; | |||||
| ReferenceCountedObjectPtr<CompileEngineChildProcess> childProcess; | |||||
| bool isForeground = false; | |||||
| struct ContentViewport : public Component | |||||
| { | |||||
| ContentViewport (Component* content); | |||||
| void resized() override; | |||||
| std::unique_ptr<Label> fileNameLabel; | |||||
| Viewport viewport; | |||||
| int lastViewedTab = 0; | |||||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ContentViewport) | |||||
| }; | |||||
| //============================================================================== | //============================================================================== | ||||
| bool documentAboutToClose (OpenDocumentManager::Document*) override; | bool documentAboutToClose (OpenDocumentManager::Document*) override; | ||||
| @@ -160,6 +165,8 @@ private: | |||||
| void globalFocusChanged (Component*) override; | void globalFocusChanged (Component*) override; | ||||
| void timerCallback() override; | void timerCallback() override; | ||||
| void liveBuildEnablementChanged (bool isEnabled); | |||||
| bool isContinuousRebuildEnabled(); | bool isContinuousRebuildEnabled(); | ||||
| void setContinuousRebuildEnabled (bool b); | void setContinuousRebuildEnabled (bool b); | ||||
| @@ -178,23 +185,26 @@ private: | |||||
| bool canSelectedProjectBeLaunch(); | bool canSelectedProjectBeLaunch(); | ||||
| //============================================================================== | //============================================================================== | ||||
| struct ContentViewport : public Component | |||||
| { | |||||
| ContentViewport (Component* content) | |||||
| { | |||||
| addAndMakeVisible (viewport); | |||||
| viewport.setViewedComponent (content, true); | |||||
| } | |||||
| Project* project = nullptr; | |||||
| OpenDocumentManager::Document* currentDocument = nullptr; | |||||
| RecentDocumentList recentDocumentList; | |||||
| void resized() override | |||||
| { | |||||
| viewport.setBounds (getLocalBounds()); | |||||
| } | |||||
| LogoComponent logoComponent; | |||||
| HeaderComponent headerComponent { this }; | |||||
| ProjectMessagesComponent projectMessagesComponent; | |||||
| Label fileNameLabel; | |||||
| TabbedComponent sidebarTabs { TabbedButtonBar::TabsAtTop }; | |||||
| std::unique_ptr<ResizableEdgeComponent> resizerBar; | |||||
| ComponentBoundsConstrainer sidebarSizeConstrainer; | |||||
| std::unique_ptr<Component> translationTool, contentView; | |||||
| BubbleMessageComponent bubbleMessage; | |||||
| Viewport viewport; | |||||
| ReferenceCountedObjectPtr<CompileEngineChildProcess> childProcess; | |||||
| String lastCrashMessage; | |||||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ContentViewport) | |||||
| }; | |||||
| bool isForeground = false, isLiveBuildEnabled = false; | |||||
| int lastViewedTab = 0; | |||||
| //============================================================================== | |||||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ProjectContentComponent) | JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ProjectContentComponent) | ||||
| }; | }; | ||||
| @@ -0,0 +1,544 @@ | |||||
| /* | |||||
| ============================================================================== | |||||
| This file is part of the JUCE 6 technical preview. | |||||
| Copyright (c) 2017 - ROLI Ltd. | |||||
| You may use this code under the terms of the GPL v3 | |||||
| (see www.gnu.org/licenses). | |||||
| For this technical preview, this file is not subject to commercial licensing. | |||||
| 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 "../../Application/jucer_CommonHeaders.h" | |||||
| #include "../../Application/jucer_Application.h" | |||||
| //============================================================================== | |||||
| class MessagesPopupWindow : public Component, | |||||
| private ComponentMovementWatcher | |||||
| { | |||||
| public: | |||||
| MessagesPopupWindow (Component& target, Component& parent, Project& project) | |||||
| : ComponentMovementWatcher (&parent), | |||||
| targetComponent (target), | |||||
| parentComponent (parent), | |||||
| messagesListComponent (*this, project) | |||||
| { | |||||
| parentComponent.addAndMakeVisible (this); | |||||
| setAlwaysOnTop (true); | |||||
| addAndMakeVisible (viewport); | |||||
| viewport.setScrollBarsShown (true, false); | |||||
| viewport.setViewedComponent (&messagesListComponent, false); | |||||
| setOpaque (true); | |||||
| } | |||||
| void paint (Graphics& g) override | |||||
| { | |||||
| g.fillAll (findColour (secondaryBackgroundColourId)); | |||||
| } | |||||
| void resized() override | |||||
| { | |||||
| viewport.setBounds (getLocalBounds()); | |||||
| } | |||||
| bool isListShowing() const | |||||
| { | |||||
| return messagesListComponent.getRequiredHeight() > 0; | |||||
| } | |||||
| void updateBounds (bool animate) | |||||
| { | |||||
| auto targetBounds = parentComponent.getLocalArea (&targetComponent, targetComponent.getLocalBounds()); | |||||
| auto height = jmin (messagesListComponent.getRequiredHeight(), maxHeight); | |||||
| auto yPos = jmax (indent, targetBounds.getY() - height); | |||||
| Rectangle<int> bounds (targetBounds.getX(), yPos, | |||||
| jmin (width, parentComponent.getWidth() - targetBounds.getX() - indent), targetBounds.getY() - yPos); | |||||
| auto& animator = Desktop::getInstance().getAnimator(); | |||||
| if (animate) | |||||
| { | |||||
| setBounds (bounds.withY (targetBounds.getY())); | |||||
| animator.animateComponent (this, bounds, 1.0f, 150, false, 1.0, 1.0); | |||||
| } | |||||
| else | |||||
| { | |||||
| if (animator.isAnimating (this)) | |||||
| animator.cancelAnimation (this, false); | |||||
| setBounds (bounds); | |||||
| } | |||||
| } | |||||
| private: | |||||
| //============================================================================== | |||||
| class MessagesListComponent : public Component, | |||||
| private ValueTree::Listener, | |||||
| private AsyncUpdater | |||||
| { | |||||
| public: | |||||
| MessagesListComponent (MessagesPopupWindow& o, Project& currentProject) | |||||
| : owner (o), | |||||
| project (currentProject) | |||||
| { | |||||
| messagesTree = project.getProjectMessages(); | |||||
| messagesTree.addListener (this); | |||||
| setOpaque (true); | |||||
| messagesChanged(); | |||||
| } | |||||
| void resized() override | |||||
| { | |||||
| auto bounds = getLocalBounds(); | |||||
| auto numMessages = messages.size(); | |||||
| for (size_t i = 0; i < numMessages; ++i) | |||||
| { | |||||
| messages[i]->setBounds (bounds.removeFromTop (messageHeight)); | |||||
| if (numMessages > 1 && i != (numMessages - 1)) | |||||
| bounds.removeFromTop (messageSpacing); | |||||
| } | |||||
| } | |||||
| void paint (Graphics& g) override | |||||
| { | |||||
| g.fillAll (findColour (backgroundColourId).contrasting (0.2f)); | |||||
| } | |||||
| int getRequiredHeight() const | |||||
| { | |||||
| auto numMessages = (int) messages.size(); | |||||
| if (numMessages > 0) | |||||
| return (numMessages * messageHeight) + ((numMessages - 1) * messageSpacing); | |||||
| return 0; | |||||
| } | |||||
| void updateSize (int parentWidth) | |||||
| { | |||||
| setSize (parentWidth, getRequiredHeight()); | |||||
| } | |||||
| private: | |||||
| static constexpr int messageHeight = 65; | |||||
| static constexpr int messageSpacing = 2; | |||||
| //============================================================================== | |||||
| struct MessageComponent : public Component | |||||
| { | |||||
| MessageComponent (MessagesListComponent& listComponent, | |||||
| const Identifier& messageToDisplay, | |||||
| std::vector<ProjectMessages::MessageAction> messageActions) | |||||
| : message (messageToDisplay) | |||||
| { | |||||
| for (auto& action : messageActions) | |||||
| { | |||||
| auto button = std::make_unique<TextButton> (action.first); | |||||
| addAndMakeVisible (*button); | |||||
| button->onClick = action.second; | |||||
| buttons.push_back (std::move (button)); | |||||
| } | |||||
| icon = (ProjectMessages::getTypeForMessage (message) == ProjectMessages::Ids::warning ? getIcons().warning : getIcons().info); | |||||
| messageTitleLabel.setText (ProjectMessages::getTitleForMessage (message), dontSendNotification); | |||||
| messageTitleLabel.setFont (Font (11.0f).boldened()); | |||||
| addAndMakeVisible (messageTitleLabel); | |||||
| messageDescriptionLabel.setText (ProjectMessages::getDescriptionForMessage (message), dontSendNotification); | |||||
| messageDescriptionLabel.setFont (Font (11.0f)); | |||||
| messageDescriptionLabel.setJustificationType (Justification::topLeft); | |||||
| addAndMakeVisible (messageDescriptionLabel); | |||||
| dismissButton.setShape (getLookAndFeel().getCrossShape (1.0f), false, true, false); | |||||
| addAndMakeVisible (dismissButton); | |||||
| dismissButton.onClick = [this, &listComponent] | |||||
| { | |||||
| listComponent.messagesTree.getChildWithName (ProjectMessages::getTypeForMessage (message)) | |||||
| .getChildWithName (message) | |||||
| .setProperty (ProjectMessages::Ids::isVisible, false, nullptr); | |||||
| }; | |||||
| } | |||||
| void paint (Graphics& g) override | |||||
| { | |||||
| g.fillAll (findColour (secondaryBackgroundColourId).contrasting (0.1f)); | |||||
| auto bounds = getLocalBounds().reduced (5); | |||||
| g.setColour (findColour (defaultIconColourId)); | |||||
| g.fillPath (icon, icon.getTransformToScaleToFit (bounds.removeFromTop (messageTitleHeight) | |||||
| .removeFromLeft (messageTitleHeight).toFloat(), true)); | |||||
| } | |||||
| void resized() override | |||||
| { | |||||
| auto bounds = getLocalBounds().reduced (5); | |||||
| auto topSlice = bounds.removeFromTop (messageTitleHeight); | |||||
| topSlice.removeFromLeft (messageTitleHeight + 5); | |||||
| topSlice.removeFromRight (5); | |||||
| dismissButton.setBounds (topSlice.removeFromRight (messageTitleHeight)); | |||||
| messageTitleLabel.setBounds (topSlice); | |||||
| bounds.removeFromTop (5); | |||||
| auto numButtons = (int) buttons.size(); | |||||
| if (numButtons > 0) | |||||
| { | |||||
| auto buttonBounds = bounds.removeFromBottom (buttonHeight); | |||||
| auto buttonWidth = roundToInt (buttonBounds.getWidth() / 3.5f); | |||||
| auto requiredWidth = (numButtons * buttonWidth) + ((numButtons - 1) * buttonSpacing); | |||||
| buttonBounds.reduce ((buttonBounds.getWidth() - requiredWidth) / 2, 0); | |||||
| for (auto& b : buttons) | |||||
| { | |||||
| b->setBounds (buttonBounds.removeFromLeft (buttonWidth)); | |||||
| buttonBounds.removeFromLeft (buttonSpacing); | |||||
| } | |||||
| bounds.removeFromBottom (5); | |||||
| } | |||||
| messageDescriptionLabel.setBounds (bounds); | |||||
| } | |||||
| static constexpr int messageTitleHeight = 11; | |||||
| static constexpr int buttonHeight = messageHeight / 4; | |||||
| static constexpr int buttonSpacing = 5; | |||||
| Identifier message; | |||||
| Path icon; | |||||
| Label messageTitleLabel, messageDescriptionLabel; | |||||
| std::vector<std::unique_ptr<TextButton>> buttons; | |||||
| ShapeButton dismissButton { {}, | |||||
| findColour (treeIconColourId), | |||||
| findColour (treeIconColourId).overlaidWith (findColour (defaultHighlightedTextColourId).withAlpha (0.2f)), | |||||
| findColour (treeIconColourId).overlaidWith (findColour (defaultHighlightedTextColourId).withAlpha (0.4f)) }; | |||||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MessageComponent) | |||||
| }; | |||||
| //============================================================================== | |||||
| void valueTreePropertyChanged (ValueTree&, const Identifier&) override { triggerAsyncUpdate(); } | |||||
| void valueTreeChildAdded (ValueTree&, ValueTree&) override { triggerAsyncUpdate(); } | |||||
| void valueTreeChildRemoved (ValueTree&, ValueTree&, int) override { triggerAsyncUpdate(); } | |||||
| void valueTreeChildOrderChanged (ValueTree&, int, int) override { triggerAsyncUpdate(); } | |||||
| void valueTreeParentChanged (ValueTree&) override { triggerAsyncUpdate(); } | |||||
| void valueTreeRedirected (ValueTree&) override { triggerAsyncUpdate(); } | |||||
| void handleAsyncUpdate() override | |||||
| { | |||||
| messagesChanged(); | |||||
| } | |||||
| void messagesChanged() | |||||
| { | |||||
| auto listWasShowing = (getHeight() > 0); | |||||
| auto warningsTree = messagesTree.getChildWithName (ProjectMessages::Ids::warning); | |||||
| auto notificationsTree = messagesTree.getChildWithName (ProjectMessages::Ids::notification); | |||||
| auto removePredicate = [warningsTree, notificationsTree] (std::unique_ptr<MessageComponent>& messageComponent) | |||||
| { | |||||
| for (int i = 0; i < warningsTree.getNumChildren(); ++i) | |||||
| { | |||||
| auto child = warningsTree.getChild (i); | |||||
| if (child.getType() == messageComponent->message | |||||
| && child.getProperty (ProjectMessages::Ids::isVisible)) | |||||
| { | |||||
| return false; | |||||
| } | |||||
| } | |||||
| for (int i = 0; i < notificationsTree.getNumChildren(); ++i) | |||||
| { | |||||
| auto child = notificationsTree.getChild (i); | |||||
| if (child.getType() == messageComponent->message | |||||
| && child.getProperty (ProjectMessages::Ids::isVisible)) | |||||
| { | |||||
| return false; | |||||
| } | |||||
| } | |||||
| return true; | |||||
| }; | |||||
| messages.erase (std::remove_if (std::begin (messages), std::end (messages), removePredicate), | |||||
| std::end (messages)); | |||||
| for (int i = 0; i < warningsTree.getNumChildren(); ++i) | |||||
| { | |||||
| auto child = warningsTree.getChild (i); | |||||
| if (! child.getProperty (ProjectMessages::Ids::isVisible)) | |||||
| continue; | |||||
| if (std::find_if (std::begin (messages), std::end (messages), | |||||
| [child] (const std::unique_ptr<MessageComponent>& messageComponent) { return messageComponent->message == child.getType(); }) | |||||
| == std::end (messages)) | |||||
| { | |||||
| messages.push_back (std::make_unique<MessageComponent> (*this, child.getType(), project.getMessageActions (child.getType()))); | |||||
| addAndMakeVisible (*messages.back()); | |||||
| } | |||||
| } | |||||
| for (int i = 0; i < notificationsTree.getNumChildren(); ++i) | |||||
| { | |||||
| auto child = notificationsTree.getChild (i); | |||||
| if (! child.getProperty (ProjectMessages::Ids::isVisible)) | |||||
| continue; | |||||
| if (std::find_if (std::begin (messages), std::end (messages), | |||||
| [child] (const std::unique_ptr<MessageComponent>& messageComponent) { return messageComponent->message == child.getType(); }) | |||||
| == std::end (messages)) | |||||
| { | |||||
| messages.push_back (std::make_unique<MessageComponent> (*this, child.getType(), project.getMessageActions (child.getType()))); | |||||
| addAndMakeVisible (*messages.back()); | |||||
| } | |||||
| } | |||||
| auto isNowShowing = (messages.size() > 0); | |||||
| owner.updateBounds (isNowShowing != listWasShowing); | |||||
| updateSize (owner.getWidth()); | |||||
| } | |||||
| //============================================================================== | |||||
| MessagesPopupWindow& owner; | |||||
| Project& project; | |||||
| ValueTree messagesTree; | |||||
| std::vector<std::unique_ptr<MessageComponent>> messages; | |||||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MessagesListComponent) | |||||
| }; | |||||
| //============================================================================== | |||||
| void componentMovedOrResized (bool, bool) override | |||||
| { | |||||
| if (isListShowing()) | |||||
| updateBounds (false); | |||||
| } | |||||
| using ComponentMovementWatcher::componentMovedOrResized; | |||||
| void componentPeerChanged() override | |||||
| { | |||||
| if (isListShowing()) | |||||
| updateBounds (false); | |||||
| } | |||||
| void componentVisibilityChanged() override | |||||
| { | |||||
| if (isListShowing()) | |||||
| updateBounds (false); | |||||
| } | |||||
| using ComponentMovementWatcher::componentVisibilityChanged; | |||||
| //============================================================================== | |||||
| static constexpr int maxHeight = 500, width = 350, indent = 20; | |||||
| Component& targetComponent; | |||||
| Component& parentComponent; | |||||
| Viewport viewport; | |||||
| MessagesListComponent messagesListComponent; | |||||
| //============================================================================== | |||||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MessagesPopupWindow) | |||||
| }; | |||||
| //============================================================================== | |||||
| class ProjectMessagesComponent : public Component | |||||
| { | |||||
| public: | |||||
| ProjectMessagesComponent() | |||||
| { | |||||
| addAndMakeVisible (warningsComponent); | |||||
| addAndMakeVisible (notificationsComponent); | |||||
| warningsComponent.addMouseListener (this, true); | |||||
| notificationsComponent.addMouseListener (this, true); | |||||
| setOpaque (true); | |||||
| } | |||||
| //============================================================================== | |||||
| void resized() override | |||||
| { | |||||
| auto b = getLocalBounds(); | |||||
| warningsComponent.setBounds (b.removeFromLeft (b.getWidth() / 2).reduced (5)); | |||||
| notificationsComponent.setBounds (b.reduced (5)); | |||||
| } | |||||
| void paint (Graphics& g) override | |||||
| { | |||||
| auto backgroundColour = findColour (backgroundColourId); | |||||
| if (isMouseDown || isMouseOver) | |||||
| backgroundColour = backgroundColour.overlaidWith (findColour (defaultHighlightColourId) | |||||
| .withAlpha (isMouseDown ? 1.0f : 0.8f)); | |||||
| g.fillAll (backgroundColour); | |||||
| } | |||||
| //============================================================================== | |||||
| void mouseEnter (const MouseEvent&) override | |||||
| { | |||||
| isMouseOver = true; | |||||
| repaint(); | |||||
| } | |||||
| void mouseExit (const MouseEvent&) override | |||||
| { | |||||
| isMouseOver = false; | |||||
| repaint(); | |||||
| } | |||||
| void mouseDown (const MouseEvent&) override | |||||
| { | |||||
| isMouseDown = true; | |||||
| repaint(); | |||||
| } | |||||
| void mouseUp (const MouseEvent&) override | |||||
| { | |||||
| isMouseDown = false; | |||||
| repaint(); | |||||
| if (messagesWindow != nullptr) | |||||
| showOrHideAllMessages (! messagesWindow->isListShowing()); | |||||
| } | |||||
| //============================================================================== | |||||
| void setProject (Project* newProject) | |||||
| { | |||||
| if (currentProject != newProject) | |||||
| { | |||||
| currentProject = newProject; | |||||
| if (currentProject != nullptr) | |||||
| { | |||||
| auto* projectWindow = ProjucerApplication::getApp().mainWindowList.getMainWindowForFile (currentProject->getFile()); | |||||
| jassert (projectWindow != nullptr); | |||||
| messagesWindow = std::make_unique<MessagesPopupWindow> (*this, *projectWindow, *currentProject); | |||||
| auto projectMessagesTree = currentProject->getProjectMessages(); | |||||
| warningsComponent.setTree (projectMessagesTree.getChildWithName (ProjectMessages::Ids::warning)); | |||||
| notificationsComponent.setTree (projectMessagesTree.getChildWithName (ProjectMessages::Ids::notification)); | |||||
| } | |||||
| else | |||||
| { | |||||
| warningsComponent.setTree ({}); | |||||
| notificationsComponent.setTree ({}); | |||||
| } | |||||
| } | |||||
| } | |||||
| private: | |||||
| //============================================================================== | |||||
| struct MessageCountComponent : public Component, | |||||
| public ValueTree::Listener | |||||
| { | |||||
| MessageCountComponent (ProjectMessagesComponent& o, Path pathToUse) | |||||
| : owner (o), | |||||
| path (pathToUse) | |||||
| { | |||||
| setInterceptsMouseClicks (false, false); | |||||
| } | |||||
| void paint (Graphics& g) override | |||||
| { | |||||
| auto b = getLocalBounds().toFloat(); | |||||
| g.setColour (findColour ((owner.isMouseDown || owner.isMouseOver) ? defaultHighlightedTextColourId : treeIconColourId)); | |||||
| g.fillPath (path, path.getTransformToScaleToFit (b.removeFromLeft (b.getWidth() / 2.0f), true)); | |||||
| b.removeFromLeft (5); | |||||
| g.drawFittedText (String (numMessages), b.getSmallestIntegerContainer(), Justification::centredLeft, 1); | |||||
| } | |||||
| void setTree (ValueTree tree) | |||||
| { | |||||
| messagesTree = tree; | |||||
| if (messagesTree.isValid()) | |||||
| messagesTree.addListener (this); | |||||
| updateNumMessages(); | |||||
| } | |||||
| void valueTreeChildAdded (ValueTree&, ValueTree&) override { updateNumMessages(); } | |||||
| void valueTreeChildRemoved (ValueTree&, ValueTree&, int) override { updateNumMessages(); } | |||||
| void updateNumMessages() | |||||
| { | |||||
| numMessages = messagesTree.getNumChildren(); | |||||
| repaint(); | |||||
| } | |||||
| ProjectMessagesComponent& owner; | |||||
| ValueTree messagesTree; | |||||
| Path path; | |||||
| int numMessages = 0; | |||||
| }; | |||||
| void showOrHideAllMessages (bool shouldBeVisible) | |||||
| { | |||||
| if (currentProject != nullptr) | |||||
| { | |||||
| auto messagesTree = currentProject->getProjectMessages(); | |||||
| auto setVisible = [shouldBeVisible] (ValueTree subTree) | |||||
| { | |||||
| for (int i = 0; i < subTree.getNumChildren(); ++i) | |||||
| subTree.getChild (i).setProperty (ProjectMessages::Ids::isVisible, shouldBeVisible, nullptr); | |||||
| }; | |||||
| setVisible (messagesTree.getChildWithName (ProjectMessages::Ids::warning)); | |||||
| setVisible (messagesTree.getChildWithName (ProjectMessages::Ids::notification)); | |||||
| } | |||||
| } | |||||
| //============================================================================== | |||||
| Project* currentProject = nullptr; | |||||
| bool isMouseOver = false, isMouseDown = false; | |||||
| MessageCountComponent warningsComponent { *this, getIcons().warning }, | |||||
| notificationsComponent { *this, getIcons().info }; | |||||
| std::unique_ptr<MessagesPopupWindow> messagesWindow; | |||||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ProjectMessagesComponent) | |||||
| }; | |||||
| @@ -0,0 +1,123 @@ | |||||
| /* | |||||
| ============================================================================== | |||||
| This file is part of the JUCE 6 technical preview. | |||||
| Copyright (c) 2017 - ROLI Ltd. | |||||
| You may use this code under the terms of the GPL v3 | |||||
| (see www.gnu.org/licenses). | |||||
| For this technical preview, this file is not subject to commercial licensing. | |||||
| 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 "../../Application/jucer_Application.h" | |||||
| class UserAvatarComponent : public Component, | |||||
| public SettableTooltipClient, | |||||
| public ChangeBroadcaster, | |||||
| private LicenseController::LicenseStateListener | |||||
| { | |||||
| public: | |||||
| UserAvatarComponent (bool tooltip, bool signIn) | |||||
| : displayTooltip (tooltip), | |||||
| signInOnClick (signIn) | |||||
| { | |||||
| ProjucerApplication::getApp().getLicenseController().addListener (this); | |||||
| licenseStateChanged(); | |||||
| } | |||||
| ~UserAvatarComponent() override | |||||
| { | |||||
| ProjucerApplication::getApp().getLicenseController().removeListener (this); | |||||
| } | |||||
| void paint (Graphics& g) override | |||||
| { | |||||
| auto bounds = getLocalBounds(); | |||||
| if (! isGPL) | |||||
| { | |||||
| bounds = bounds.removeFromRight (bounds.getHeight()); | |||||
| Path ellipse; | |||||
| ellipse.addEllipse (bounds.toFloat()); | |||||
| g.reduceClipRegion (ellipse); | |||||
| } | |||||
| g.drawImage (userAvatarImage, bounds.toFloat(), RectanglePlacement::fillDestination); | |||||
| } | |||||
| void mouseUp (const MouseEvent&) override | |||||
| { | |||||
| if (signInOnClick) | |||||
| { | |||||
| PopupMenu menu; | |||||
| menu.addCommandItem (ProjucerApplication::getApp().commandManager.get(), CommandIDs::loginLogout); | |||||
| menu.showMenuAsync (PopupMenu::Options().withTargetComponent (this)); | |||||
| } | |||||
| } | |||||
| bool isDisplaingGPLLogo() const noexcept { return isGPL; } | |||||
| private: | |||||
| Image createDefaultAvatarImage() | |||||
| { | |||||
| Image image (Image::ARGB, 250, 250, true); | |||||
| Graphics g (image); | |||||
| g.setColour (findColour (defaultButtonBackgroundColourId)); | |||||
| g.fillAll(); | |||||
| g.setColour (findColour (defaultIconColourId)); | |||||
| auto path = getIcons().user; | |||||
| g.fillPath (path, RectanglePlacement (RectanglePlacement::centred) | |||||
| .getTransformToFit (path.getBounds(), image.getBounds().reduced (image.getHeight() / 5).toFloat())); | |||||
| return image; | |||||
| } | |||||
| void licenseStateChanged() override | |||||
| { | |||||
| auto state = ProjucerApplication::getApp().getLicenseController().getCurrentState(); | |||||
| isGPL = ProjucerApplication::getApp().getLicenseController().getCurrentState().isGPL(); | |||||
| if (displayTooltip) | |||||
| { | |||||
| auto formattedUserString = [state]() -> String | |||||
| { | |||||
| if (state.isValid()) | |||||
| return (state.isGPL() ? "" : (state.username + " - ")) + state.getLicenseTypeString(); | |||||
| return "Not logged in"; | |||||
| }(); | |||||
| setTooltip (formattedUserString); | |||||
| } | |||||
| userAvatarImage = state.isValid() ? state.avatar : defaultAvatarImage; | |||||
| repaint(); | |||||
| sendChangeMessage(); | |||||
| } | |||||
| void lookAndFeelChanged() override | |||||
| { | |||||
| defaultAvatarImage = createDefaultAvatarImage(); | |||||
| licenseStateChanged(); | |||||
| repaint(); | |||||
| } | |||||
| Image userAvatarImage, defaultAvatarImage { createDefaultAvatarImage() }; | |||||
| bool isGPL = false, displayTooltip = false, signInOnClick = false; | |||||
| }; | |||||
| @@ -22,17 +22,63 @@ | |||||
| #include "../Application/jucer_Application.h" | #include "../Application/jucer_Application.h" | ||||
| #include "../LiveBuildEngine/jucer_CompileEngineSettings.h" | #include "../LiveBuildEngine/jucer_CompileEngineSettings.h" | ||||
| namespace | |||||
| //============================================================================== | |||||
| Project::ProjectFileModificationPoller::ProjectFileModificationPoller (Project& p) | |||||
| : project (p) | |||||
| { | |||||
| startTimer (250); | |||||
| } | |||||
| void Project::ProjectFileModificationPoller::reset() | |||||
| { | { | ||||
| String makeValid4CC (const String& seed) | |||||
| project.removeProjectMessage (ProjectMessages::Ids::jucerFileModified); | |||||
| showingWarning = false; | |||||
| startTimer (250); | |||||
| } | |||||
| void Project::ProjectFileModificationPoller::timerCallback() | |||||
| { | |||||
| if (project.hasProjectBeenModified()) | |||||
| { | { | ||||
| auto s = build_tools::makeValidIdentifier (seed, false, true, false) + "xxxx"; | |||||
| if (! showingWarning) | |||||
| { | |||||
| project.addProjectMessage (ProjectMessages::Ids::jucerFileModified, | |||||
| { { "Keep", [this] { keepProject(); } }, | |||||
| { "Re-load from disk", [this] { reloadProjectFromDisk(); } }, | |||||
| { "Ignore", [this] { reset(); } } }); | |||||
| return s.substring (0, 1).toUpperCase() | |||||
| + s.substring (1, 4).toLowerCase(); | |||||
| stopTimer(); | |||||
| showingWarning = true; | |||||
| } | |||||
| } | } | ||||
| } | } | ||||
| void Project::ProjectFileModificationPoller::reloadProjectFromDisk() | |||||
| { | |||||
| auto oldTemporaryDirectory = project.getTemporaryDirectory(); | |||||
| auto projectFile = project.getFile(); | |||||
| MessageManager::callAsync ([oldTemporaryDirectory, projectFile] | |||||
| { | |||||
| if (auto* mw = ProjucerApplication::getApp().mainWindowList.getMainWindowForFile (projectFile)) | |||||
| { | |||||
| mw->closeCurrentProject (OpenDocumentManager::SaveIfNeeded::no); | |||||
| mw->openFile (projectFile); | |||||
| if (oldTemporaryDirectory != File()) | |||||
| if (auto* newProject = mw->getProject()) | |||||
| newProject->setTemporaryDirectory (oldTemporaryDirectory); | |||||
| } | |||||
| }); | |||||
| } | |||||
| void Project::ProjectFileModificationPoller::keepProject() | |||||
| { | |||||
| project.saveProject(); | |||||
| reset(); | |||||
| } | |||||
| //============================================================================== | //============================================================================== | ||||
| Project::Project (const File& f) | Project::Project (const File& f) | ||||
| : FileBasedDocument (projectFileExtension, | : FileBasedDocument (projectFileExtension, | ||||
| @@ -48,16 +94,37 @@ Project::Project (const File& f) | |||||
| initialiseMainGroup(); | initialiseMainGroup(); | ||||
| initialiseAudioPluginValues(); | initialiseAudioPluginValues(); | ||||
| exporterPathsModuleList.reset (new AvailableModuleList()); | |||||
| setChangedFlag (false); | setChangedFlag (false); | ||||
| modificationTime = getFile().getLastModificationTime(); | modificationTime = getFile().getLastModificationTime(); | ||||
| auto& app = ProjucerApplication::getApp(); | |||||
| if (! app.isRunningCommandLine) | |||||
| app.getLicenseController().addListener (this); | |||||
| app.getJUCEPathModulesList().addListener (this); | |||||
| app.getUserPathsModulesList().addListener (this); | |||||
| updateJUCEPathWarning(); | |||||
| getGlobalProperties().addChangeListener (this); | |||||
| LatestVersionCheckerAndUpdater::getInstance()->checkForNewVersion (true); | |||||
| } | } | ||||
| Project::~Project() | Project::~Project() | ||||
| { | { | ||||
| projectRoot.removeListener (this); | projectRoot.removeListener (this); | ||||
| ProjucerApplication::getApp().openDocumentManager.closeAllDocumentsUsingProject (*this, false); | |||||
| getGlobalProperties().removeChangeListener (this); | |||||
| auto& app = ProjucerApplication::getApp(); | |||||
| app.openDocumentManager.closeAllDocumentsUsingProject (*this, OpenDocumentManager::SaveIfNeeded::no); | |||||
| if (! app.isRunningCommandLine) | |||||
| app.getLicenseController().removeListener (this); | |||||
| app.getJUCEPathModulesList().removeListener (this); | |||||
| app.getUserPathsModulesList().removeListener (this); | |||||
| } | } | ||||
| const char* Project::projectFileExtension = ".jucer"; | const char* Project::projectFileExtension = ".jucer"; | ||||
| @@ -93,6 +160,8 @@ void Project::updateCompanyNameDependencies() | |||||
| bundleIdentifierValue.setDefault (getDefaultBundleIdentifierString()); | bundleIdentifierValue.setDefault (getDefaultBundleIdentifierString()); | ||||
| pluginAAXIdentifierValue.setDefault (getDefaultAAXIdentifierString()); | pluginAAXIdentifierValue.setDefault (getDefaultAAXIdentifierString()); | ||||
| pluginManufacturerValue.setDefault (getDefaultPluginManufacturerString()); | pluginManufacturerValue.setDefault (getDefaultPluginManufacturerString()); | ||||
| updateLicenseWarning(); | |||||
| } | } | ||||
| void Project::updateProjectSettings() | void Project::updateProjectSettings() | ||||
| @@ -105,7 +174,7 @@ bool Project::setCppVersionFromOldExporterSettings() | |||||
| { | { | ||||
| auto highestLanguageStandard = -1; | auto highestLanguageStandard = -1; | ||||
| for (Project::ExporterIterator exporter (*this); exporter.next();) | |||||
| for (ExporterIterator exporter (*this); exporter.next();) | |||||
| { | { | ||||
| if (exporter->isXcode()) // cpp version was per-build configuration for xcode exporters | if (exporter->isXcode()) // cpp version was per-build configuration for xcode exporters | ||||
| { | { | ||||
| @@ -130,7 +199,7 @@ bool Project::setCppVersionFromOldExporterSettings() | |||||
| { | { | ||||
| if (cppLanguageStandard.toString().containsIgnoreCase ("latest")) | if (cppLanguageStandard.toString().containsIgnoreCase ("latest")) | ||||
| { | { | ||||
| cppStandardValue = "latest"; | |||||
| cppStandardValue = Project::getCppStandardVars().getLast(); | |||||
| return true; | return true; | ||||
| } | } | ||||
| @@ -153,7 +222,7 @@ bool Project::setCppVersionFromOldExporterSettings() | |||||
| void Project::updateDeprecatedProjectSettings() | void Project::updateDeprecatedProjectSettings() | ||||
| { | { | ||||
| for (Project::ExporterIterator exporter (*this); exporter.next();) | |||||
| for (ExporterIterator exporter (*this); exporter.next();) | |||||
| exporter->updateDeprecatedSettings(); | exporter->updateDeprecatedSettings(); | ||||
| } | } | ||||
| @@ -161,7 +230,7 @@ void Project::updateDeprecatedProjectSettingsInteractively() | |||||
| { | { | ||||
| jassert (! ProjucerApplication::getApp().isRunningCommandLine); | jassert (! ProjucerApplication::getApp().isRunningCommandLine); | ||||
| for (Project::ExporterIterator exporter (*this); exporter.next();) | |||||
| for (ExporterIterator exporter (*this); exporter.next();) | |||||
| exporter->updateDeprecatedSettingsInteractively(); | exporter->updateDeprecatedSettingsInteractively(); | ||||
| } | } | ||||
| @@ -226,6 +295,14 @@ void Project::initialiseProjectValues() | |||||
| void Project::initialiseAudioPluginValues() | void Project::initialiseAudioPluginValues() | ||||
| { | { | ||||
| auto makeValid4CC = [] (const String& seed) | |||||
| { | |||||
| auto s = build_tools::makeValidIdentifier (seed, false, true, false) + "xxxx"; | |||||
| return s.substring (0, 1).toUpperCase() | |||||
| + s.substring (1, 4).toLowerCase(); | |||||
| }; | |||||
| pluginFormatsValue.referTo (projectRoot, Ids::pluginFormats, getUndoManager(), | pluginFormatsValue.referTo (projectRoot, Ids::pluginFormats, getUndoManager(), | ||||
| Array<var> (Ids::buildVST3.toString(), Ids::buildAU.toString(), Ids::buildStandalone.toString()), ","); | Array<var> (Ids::buildVST3.toString(), Ids::buildAU.toString(), Ids::buildStandalone.toString()), ","); | ||||
| pluginCharacteristicsValue.referTo (projectRoot, Ids::pluginCharacteristicsValue, getUndoManager(), Array<var> (), ","); | pluginCharacteristicsValue.referTo (projectRoot, Ids::pluginCharacteristicsValue, getUndoManager(), Array<var> (), ","); | ||||
| @@ -259,7 +336,7 @@ void Project::updateOldStyleConfigList() | |||||
| { | { | ||||
| projectRoot.removeChild (deprecatedConfigsList, nullptr); | projectRoot.removeChild (deprecatedConfigsList, nullptr); | ||||
| for (Project::ExporterIterator exporter (*this); exporter.next();) | |||||
| for (ExporterIterator exporter (*this); exporter.next();) | |||||
| { | { | ||||
| if (exporter->getNumConfigurations() == 0) | if (exporter->getNumConfigurations() == 0) | ||||
| { | { | ||||
| @@ -287,7 +364,7 @@ void Project::moveOldPropertyFromProjectToAllExporters (Identifier name) | |||||
| { | { | ||||
| if (projectRoot.hasProperty (name)) | if (projectRoot.hasProperty (name)) | ||||
| { | { | ||||
| for (Project::ExporterIterator exporter (*this); exporter.next();) | |||||
| for (ExporterIterator exporter (*this); exporter.next();) | |||||
| exporter->settings.setProperty (name, projectRoot [name], nullptr); | exporter->settings.setProperty (name, projectRoot [name], nullptr); | ||||
| projectRoot.removeProperty (name, nullptr); | projectRoot.removeProperty (name, nullptr); | ||||
| @@ -325,7 +402,7 @@ void Project::removeDefunctExporters() | |||||
| void Project::updateOldModulePaths() | void Project::updateOldModulePaths() | ||||
| { | { | ||||
| for (Project::ExporterIterator exporter (*this); exporter.next();) | |||||
| for (ExporterIterator exporter (*this); exporter.next();) | |||||
| exporter->updateOldModulePaths(); | exporter->updateOldModulePaths(); | ||||
| } | } | ||||
| @@ -499,32 +576,6 @@ static constexpr int getBuiltJuceVersion() | |||||
| + JUCE_BUILDNUMBER; | + JUCE_BUILDNUMBER; | ||||
| } | } | ||||
| static bool isModuleNewerThanProjucer (const ModuleDescription& module) | |||||
| { | |||||
| return module.getID().startsWith ("juce_") && getJuceVersion (module.getVersion()) > getBuiltJuceVersion(); | |||||
| } | |||||
| void Project::warnAboutOldProjucerVersion() | |||||
| { | |||||
| for (auto& juceModule : ProjucerApplication::getApp().getJUCEPathModuleList().getAllModules()) | |||||
| { | |||||
| if (isModuleNewerThanProjucer ({ juceModule.second })) | |||||
| { | |||||
| 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!"); | |||||
| return; | |||||
| } | |||||
| } | |||||
| } | |||||
| //============================================================================== | //============================================================================== | ||||
| static File lastDocumentOpened; | static File lastDocumentOpened; | ||||
| @@ -560,7 +611,7 @@ Result Project::loadDocument (const File& file) | |||||
| registerRecentFile (file); | registerRecentFile (file); | ||||
| enabledModuleList.reset(); | |||||
| enabledModulesList.reset(); | |||||
| projectRoot = newTree; | projectRoot = newTree; | ||||
| projectRoot.addListener (this); | projectRoot.addListener (this); | ||||
| @@ -582,60 +633,303 @@ Result Project::loadDocument (const File& file) | |||||
| moveOldPropertyFromProjectToAllExporters (Ids::smallIcon); | moveOldPropertyFromProjectToAllExporters (Ids::smallIcon); | ||||
| getEnabledModules().sortAlphabetically(); | getEnabledModules().sortAlphabetically(); | ||||
| compileEngineSettings.reset (new CompileEngineSettings (projectRoot)); | |||||
| exporterPathsModulesList.reset (new AvailableModulesList()); | |||||
| rescanExporterPathModules (! ProjucerApplication::getApp().isRunningCommandLine); | |||||
| exporterPathsModulesList->addListener (this); | |||||
| setCppVersionFromOldExporterSettings(); | setCppVersionFromOldExporterSettings(); | ||||
| updateDeprecatedProjectSettings(); | updateDeprecatedProjectSettings(); | ||||
| setChangedFlag (false); | setChangedFlag (false); | ||||
| if (! ProjucerApplication::getApp().isRunningCommandLine) | |||||
| warnAboutOldProjucerVersion(); | |||||
| compileEngineSettings.reset (new CompileEngineSettings (projectRoot)); | |||||
| exporterPathsModuleList.reset (new AvailableModuleList()); | |||||
| rescanExporterPathModules (! ProjucerApplication::getApp().isRunningCommandLine); | |||||
| updateLicenseWarning(); | |||||
| return Result::ok(); | return Result::ok(); | ||||
| } | } | ||||
| Result Project::saveDocument (const File& file) | Result Project::saveDocument (const File& file) | ||||
| { | { | ||||
| return saveProject (file, false); | |||||
| jassert (file == getFile()); | |||||
| ignoreUnused (file); | |||||
| return saveProject(); | |||||
| } | } | ||||
| Result Project::saveProject (const File& file, bool isCommandLineApp) | |||||
| Result Project::saveProject (ProjectExporter* exporterToSave) | |||||
| { | { | ||||
| if (isSaveAndExportDisabled()) | |||||
| return Result::fail ("Save and export is disabled."); | |||||
| if (isSaving) | if (isSaving) | ||||
| return Result::ok(); | return Result::ok(); | ||||
| if (isTemporaryProject()) | if (isTemporaryProject()) | ||||
| { | { | ||||
| askUserWhereToSaveProject(); | |||||
| saveAndMoveTemporaryProject (false); | |||||
| return Result::ok(); | return Result::ok(); | ||||
| } | } | ||||
| updateProjectSettings(); | updateProjectSettings(); | ||||
| if (! isCommandLineApp) | |||||
| if (! ProjucerApplication::getApp().isRunningCommandLine) | |||||
| { | { | ||||
| ProjucerApplication::getApp().openDocumentManager.saveAll(); | ProjucerApplication::getApp().openDocumentManager.saveAll(); | ||||
| if (! isTemporaryProject()) | if (! isTemporaryProject()) | ||||
| registerRecentFile (file); | |||||
| registerRecentFile (getFile()); | |||||
| } | } | ||||
| const ScopedValueSetter<bool> vs (isSaving, true, false); | const ScopedValueSetter<bool> vs (isSaving, true, false); | ||||
| ProjectSaver saver (*this, file); | |||||
| return saver.save (! isCommandLineApp, shouldWaitAfterSaving, specifiedExporterToSave); | |||||
| ProjectSaver saver (*this); | |||||
| return saver.save (exporterToSave); | |||||
| } | |||||
| Result Project::openProjectInIDE (ProjectExporter& exporterToOpen, bool saveFirst) | |||||
| { | |||||
| for (ExporterIterator exporter (*this); exporter.next();) | |||||
| { | |||||
| if (exporter->canLaunchProject() && exporter->getName() == exporterToOpen.getName()) | |||||
| { | |||||
| if (isTemporaryProject()) | |||||
| { | |||||
| saveAndMoveTemporaryProject (true); | |||||
| return Result::ok(); | |||||
| } | |||||
| if (saveFirst) | |||||
| { | |||||
| auto result = saveProject(); | |||||
| if (! result.wasOk()) | |||||
| return result; | |||||
| } | |||||
| // Workaround for a bug where Xcode thinks the project is invalid if opened immediately | |||||
| // after writing | |||||
| if (saveFirst && exporter->isXcode()) | |||||
| Thread::sleep (1000); | |||||
| exporter->launchProject(); | |||||
| } | |||||
| } | |||||
| return Result::ok(); | |||||
| } | } | ||||
| Result Project::saveResourcesOnly (const File& file) | |||||
| Result Project::saveResourcesOnly() | |||||
| { | { | ||||
| ProjectSaver saver (*this, file); | |||||
| ProjectSaver saver (*this); | |||||
| return saver.saveResourcesOnly(); | return saver.saveResourcesOnly(); | ||||
| } | } | ||||
| bool Project::hasIncompatibleLicenseTypeAndSplashScreenSetting() const | |||||
| { | |||||
| auto companyName = companyNameValue.get().toString(); | |||||
| auto isJUCEProject = (companyName == "ROLI Ltd." || companyName == "JUCE"); | |||||
| return ! ProjucerApplication::getApp().isRunningCommandLine && ! isJUCEProject && ! shouldDisplaySplashScreen() | |||||
| && ! ProjucerApplication::getApp().getLicenseController().getCurrentState().isPaidOrGPL(); | |||||
| } | |||||
| bool Project::isSaveAndExportDisabled() const | |||||
| { | |||||
| return ! ProjucerApplication::getApp().isRunningCommandLine && hasIncompatibleLicenseTypeAndSplashScreenSetting(); | |||||
| } | |||||
| void Project::updateLicenseWarning() | |||||
| { | |||||
| if (hasIncompatibleLicenseTypeAndSplashScreenSetting()) | |||||
| { | |||||
| addProjectMessage (ProjectMessages::Ids::incompatibleLicense, | |||||
| { { "Log in", [this] { ProjucerApplication::getApp().mainWindowList.getMainWindowForFile (getFile())->showLoginFormOverlay(); } }, | |||||
| { "Enable splash screen", [this] { displaySplashScreenValue = true; } } }); | |||||
| } | |||||
| else | |||||
| { | |||||
| removeProjectMessage (ProjectMessages::Ids::incompatibleLicense); | |||||
| } | |||||
| } | |||||
| void Project::updateJUCEPathWarning() | |||||
| { | |||||
| if (ProjucerApplication::getApp().shouldPromptUserAboutIncorrectJUCEPath() | |||||
| && ProjucerApplication::getApp().settings->isJUCEPathIncorrect()) | |||||
| { | |||||
| auto dontAskAgain = [this] | |||||
| { | |||||
| ProjucerApplication::getApp().setShouldPromptUserAboutIncorrectJUCEPath (false); | |||||
| removeProjectMessage (ProjectMessages::Ids::jucePath); | |||||
| }; | |||||
| addProjectMessage (ProjectMessages::Ids::jucePath, | |||||
| { { "Set path", [] { ProjucerApplication::getApp().showPathsWindow (true); } }, | |||||
| { "Ignore", [this] { removeProjectMessage (ProjectMessages::Ids::jucePath); } }, | |||||
| { "Don't ask again", std::move (dontAskAgain) } }); | |||||
| } | |||||
| else | |||||
| { | |||||
| removeProjectMessage (ProjectMessages::Ids::jucePath); | |||||
| } | |||||
| } | |||||
| void Project::updateModuleWarnings() | |||||
| { | |||||
| auto& modules = getEnabledModules(); | |||||
| bool cppStandard = false, missingDependencies = false, oldProjucer = false, moduleNotFound = false; | |||||
| for (auto moduleID : modules.getAllModules()) | |||||
| { | |||||
| if (! cppStandard && modules.doesModuleHaveHigherCppStandardThanProject (moduleID)) | |||||
| cppStandard = true; | |||||
| if (! missingDependencies && ! modules.getExtraDependenciesNeeded (moduleID).isEmpty()) | |||||
| missingDependencies = true; | |||||
| auto info = modules.getModuleInfo (moduleID); | |||||
| if (! oldProjucer && (isJUCEModule (moduleID) && getJuceVersion (info.getVersion()) > getBuiltJuceVersion())) | |||||
| oldProjucer = true; | |||||
| if (! moduleNotFound && ! info.isValid()) | |||||
| moduleNotFound = true; | |||||
| } | |||||
| updateCppStandardWarning (cppStandard); | |||||
| updateMissingModuleDependenciesWarning (missingDependencies); | |||||
| updateOldProjucerWarning (oldProjucer); | |||||
| updateModuleNotFoundWarning (moduleNotFound); | |||||
| } | |||||
| void Project::updateCppStandardWarning (bool showWarning) | |||||
| { | |||||
| if (showWarning) | |||||
| { | |||||
| auto removeModules = [this] | |||||
| { | |||||
| auto& modules = getEnabledModules(); | |||||
| for (auto& module : modules.getModulesWithHigherCppStandardThanProject()) | |||||
| modules.removeModule (module); | |||||
| }; | |||||
| auto updateCppStandard = [this] | |||||
| { | |||||
| cppStandardValue = getEnabledModules().getHighestModuleCppStandard(); | |||||
| }; | |||||
| addProjectMessage (ProjectMessages::Ids::cppStandard, | |||||
| { { "Update project C++ standard" , std::move (updateCppStandard) }, | |||||
| { "Remove module(s)", std::move (removeModules) } }); | |||||
| } | |||||
| else | |||||
| { | |||||
| removeProjectMessage (ProjectMessages::Ids::cppStandard); | |||||
| } | |||||
| } | |||||
| void Project::updateMissingModuleDependenciesWarning (bool showWarning) | |||||
| { | |||||
| if (showWarning) | |||||
| { | |||||
| auto removeModules = [this] | |||||
| { | |||||
| auto& modules = getEnabledModules(); | |||||
| for (auto& mod : modules.getModulesWithMissingDependencies()) | |||||
| modules.removeModule (mod); | |||||
| }; | |||||
| auto addMissingDependencies = [this] | |||||
| { | |||||
| auto& modules = getEnabledModules(); | |||||
| for (auto& mod : modules.getModulesWithMissingDependencies()) | |||||
| modules.tryToFixMissingDependencies (mod); | |||||
| }; | |||||
| addProjectMessage (ProjectMessages::Ids::missingModuleDependencies, | |||||
| { { "Add missing dependencies", std::move (addMissingDependencies) }, | |||||
| { "Remove module(s)", std::move (removeModules) } }); | |||||
| } | |||||
| else | |||||
| { | |||||
| removeProjectMessage (ProjectMessages::Ids::missingModuleDependencies); | |||||
| } | |||||
| } | |||||
| void Project::updateOldProjucerWarning (bool showWarning) | |||||
| { | |||||
| if (showWarning) | |||||
| addProjectMessage (ProjectMessages::Ids::oldProjucer, {}); | |||||
| else | |||||
| removeProjectMessage (ProjectMessages::Ids::oldProjucer); | |||||
| } | |||||
| void Project::updateModuleNotFoundWarning (bool showWarning) | |||||
| { | |||||
| if (showWarning) | |||||
| addProjectMessage (ProjectMessages::Ids::moduleNotFound, {}); | |||||
| else | |||||
| removeProjectMessage (ProjectMessages::Ids::moduleNotFound); | |||||
| } | |||||
| void Project::licenseStateChanged() | |||||
| { | |||||
| updateLicenseWarning(); | |||||
| } | |||||
| void Project::changeListenerCallback (ChangeBroadcaster*) | |||||
| { | |||||
| updateJUCEPathWarning(); | |||||
| } | |||||
| void Project::availableModulesChanged (AvailableModulesList* listThatHasChanged) | |||||
| { | |||||
| if (listThatHasChanged == &ProjucerApplication::getApp().getJUCEPathModulesList()) | |||||
| updateJUCEPathWarning(); | |||||
| updateModuleWarnings(); | |||||
| } | |||||
| void Project::addProjectMessage (const Identifier& messageToAdd, | |||||
| std::vector<ProjectMessages::MessageAction>&& actions) | |||||
| { | |||||
| removeProjectMessage (messageToAdd); | |||||
| messageActions[messageToAdd] = std::move (actions); | |||||
| ValueTree child (messageToAdd); | |||||
| child.setProperty (ProjectMessages::Ids::isVisible, true, nullptr); | |||||
| projectMessages.getChildWithName (ProjectMessages::getTypeForMessage (messageToAdd)).addChild (child, -1, nullptr); | |||||
| } | |||||
| void Project::removeProjectMessage (const Identifier& messageToRemove) | |||||
| { | |||||
| auto subTree = projectMessages.getChildWithName (ProjectMessages::getTypeForMessage (messageToRemove)); | |||||
| auto child = subTree.getChildWithName (messageToRemove); | |||||
| if (child.isValid()) | |||||
| subTree.removeChild (child, nullptr); | |||||
| messageActions.erase (messageToRemove); | |||||
| } | |||||
| std::vector<ProjectMessages::MessageAction> Project::getMessageActions (const Identifier& message) | |||||
| { | |||||
| auto iter = messageActions.find (message); | |||||
| if (iter != messageActions.end()) | |||||
| return iter->second; | |||||
| jassertfalse; | |||||
| return {}; | |||||
| } | |||||
| //============================================================================== | //============================================================================== | ||||
| void Project::setTemporaryDirectory (const File& dir) noexcept | void Project::setTemporaryDirectory (const File& dir) noexcept | ||||
| { | { | ||||
| @@ -645,17 +939,16 @@ void Project::setTemporaryDirectory (const File& dir) noexcept | |||||
| forgetRecentFile (getFile()); | forgetRecentFile (getFile()); | ||||
| } | } | ||||
| void Project::askUserWhereToSaveProject() | |||||
| void Project::saveAndMoveTemporaryProject (bool openInIDE) | |||||
| { | { | ||||
| FileChooser fc ("Save Project"); | FileChooser fc ("Save Project"); | ||||
| fc.browseForDirectory(); | fc.browseForDirectory(); | ||||
| if (fc.getResult().exists()) | |||||
| moveTemporaryDirectory (fc.getResult()); | |||||
| } | |||||
| auto newParentDirectory = fc.getResult(); | |||||
| if (! newParentDirectory.exists()) | |||||
| return; | |||||
| void Project::moveTemporaryDirectory (const File& newParentDirectory) | |||||
| { | |||||
| auto newDirectory = newParentDirectory.getChildFile (tempDirectory.getFileName()); | auto newDirectory = newParentDirectory.getChildFile (tempDirectory.getFileName()); | ||||
| auto oldJucerFileName = getFile().getFileName(); | auto oldJucerFileName = getFile().getFileName(); | ||||
| @@ -670,10 +963,12 @@ void Project::moveTemporaryDirectory (const File& newParentDirectory) | |||||
| { | { | ||||
| Component::SafePointer<MainWindow> safeWindow (window); | Component::SafePointer<MainWindow> safeWindow (window); | ||||
| MessageManager::callAsync ([safeWindow, newDirectory, oldJucerFileName] | |||||
| MessageManager::callAsync ([safeWindow, newDirectory, oldJucerFileName, openInIDE]() mutable | |||||
| { | { | ||||
| if (safeWindow != nullptr) | if (safeWindow != nullptr) | ||||
| safeWindow.getComponent()->moveProject (newDirectory.getChildFile (oldJucerFileName)); | |||||
| safeWindow->moveProject (newDirectory.getChildFile (oldJucerFileName), | |||||
| openInIDE ? MainWindow::OpenInIDE::yes | |||||
| : MainWindow::OpenInIDE::no); | |||||
| }); | }); | ||||
| } | } | ||||
| } | } | ||||
| @@ -724,14 +1019,43 @@ void Project::valueTreePropertyChanged (ValueTree& tree, const Identifier& prope | |||||
| if (shouldWriteLegacyPluginCharacteristicsSettings) | if (shouldWriteLegacyPluginCharacteristicsSettings) | ||||
| writeLegacyPluginCharacteristicsSettings(); | writeLegacyPluginCharacteristicsSettings(); | ||||
| } | } | ||||
| else if (property == Ids::displaySplashScreen) | |||||
| { | |||||
| updateLicenseWarning(); | |||||
| } | |||||
| else if (property == Ids::cppLanguageStandard) | |||||
| { | |||||
| updateModuleWarnings(); | |||||
| } | |||||
| changed(); | changed(); | ||||
| } | } | ||||
| } | } | ||||
| void Project::valueTreeChildAdded (ValueTree&, ValueTree&) { changed(); } | |||||
| void Project::valueTreeChildRemoved (ValueTree&, ValueTree&, int) { changed(); } | |||||
| void Project::valueTreeChildOrderChanged (ValueTree&, int, int) { changed(); } | |||||
| void Project::valueTreeChildAdded (ValueTree& parent, ValueTree& child) | |||||
| { | |||||
| ignoreUnused (parent); | |||||
| if (child.getType() == Ids::MODULE) | |||||
| updateModuleWarnings(); | |||||
| changed(); | |||||
| } | |||||
| void Project::valueTreeChildRemoved (ValueTree& parent, ValueTree& child, int index) | |||||
| { | |||||
| ignoreUnused (parent, index); | |||||
| if (child.getType() == Ids::MODULE) | |||||
| updateModuleWarnings(); | |||||
| changed(); | |||||
| } | |||||
| void Project::valueTreeChildOrderChanged (ValueTree&, int, int) | |||||
| { | |||||
| changed(); | |||||
| } | |||||
| //============================================================================== | //============================================================================== | ||||
| bool Project::hasProjectBeenModified() | bool Project::hasProjectBeenModified() | ||||
| @@ -888,24 +1212,17 @@ void Project::createPropertyEditors (PropertyListBuilder& props) | |||||
| "no such statement will be included. This setting used to be enabled by default, but it " | "no such statement will be included. This setting used to be enabled by default, but it " | ||||
| "is recommended to leave it disabled for new projects."); | "is recommended to leave it disabled for new projects."); | ||||
| { | |||||
| String licenseRequiredTagline ("Required for closed source applications without an Indie or Pro JUCE license"); | |||||
| String licenseRequiredInfo ("In accordance with the terms of the JUCE 5 End-Use License Agreement (www.juce.com/juce-5-licence), " | |||||
| "this option can only be disabled for closed source applications if you have a JUCE Indie or Pro " | |||||
| "license, or are using JUCE under the GPL v3 license."); | |||||
| StringPairArray description; | |||||
| description.set ("Display the JUCE splash screen", "This option controls the display of the standard JUCE splash screen."); | |||||
| props.add (new ChoicePropertyComponent (displaySplashScreenValue, "Display the JUCE Splash Screen (required for closed source applications without an Indie or Pro JUCE license)"), | |||||
| "This option controls the display of the standard JUCE splash screen. " | |||||
| "In accordance with the terms of the JUCE 5 End-Use License Agreement (www.juce.com/juce-5-licence), " | |||||
| "this option can only be disabled for closed source applications if you have a JUCE Indie or Pro " | |||||
| "license, or are using JUCE under the GPL v3 license."); | |||||
| props.add (new ChoicePropertyComponent (displaySplashScreenValue, String ("Display the JUCE Splash Screen") + " (" + licenseRequiredTagline + ")"), | |||||
| description["Display the JUCE splash screen"] + " " + licenseRequiredInfo); | |||||
| } | |||||
| props.add (new ChoicePropertyComponent (splashScreenColourValue, "Splash Screen Colour", | |||||
| { "Dark", "Light" }, | |||||
| { "Dark", "Light" }), | |||||
| props.add (new ChoicePropertyComponentWithEnablement (splashScreenColourValue, displaySplashScreenValue, "Splash Screen Colour", | |||||
| { "Dark", "Light" }, { "Dark", "Light" }), | |||||
| "Choose the colour of the JUCE splash screen."); | "Choose the colour of the JUCE splash screen."); | ||||
| { | { | ||||
| StringArray projectTypeNames; | StringArray projectTypeNames; | ||||
| Array<var> projectTypeCodes; | Array<var> projectTypeCodes; | ||||
| @@ -954,8 +1271,8 @@ void Project::createPropertyEditors (PropertyListBuilder& props) | |||||
| "The namespace containing the binary assets."); | "The namespace containing the binary assets."); | ||||
| props.add (new ChoicePropertyComponent (cppStandardValue, "C++ Language Standard", | props.add (new ChoicePropertyComponent (cppStandardValue, "C++ Language Standard", | ||||
| { "C++11", "C++14", "C++17", "Use Latest" }, | |||||
| { "11", "14", "17", "latest" }), | |||||
| getCppStandardStrings(), | |||||
| getCppStandardVars()), | |||||
| "The standard of the C++ language that will be used for compilation."); | "The standard of the C++ language that will be used for compilation."); | ||||
| props.add (new TextPropertyComponent (preprocessorDefsValue, "Preprocessor Definitions", 32768, true), | props.add (new TextPropertyComponent (preprocessorDefsValue, "Preprocessor Definitions", 32768, true), | ||||
| @@ -1912,12 +2229,12 @@ Array<var> Project::getDefaultRTASCategories() const noexcept | |||||
| } | } | ||||
| //============================================================================== | //============================================================================== | ||||
| EnabledModuleList& Project::getEnabledModules() | |||||
| EnabledModulesList& Project::getEnabledModules() | |||||
| { | { | ||||
| if (enabledModuleList == nullptr) | |||||
| enabledModuleList.reset (new EnabledModuleList (*this, projectRoot.getOrCreateChildWithName (Ids::MODULES, nullptr))); | |||||
| if (enabledModulesList == nullptr) | |||||
| enabledModulesList.reset (new EnabledModulesList (*this, projectRoot.getOrCreateChildWithName (Ids::MODULES, nullptr))); | |||||
| return *enabledModuleList; | |||||
| return *enabledModulesList; | |||||
| } | } | ||||
| static StringArray getModulePathsFromExporters (Project& project, bool onlyThisOS) | static StringArray getModulePathsFromExporters (Project& project, bool onlyThisOS) | ||||
| @@ -1979,37 +2296,38 @@ static Array<File> getExporterModulePathsToScan (Project& project) | |||||
| return files; | return files; | ||||
| } | } | ||||
| AvailableModuleList& Project::getExporterPathsModuleList() | |||||
| AvailableModulesList& Project::getExporterPathsModulesList() | |||||
| { | { | ||||
| return *exporterPathsModuleList; | |||||
| jassert (exporterPathsModulesList != nullptr); | |||||
| return *exporterPathsModulesList; | |||||
| } | } | ||||
| void Project::rescanExporterPathModules (bool async) | void Project::rescanExporterPathModules (bool async) | ||||
| { | { | ||||
| if (async) | if (async) | ||||
| exporterPathsModuleList->scanPathsAsync (getExporterModulePathsToScan (*this)); | |||||
| exporterPathsModulesList->scanPathsAsync (getExporterModulePathsToScan (*this)); | |||||
| else | else | ||||
| exporterPathsModuleList->scanPaths (getExporterModulePathsToScan (*this)); | |||||
| exporterPathsModulesList->scanPaths (getExporterModulePathsToScan (*this)); | |||||
| } | } | ||||
| AvailableModuleList::ModuleIDAndFolder Project::getModuleWithID (const String& id) | |||||
| AvailableModulesList::ModuleIDAndFolder Project::getModuleWithID (const String& id) | |||||
| { | { | ||||
| if (! getEnabledModules().shouldUseGlobalPath (id)) | if (! getEnabledModules().shouldUseGlobalPath (id)) | ||||
| { | { | ||||
| const auto& mod = exporterPathsModuleList->getModuleWithID (id); | |||||
| const auto& mod = exporterPathsModulesList->getModuleWithID (id); | |||||
| if (mod.second != File()) | if (mod.second != File()) | ||||
| return mod; | return mod; | ||||
| } | } | ||||
| const auto& list = (isJUCEModule (id) ? ProjucerApplication::getApp().getJUCEPathModuleList().getAllModules() | |||||
| : ProjucerApplication::getApp().getUserPathsModuleList().getAllModules()); | |||||
| const auto& list = (isJUCEModule (id) ? ProjucerApplication::getApp().getJUCEPathModulesList().getAllModules() | |||||
| : ProjucerApplication::getApp().getUserPathsModulesList().getAllModules()); | |||||
| for (auto& m : list) | for (auto& m : list) | ||||
| if (m.first == id) | if (m.first == id) | ||||
| return m; | return m; | ||||
| return exporterPathsModuleList->getModuleWithID (id); | |||||
| return exporterPathsModulesList->getModuleWithID (id); | |||||
| } | } | ||||
| //============================================================================== | //============================================================================== | ||||
| @@ -18,16 +18,96 @@ | |||||
| #pragma once | #pragma once | ||||
| #include "../Application/UserAccount/jucer_LicenseController.h" | |||||
| #include "Modules/jucer_AvailableModulesList.h" | |||||
| class ProjectExporter; | class ProjectExporter; | ||||
| class LibraryModule; | class LibraryModule; | ||||
| class EnabledModuleList; | |||||
| class AvailableModuleList; | |||||
| class ProjectContentComponent; | |||||
| class EnabledModulesList; | |||||
| class CompileEngineSettings; | class CompileEngineSettings; | ||||
| namespace ProjectMessages | |||||
| { | |||||
| namespace Ids | |||||
| { | |||||
| #define DECLARE_ID(name) static const Identifier name (#name) | |||||
| DECLARE_ID (projectMessages); | |||||
| DECLARE_ID (incompatibleLicense); | |||||
| DECLARE_ID (cppStandard); | |||||
| DECLARE_ID (moduleNotFound); | |||||
| DECLARE_ID (jucePath); | |||||
| DECLARE_ID (jucerFileModified); | |||||
| DECLARE_ID (missingModuleDependencies); | |||||
| DECLARE_ID (oldProjucer); | |||||
| DECLARE_ID (newVersionAvailable); | |||||
| DECLARE_ID (notification); | |||||
| DECLARE_ID (warning); | |||||
| DECLARE_ID (isVisible); | |||||
| #undef DECLARE_ID | |||||
| } | |||||
| inline Identifier getTypeForMessage (const Identifier& message) | |||||
| { | |||||
| if (message == Ids::incompatibleLicense || message == Ids::cppStandard || message == Ids::moduleNotFound | |||||
| || message == Ids::jucePath || message == Ids::jucerFileModified || message == Ids::missingModuleDependencies | |||||
| || message == Ids::oldProjucer) | |||||
| { | |||||
| return Ids::warning; | |||||
| } | |||||
| if (message == Ids::newVersionAvailable) | |||||
| { | |||||
| return Ids::notification; | |||||
| } | |||||
| jassertfalse; | |||||
| return {}; | |||||
| } | |||||
| inline String getTitleForMessage (const Identifier& message) | |||||
| { | |||||
| if (message == Ids::incompatibleLicense) return "Incompatible License and Splash Screen Setting"; | |||||
| if (message == Ids::cppStandard) return "C++ Standard"; | |||||
| if (message == Ids::moduleNotFound) return "Module Not Found"; | |||||
| if (message == Ids::jucePath) return "JUCE Path"; | |||||
| if (message == Ids::jucerFileModified) return "Project File Modified"; | |||||
| if (message == Ids::missingModuleDependencies) return "Missing Module Dependencies"; | |||||
| if (message == Ids::oldProjucer) return "Projucer Out of Date"; | |||||
| if (message == Ids::newVersionAvailable) return "New Version Available"; | |||||
| jassertfalse; | |||||
| return {}; | |||||
| } | |||||
| inline String getDescriptionForMessage (const Identifier& message) | |||||
| { | |||||
| if (message == Ids::incompatibleLicense) return "Save and export is disabled."; | |||||
| if (message == Ids::cppStandard) return "Module(s) have a higher C++ standard requirement than the project."; | |||||
| if (message == Ids::moduleNotFound) return "Module(s) could not be found at the specified paths."; | |||||
| if (message == Ids::jucePath) return "The path to your JUCE folder is incorrect."; | |||||
| if (message == Ids::jucerFileModified) return "The .jucer file has been modified since the last save."; | |||||
| if (message == Ids::missingModuleDependencies) return "Module(s) have missing dependencies."; | |||||
| if (message == Ids::oldProjucer) return "The version of the Projucer you are using is out of date."; | |||||
| if (message == Ids::newVersionAvailable) return "A new version of JUCE is available to download."; | |||||
| jassertfalse; | |||||
| return {}; | |||||
| } | |||||
| using MessageAction = std::pair<String, std::function<void()>>; | |||||
| } | |||||
| //============================================================================== | //============================================================================== | ||||
| class Project : public FileBasedDocument, | class Project : public FileBasedDocument, | ||||
| public ValueTree::Listener | |||||
| public ValueTree::Listener, | |||||
| private LicenseController::LicenseStateListener, | |||||
| private ChangeListener, | |||||
| private AvailableModulesList::Listener | |||||
| { | { | ||||
| public: | public: | ||||
| //============================================================================== | //============================================================================== | ||||
| @@ -35,12 +115,14 @@ public: | |||||
| ~Project() override; | ~Project() override; | ||||
| //============================================================================== | //============================================================================== | ||||
| // FileBasedDocument stuff.. | |||||
| String getDocumentTitle() override; | String getDocumentTitle() override; | ||||
| Result loadDocument (const File& file) override; | Result loadDocument (const File& file) override; | ||||
| Result saveDocument (const File& file) override; | Result saveDocument (const File& file) override; | ||||
| Result saveProject (const File& file, bool isCommandLineApp); | |||||
| Result saveResourcesOnly (const File& file); | |||||
| Result saveProject (ProjectExporter* exporterToSave = nullptr); | |||||
| Result saveResourcesOnly(); | |||||
| Result openProjectInIDE (ProjectExporter& exporterToOpen, bool saveFirst); | |||||
| File getLastDocumentOpened() override; | File getLastDocumentOpened() override; | ||||
| void setLastDocumentOpened (const File& file) override; | void setLastDocumentOpened (const File& file) override; | ||||
| @@ -116,6 +198,9 @@ public: | |||||
| bool shouldDisplaySplashScreen() const { return displaySplashScreenValue.get(); } | bool shouldDisplaySplashScreen() const { return displaySplashScreenValue.get(); } | ||||
| String getSplashScreenColourString() const { return splashScreenColourValue.get(); } | String getSplashScreenColourString() const { return splashScreenColourValue.get(); } | ||||
| static StringArray getCppStandardStrings() { return { "C++11", "C++14", "C++17", "Use Latest" }; } | |||||
| static Array<var> getCppStandardVars() { return { "11", "14", "17", "latest" }; } | |||||
| String getCppStandardString() const { return cppStandardValue.get(); } | String getCppStandardString() const { return cppStandardValue.get(); } | ||||
| StringArray getCompilerFlagSchemes() const; | StringArray getCompilerFlagSchemes() const; | ||||
| @@ -363,9 +448,9 @@ public: | |||||
| bool isConfigFlagEnabled (const String& name, bool defaultIsEnabled = false) const; | bool isConfigFlagEnabled (const String& name, bool defaultIsEnabled = false) const; | ||||
| //============================================================================== | //============================================================================== | ||||
| EnabledModuleList& getEnabledModules(); | |||||
| EnabledModulesList& getEnabledModules(); | |||||
| AvailableModuleList& getExporterPathsModuleList(); | |||||
| AvailableModulesList& getExporterPathsModulesList(); | |||||
| void rescanExporterPathModules (bool async = false); | void rescanExporterPathModules (bool async = false); | ||||
| std::pair<String, File> getModuleWithID (const String&); | std::pair<String, File> getModuleWithID (const String&); | ||||
| @@ -397,22 +482,45 @@ public: | |||||
| String getUniqueTargetFolderSuffixForExporter (const String& exporterName, const String& baseTargetFolder); | String getUniqueTargetFolderSuffixForExporter (const String& exporterName, const String& baseTargetFolder); | ||||
| //============================================================================== | //============================================================================== | ||||
| bool isCurrentlySaving() const noexcept { return isSaving; } | |||||
| bool shouldWaitAfterSaving = false; | |||||
| String specifiedExporterToSave = {}; | |||||
| bool isCurrentlySaving() const noexcept { return isSaving; } | |||||
| //============================================================================== | |||||
| bool isTemporaryProject() const noexcept { return tempDirectory != File(); } | bool isTemporaryProject() const noexcept { return tempDirectory != File(); } | ||||
| File getTemporaryDirectory() const noexcept { return tempDirectory; } | File getTemporaryDirectory() const noexcept { return tempDirectory; } | ||||
| void setTemporaryDirectory (const File&) noexcept; | void setTemporaryDirectory (const File&) noexcept; | ||||
| void setOpenInIDEAfterSaving (bool open) noexcept { openInIDEAfterSaving = open; } | |||||
| bool shouldOpenInIDEAfterSaving() const noexcept { return openInIDEAfterSaving; } | |||||
| //============================================================================== | //============================================================================== | ||||
| CompileEngineSettings& getCompileEngineSettings() { return *compileEngineSettings; } | CompileEngineSettings& getCompileEngineSettings() { return *compileEngineSettings; } | ||||
| //============================================================================== | |||||
| ValueTree getProjectMessages() const { return projectMessages; } | |||||
| void addProjectMessage (const Identifier& messageToAdd, std::vector<ProjectMessages::MessageAction>&& messageActions); | |||||
| void removeProjectMessage (const Identifier& messageToRemove); | |||||
| std::vector<ProjectMessages::MessageAction> getMessageActions (const Identifier& message); | |||||
| //============================================================================== | |||||
| bool hasIncompatibleLicenseTypeAndSplashScreenSetting() const; | |||||
| bool isSaveAndExportDisabled() const; | |||||
| private: | private: | ||||
| //============================================================================== | |||||
| struct ProjectFileModificationPoller : private Timer | |||||
| { | |||||
| ProjectFileModificationPoller (Project& p); | |||||
| private: | |||||
| void timerCallback() override; | |||||
| void reset(); | |||||
| void keepProject(); | |||||
| void reloadProjectFromDisk(); | |||||
| Project& project; | |||||
| bool showingWarning = false; | |||||
| }; | |||||
| //============================================================================== | |||||
| ValueTree projectRoot { Ids::JUCERPROJECT }; | ValueTree projectRoot { Ids::JUCERPROJECT }; | ||||
| ValueWithDefault projectNameValue, projectUIDValue, projectLineFeedValue, projectTypeValue, versionValue, bundleIdentifierValue, companyNameValue, | ValueWithDefault projectNameValue, projectUIDValue, projectLineFeedValue, projectTypeValue, versionValue, bundleIdentifierValue, companyNameValue, | ||||
| @@ -427,8 +535,8 @@ private: | |||||
| //============================================================================== | //============================================================================== | ||||
| std::unique_ptr<CompileEngineSettings> compileEngineSettings; | std::unique_ptr<CompileEngineSettings> compileEngineSettings; | ||||
| std::unique_ptr<EnabledModuleList> enabledModuleList; | |||||
| std::unique_ptr<AvailableModuleList> exporterPathsModuleList; | |||||
| std::unique_ptr<EnabledModulesList> enabledModulesList; | |||||
| std::unique_ptr<AvailableModulesList> exporterPathsModulesList; | |||||
| //============================================================================== | //============================================================================== | ||||
| void updateDeprecatedProjectSettings(); | void updateDeprecatedProjectSettings(); | ||||
| @@ -449,10 +557,8 @@ private: | |||||
| //============================================================================== | //============================================================================== | ||||
| File tempDirectory = {}; | File tempDirectory = {}; | ||||
| bool openInIDEAfterSaving = false; | |||||
| void askUserWhereToSaveProject(); | |||||
| void moveTemporaryDirectory (const File&); | |||||
| void saveAndMoveTemporaryProject (bool openInIDE); | |||||
| bool saveProjectRootToFile(); | bool saveProjectRootToFile(); | ||||
| //============================================================================== | //============================================================================== | ||||
| @@ -481,7 +587,27 @@ private: | |||||
| void moveOldPropertyFromProjectToAllExporters (Identifier name); | void moveOldPropertyFromProjectToAllExporters (Identifier name); | ||||
| void removeDefunctExporters(); | void removeDefunctExporters(); | ||||
| void updateOldModulePaths(); | void updateOldModulePaths(); | ||||
| void warnAboutOldProjucerVersion(); | |||||
| //============================================================================== | |||||
| void licenseStateChanged() override; | |||||
| void changeListenerCallback (ChangeBroadcaster*) override; | |||||
| void availableModulesChanged (AvailableModulesList*) override; | |||||
| void updateLicenseWarning(); | |||||
| void updateJUCEPathWarning(); | |||||
| void updateModuleWarnings(); | |||||
| void updateCppStandardWarning (bool showWarning); | |||||
| void updateMissingModuleDependenciesWarning (bool showWarning); | |||||
| void updateOldProjucerWarning (bool showWarning); | |||||
| void updateModuleNotFoundWarning (bool showWarning); | |||||
| ValueTree projectMessages { ProjectMessages::Ids::projectMessages, {}, | |||||
| { { ProjectMessages::Ids::notification, {} }, { ProjectMessages::Ids::warning, {} } } }; | |||||
| std::map<Identifier, std::vector<ProjectMessages::MessageAction>> messageActions; | |||||
| ProjectFileModificationPoller fileModificationPoller { *this }; | |||||
| //============================================================================== | |||||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Project) | JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Project) | ||||
| }; | }; | ||||
| @@ -16,12 +16,655 @@ | |||||
| ============================================================================== | ============================================================================== | ||||
| */ | */ | ||||
| #include "../Application/jucer_Headers.h" | |||||
| #include "jucer_ProjectSaver.h" | #include "jucer_ProjectSaver.h" | ||||
| #include "jucer_ProjectExport_CLion.h" | #include "jucer_ProjectExport_CLion.h" | ||||
| #include "../Application/jucer_Application.h" | |||||
| static constexpr const char* generatedGroupID = "__jucelibfiles"; | |||||
| static constexpr const char* generatedGroupUID = "__generatedcode__"; | |||||
| //============================================================================== | |||||
| ProjectSaver::ProjectSaver (Project& p) | |||||
| : project (p), | |||||
| generatedCodeFolder (project.getGeneratedCodeFolder()), | |||||
| generatedFilesGroup (Project::Item::createGroup (project, getJuceCodeGroupName(), generatedGroupUID, true)), | |||||
| projectLineFeed (project.getProjectLineFeed()) | |||||
| { | |||||
| generatedFilesGroup.setID (generatedGroupID); | |||||
| } | |||||
| Result ProjectSaver::save (ProjectExporter* exporterToSave) | |||||
| { | |||||
| if (! ProjucerApplication::getApp().isRunningCommandLine) | |||||
| { | |||||
| SaveThreadWithProgressWindow thread (*this, exporterToSave); | |||||
| thread.runThread(); | |||||
| return thread.result; | |||||
| } | |||||
| return saveProject (exporterToSave); | |||||
| } | |||||
| Result ProjectSaver::saveResourcesOnly() | |||||
| { | |||||
| writeBinaryDataFiles(); | |||||
| if (errors.size() > 0) | |||||
| return Result::fail (errors[0]); | |||||
| return Result::ok(); | |||||
| } | |||||
| void ProjectSaver::saveBasicProjectItems (const OwnedArray<LibraryModule>& modules, const String& appConfigUserContent) | |||||
| { | |||||
| writePluginDefines(); | |||||
| writeAppConfigFile (modules, appConfigUserContent); | |||||
| writeBinaryDataFiles(); | |||||
| writeAppHeader (modules); | |||||
| writeModuleCppWrappers (modules); | |||||
| } | |||||
| Result ProjectSaver::saveContentNeededForLiveBuild() | |||||
| { | |||||
| auto modules = getModules(); | |||||
| if (errors.size() == 0) | |||||
| { | |||||
| saveBasicProjectItems (modules, loadUserContentFromAppConfig()); | |||||
| return Result::ok(); | |||||
| } | |||||
| return Result::fail (errors[0]); | |||||
| } | |||||
| Project::Item ProjectSaver::addFileToGeneratedGroup (const File& file) | |||||
| { | |||||
| auto item = generatedFilesGroup.findItemForFile (file); | |||||
| if (item.isValid()) | |||||
| return item; | |||||
| generatedFilesGroup.addFileAtIndex (file, -1, true); | |||||
| return generatedFilesGroup.findItemForFile (file); | |||||
| } | |||||
| bool ProjectSaver::copyFolder (const File& source, const File& dest) | |||||
| { | |||||
| if (source.isDirectory() && dest.createDirectory()) | |||||
| { | |||||
| for (auto& f : source.findChildFiles (File::findFiles, false)) | |||||
| { | |||||
| auto target = dest.getChildFile (f.getFileName()); | |||||
| filesCreated.add (target); | |||||
| if (! f.copyFileTo (target)) | |||||
| return false; | |||||
| } | |||||
| for (auto& f : source.findChildFiles (File::findDirectories, false)) | |||||
| { | |||||
| auto name = f.getFileName(); | |||||
| if (name == ".git" || name == ".svn" || name == ".cvs") | |||||
| continue; | |||||
| if (! copyFolder (f, dest.getChildFile (f.getFileName()))) | |||||
| return false; | |||||
| } | |||||
| return true; | |||||
| } | |||||
| return false; | |||||
| } | |||||
| //============================================================================== | |||||
| Project::Item ProjectSaver::saveGeneratedFile (const String& filePath, const MemoryOutputStream& newData) | |||||
| { | |||||
| if (! generatedCodeFolder.createDirectory()) | |||||
| { | |||||
| addError ("Couldn't create folder: " + generatedCodeFolder.getFullPathName()); | |||||
| return Project::Item (project, {}, false); | |||||
| } | |||||
| auto file = generatedCodeFolder.getChildFile (filePath); | |||||
| if (replaceFileIfDifferent (file, newData)) | |||||
| return addFileToGeneratedGroup (file); | |||||
| return { project, {}, true }; | |||||
| } | |||||
| bool ProjectSaver::replaceFileIfDifferent (const File& f, const MemoryOutputStream& newData) | |||||
| { | |||||
| filesCreated.add (f); | |||||
| if (! build_tools::overwriteFileWithNewDataIfDifferent (f, newData)) | |||||
| { | |||||
| addError ("Can't write to file: " + f.getFullPathName()); | |||||
| return false; | |||||
| } | |||||
| return true; | |||||
| } | |||||
| bool ProjectSaver::deleteUnwantedFilesIn (const File& parent) | |||||
| { | |||||
| // Recursively clears out any files in a folder that we didn't create, but avoids | |||||
| // any folders containing hidden files that might be used by version-control systems. | |||||
| auto shouldFileBeKept = [] (const String& filename) | |||||
| { | |||||
| static StringArray filesToKeep (".svn", ".cvs", "CMakeLists.txt"); | |||||
| return filesToKeep.contains (filename); | |||||
| }; | |||||
| bool folderIsNowEmpty = true; | |||||
| Array<File> filesToDelete; | |||||
| for (const auto& i : RangedDirectoryIterator (parent, false, "*", File::findFilesAndDirectories)) | |||||
| { | |||||
| auto f = i.getFile(); | |||||
| if (filesCreated.contains (f) || shouldFileBeKept (f.getFileName())) | |||||
| { | |||||
| folderIsNowEmpty = false; | |||||
| } | |||||
| else if (i.isDirectory()) | |||||
| { | |||||
| if (deleteUnwantedFilesIn (f)) | |||||
| filesToDelete.add (f); | |||||
| else | |||||
| folderIsNowEmpty = false; | |||||
| } | |||||
| else | |||||
| { | |||||
| filesToDelete.add (f); | |||||
| } | |||||
| } | |||||
| for (int j = filesToDelete.size(); --j >= 0;) | |||||
| filesToDelete.getReference (j).deleteRecursively(); | |||||
| return folderIsNowEmpty; | |||||
| } | |||||
| //============================================================================== | |||||
| void ProjectSaver::addError (const String& message) | |||||
| { | |||||
| const ScopedLock sl (errorLock); | |||||
| errors.add (message); | |||||
| } | |||||
| //============================================================================== | |||||
| File ProjectSaver::getAppConfigFile() const | |||||
| { | |||||
| return generatedCodeFolder.getChildFile (Project::getAppConfigFilename()); | |||||
| } | |||||
| File ProjectSaver::getPluginDefinesFile() const | |||||
| { | |||||
| return generatedCodeFolder.getChildFile (Project::getPluginDefinesFilename()); | |||||
| } | |||||
| String ProjectSaver::loadUserContentFromAppConfig() const | |||||
| { | |||||
| StringArray userContent; | |||||
| bool foundCodeSection = false; | |||||
| auto lines = StringArray::fromLines (getAppConfigFile().loadFileAsString()); | |||||
| for (int i = 0; i < lines.size(); ++i) | |||||
| { | |||||
| if (lines[i].contains ("[BEGIN_USER_CODE_SECTION]")) | |||||
| { | |||||
| for (int j = i + 1; j < lines.size() && ! lines[j].contains ("[END_USER_CODE_SECTION]"); ++j) | |||||
| userContent.add (lines[j]); | |||||
| foundCodeSection = true; | |||||
| break; | |||||
| } | |||||
| } | |||||
| if (! foundCodeSection) | |||||
| { | |||||
| userContent.add ({}); | |||||
| userContent.add ("// (You can add your own code in this section, and the Projucer will not overwrite it)"); | |||||
| userContent.add ({}); | |||||
| } | |||||
| return userContent.joinIntoString (projectLineFeed) + projectLineFeed; | |||||
| } | |||||
| //============================================================================== | |||||
| OwnedArray<LibraryModule> ProjectSaver::getModules() | |||||
| { | |||||
| OwnedArray<LibraryModule> modules; | |||||
| project.getEnabledModules().createRequiredModules (modules); | |||||
| for (auto* module : modules) | |||||
| { | |||||
| if (! module->isValid()) | |||||
| { | |||||
| addError ("At least one of your JUCE module paths is invalid!\n" | |||||
| "Please go to the Modules settings page and ensure each path points to the correct JUCE modules folder."); | |||||
| return {}; | |||||
| } | |||||
| if (project.getEnabledModules().getExtraDependenciesNeeded (module->getID()).size() > 0) | |||||
| { | |||||
| addError ("At least one of your modules has missing dependencies!\n" | |||||
| "Please go to the settings page of the highlighted modules and add the required dependencies."); | |||||
| return {}; | |||||
| } | |||||
| } | |||||
| return modules; | |||||
| } | |||||
| //============================================================================== | //============================================================================== | ||||
| Result ProjectSaver::saveProject (ProjectExporter* specifiedExporterToSave) | |||||
| { | |||||
| if (project.getNumExporters() == 0) | |||||
| { | |||||
| return Result::fail ("No exporters found!\n" | |||||
| "Please add an exporter before saving."); | |||||
| } | |||||
| auto oldProjectFile = project.getFile(); | |||||
| auto modules = getModules(); | |||||
| if (errors.size() == 0) | |||||
| { | |||||
| writeMainProjectFile(); | |||||
| project.updateModificationTime(); | |||||
| auto projectRootHash = project.getProjectRoot().toXmlString().hashCode(); | |||||
| if (project.isAudioPluginProject()) | |||||
| { | |||||
| if (project.shouldBuildUnityPlugin()) | |||||
| writeUnityScriptFile(); | |||||
| } | |||||
| saveBasicProjectItems (modules, loadUserContentFromAppConfig()); | |||||
| writeProjects (modules, specifiedExporterToSave); | |||||
| runPostExportScript(); | |||||
| // if the project root has changed after writing the other files then re-save it | |||||
| if (project.getProjectRoot().toXmlString().hashCode() != projectRootHash) | |||||
| { | |||||
| writeMainProjectFile(); | |||||
| project.updateModificationTime(); | |||||
| } | |||||
| if (generatedCodeFolder.exists()) | |||||
| { | |||||
| writeReadmeFile(); | |||||
| deleteUnwantedFilesIn (generatedCodeFolder); | |||||
| } | |||||
| if (errors.size() == 0) | |||||
| return Result::ok(); | |||||
| } | |||||
| project.setFile (oldProjectFile); | |||||
| return Result::fail (errors[0]); | |||||
| } | |||||
| //============================================================================== | |||||
| void ProjectSaver::writeMainProjectFile() | |||||
| { | |||||
| if (auto xml = project.getProjectRoot().createXml()) | |||||
| { | |||||
| XmlElement::TextFormat format; | |||||
| format.newLineChars = projectLineFeed.toRawUTF8(); | |||||
| MemoryOutputStream mo (8192); | |||||
| xml->writeTo (mo, format); | |||||
| replaceFileIfDifferent (project.getFile(), mo); | |||||
| } | |||||
| else | |||||
| { | |||||
| addError ("Failed to write main project file: " + project.getFile().getFullPathName()); | |||||
| } | |||||
| } | |||||
| static void writeAutoGenWarningComment (OutputStream& out) | |||||
| { | |||||
| out << "/*" << newLine << newLine | |||||
| << " IMPORTANT! This file is auto-generated each time you save your" << newLine | |||||
| << " project - if you alter its contents, your changes may be overwritten!" << newLine | |||||
| << newLine; | |||||
| } | |||||
| void ProjectSaver::writePluginDefines (MemoryOutputStream& out) const | |||||
| { | |||||
| const auto pluginDefines = getAudioPluginDefines(); | |||||
| if (pluginDefines.isEmpty()) | |||||
| return; | |||||
| writeAutoGenWarningComment (out); | |||||
| out << "*/" << newLine << newLine | |||||
| << "#pragma once" << newLine << newLine | |||||
| << pluginDefines << newLine; | |||||
| } | |||||
| void ProjectSaver::writeAppConfig (MemoryOutputStream& out, const OwnedArray<LibraryModule>& modules, const String& userContent) | |||||
| { | |||||
| if (! project.shouldUseAppConfig()) | |||||
| return; | |||||
| writeAutoGenWarningComment (out); | |||||
| out << " There's a section below where you can add your own custom code safely, and the" << newLine | |||||
| << " Projucer will preserve the contents of that block, but the best way to change" << newLine | |||||
| << " any of these definitions is by using the Projucer's project settings." << newLine | |||||
| << newLine | |||||
| << " Any commented-out settings will assume their default values." << newLine | |||||
| << newLine | |||||
| << "*/" << newLine | |||||
| << newLine; | |||||
| out << "#pragma once" << newLine | |||||
| << newLine | |||||
| << "//==============================================================================" << newLine | |||||
| << "// [BEGIN_USER_CODE_SECTION]" << newLine | |||||
| << userContent | |||||
| << "// [END_USER_CODE_SECTION]" << newLine; | |||||
| if (getPluginDefinesFile().existsAsFile() && getAudioPluginDefines().isNotEmpty()) | |||||
| out << newLine << CodeHelpers::createIncludeStatement (Project::getPluginDefinesFilename()) << newLine; | |||||
| out << newLine | |||||
| << "/*" << newLine | |||||
| << " ==============================================================================" << newLine | |||||
| << newLine | |||||
| << " In accordance with the terms of the JUCE 5 End-Use License Agreement, the" << newLine | |||||
| << " JUCE Code in SECTION A cannot be removed, changed or otherwise rendered" << newLine | |||||
| << " ineffective unless you have a JUCE Indie or Pro license, or are using JUCE" << newLine | |||||
| << " under the GPL v3 license." << newLine | |||||
| << newLine | |||||
| << " End User License Agreement: www.juce.com/juce-5-licence" << newLine | |||||
| << newLine | |||||
| << " ==============================================================================" << newLine | |||||
| << "*/" << newLine | |||||
| << newLine | |||||
| << "// BEGIN SECTION A" << newLine | |||||
| << newLine | |||||
| << "#ifndef JUCE_DISPLAY_SPLASH_SCREEN" << newLine | |||||
| << " #define JUCE_DISPLAY_SPLASH_SCREEN " << (project.shouldDisplaySplashScreen() ? "1" : "0") << newLine | |||||
| << "#endif" << newLine << newLine | |||||
| << "// END SECTION A" << newLine | |||||
| << newLine | |||||
| << "#define JUCE_USE_DARK_SPLASH_SCREEN " << (project.getSplashScreenColourString() == "Dark" ? "1" : "0") << newLine | |||||
| << newLine | |||||
| << "#define JUCE_PROJUCER_VERSION 0x" << String::toHexString (ProjectInfo::versionNumber) << newLine; | |||||
| out << newLine | |||||
| << "//==============================================================================" << newLine; | |||||
| auto longestModuleName = [&modules]() | |||||
| { | |||||
| int longest = 0; | |||||
| for (auto* module : modules) | |||||
| longest = jmax (longest, module->getID().length()); | |||||
| return longest; | |||||
| }(); | |||||
| for (auto* module : modules) | |||||
| { | |||||
| out << "#define JUCE_MODULE_AVAILABLE_" << module->getID() | |||||
| << String::repeatedString (" ", longestModuleName + 5 - module->getID().length()) << " 1" << newLine; | |||||
| } | |||||
| out << newLine << "#define JUCE_GLOBAL_MODULE_SETTINGS_INCLUDED 1" << newLine; | |||||
| for (auto* module : modules) | |||||
| { | |||||
| OwnedArray<Project::ConfigFlag> flags; | |||||
| module->getConfigFlags (project, flags); | |||||
| if (flags.size() > 0) | |||||
| { | |||||
| out << newLine | |||||
| << "//==============================================================================" << newLine | |||||
| << "// " << module->getID() << " flags:" << newLine; | |||||
| for (auto* flag : flags) | |||||
| { | |||||
| out << newLine | |||||
| << "#ifndef " << flag->symbol | |||||
| << newLine | |||||
| << (flag->value.isUsingDefault() ? " //#define " : " #define ") << flag->symbol << " " << (flag->value.get() ? "1" : "0") | |||||
| << newLine | |||||
| << "#endif" | |||||
| << newLine; | |||||
| } | |||||
| } | |||||
| } | |||||
| auto& type = project.getProjectType(); | |||||
| auto isStandaloneApplication = (! type.isAudioPlugin() && ! type.isDynamicLibrary()); | |||||
| out << newLine | |||||
| << "//==============================================================================" << newLine | |||||
| << "#ifndef JUCE_STANDALONE_APPLICATION" << newLine | |||||
| << " #if defined(JucePlugin_Name) && defined(JucePlugin_Build_Standalone)" << newLine | |||||
| << " #define JUCE_STANDALONE_APPLICATION JucePlugin_Build_Standalone" << newLine | |||||
| << " #else" << newLine | |||||
| << " #define JUCE_STANDALONE_APPLICATION " << (isStandaloneApplication ? "1" : "0") << newLine | |||||
| << " #endif" << newLine | |||||
| << "#endif" << newLine; | |||||
| } | |||||
| template <typename WriterCallback> | |||||
| void ProjectSaver::writeOrRemoveGeneratedFile (const String& name, WriterCallback&& writerCallback) | |||||
| { | |||||
| MemoryOutputStream mem; | |||||
| mem.setNewLineString (projectLineFeed); | |||||
| writerCallback (mem); | |||||
| if (mem.getDataSize() != 0) | |||||
| { | |||||
| saveGeneratedFile (name, mem); | |||||
| return; | |||||
| } | |||||
| const auto destFile = generatedCodeFolder.getChildFile (name); | |||||
| if (destFile.existsAsFile()) | |||||
| { | |||||
| if (! destFile.deleteFile()) | |||||
| addError ("Couldn't remove unnecessary file: " + destFile.getFullPathName()); | |||||
| } | |||||
| } | |||||
| void ProjectSaver::writePluginDefines() | |||||
| { | |||||
| writeOrRemoveGeneratedFile (Project::getPluginDefinesFilename(), [&] (MemoryOutputStream& mem) | |||||
| { | |||||
| writePluginDefines (mem); | |||||
| }); | |||||
| } | |||||
| void ProjectSaver::writeAppConfigFile (const OwnedArray<LibraryModule>& modules, const String& userContent) | |||||
| { | |||||
| writeOrRemoveGeneratedFile (Project::getAppConfigFilename(), [&] (MemoryOutputStream& mem) | |||||
| { | |||||
| writeAppConfig (mem, modules, userContent); | |||||
| }); | |||||
| } | |||||
| void ProjectSaver::writeAppHeader (MemoryOutputStream& out, const OwnedArray<LibraryModule>& modules) | |||||
| { | |||||
| writeAutoGenWarningComment (out); | |||||
| out << " This is the header file that your files should include in order to get all the" << newLine | |||||
| << " JUCE library headers. You should avoid including the JUCE headers directly in" << newLine | |||||
| << " your own source files, because that wouldn't pick up the correct configuration" << newLine | |||||
| << " options for your app." << newLine | |||||
| << newLine | |||||
| << "*/" << newLine << newLine; | |||||
| out << "#pragma once" << newLine << newLine; | |||||
| if (getAppConfigFile().exists() && project.shouldUseAppConfig()) | |||||
| out << CodeHelpers::createIncludeStatement (Project::getAppConfigFilename()) << newLine; | |||||
| if (modules.size() > 0) | |||||
| { | |||||
| out << newLine; | |||||
| for (auto* module : modules) | |||||
| module->writeIncludes (*this, out); | |||||
| out << newLine; | |||||
| } | |||||
| if (hasBinaryData && project.shouldIncludeBinaryInJuceHeader()) | |||||
| out << CodeHelpers::createIncludeStatement (project.getBinaryDataHeaderFile(), getAppConfigFile()) << newLine; | |||||
| out << newLine | |||||
| << "#if defined (JUCE_PROJUCER_VERSION) && JUCE_PROJUCER_VERSION < JUCE_VERSION" << newLine | |||||
| << " /** If you've hit this error then the version of the Projucer that was used to generate this project is" << newLine | |||||
| << " older than the version of the JUCE modules being included. To fix this error, re-save your project" << newLine | |||||
| << " using the latest version of the Projucer or, if you aren't using the Projucer to manage your project," << newLine | |||||
| << " remove the JUCE_PROJUCER_VERSION define from the AppConfig.h file." << newLine | |||||
| << " */" << newLine | |||||
| << " #error \"This project was last saved using an outdated version of the Projucer! Re-save this project with the latest version to fix this error.\"" << newLine | |||||
| << "#endif" << newLine | |||||
| << newLine; | |||||
| if (project.shouldAddUsingNamespaceToJuceHeader()) | |||||
| out << "#if ! DONT_SET_USING_JUCE_NAMESPACE" << newLine | |||||
| << " // If your code uses a lot of JUCE classes, then this will obviously save you" << newLine | |||||
| << " // a lot of typing, but can be disabled by setting DONT_SET_USING_JUCE_NAMESPACE." << newLine | |||||
| << " using namespace juce;" << newLine | |||||
| << "#endif" << newLine; | |||||
| out << newLine | |||||
| << "#if ! JUCE_DONT_DECLARE_PROJECTINFO" << newLine | |||||
| << "namespace ProjectInfo" << newLine | |||||
| << "{" << newLine | |||||
| << " const char* const projectName = " << CppTokeniserFunctions::addEscapeChars (project.getProjectNameString()).quoted() << ";" << newLine | |||||
| << " const char* const companyName = " << CppTokeniserFunctions::addEscapeChars (project.getCompanyNameString()).quoted() << ";" << newLine | |||||
| << " const char* const versionString = " << CppTokeniserFunctions::addEscapeChars (project.getVersionString()).quoted() << ";" << newLine | |||||
| << " const int versionNumber = " << project.getVersionAsHex() << ";" << newLine | |||||
| << "}" << newLine | |||||
| << "#endif" << newLine; | |||||
| } | |||||
| void ProjectSaver::writeAppHeader (const OwnedArray<LibraryModule>& modules) | |||||
| { | |||||
| MemoryOutputStream mem; | |||||
| mem.setNewLineString (projectLineFeed); | |||||
| writeAppHeader (mem, modules); | |||||
| saveGeneratedFile (Project::getJuceSourceHFilename(), mem); | |||||
| } | |||||
| void ProjectSaver::writeModuleCppWrappers (const OwnedArray<LibraryModule>& modules) | |||||
| { | |||||
| for (auto* module : modules) | |||||
| { | |||||
| for (auto& cu : module->getAllCompileUnits()) | |||||
| { | |||||
| MemoryOutputStream mem; | |||||
| mem.setNewLineString (projectLineFeed); | |||||
| writeAutoGenWarningComment (mem); | |||||
| mem << "*/" << newLine << newLine; | |||||
| if (project.shouldUseAppConfig()) | |||||
| mem << "#include " << Project::getAppConfigFilename().quoted() << newLine; | |||||
| mem << "#include <"; | |||||
| if (cu.file.getFileExtension() != ".r") // .r files are included without the path | |||||
| mem << module->getID() << "/"; | |||||
| mem << cu.file.getFileName() << ">" << newLine; | |||||
| replaceFileIfDifferent (generatedCodeFolder.getChildFile (cu.getFilenameForProxyFile()), mem); | |||||
| } | |||||
| } | |||||
| } | |||||
| void ProjectSaver::writeBinaryDataFiles() | |||||
| { | |||||
| auto binaryDataH = project.getBinaryDataHeaderFile(); | |||||
| JucerResourceFile resourceFile (project); | |||||
| if (resourceFile.getNumFiles() > 0) | |||||
| { | |||||
| auto dataNamespace = project.getBinaryDataNamespaceString().trim(); | |||||
| if (dataNamespace.isEmpty()) | |||||
| dataNamespace = "BinaryData"; | |||||
| resourceFile.setClassName (dataNamespace); | |||||
| auto maxSize = project.getMaxBinaryFileSize(); | |||||
| if (maxSize <= 0) | |||||
| maxSize = 10 * 1024 * 1024; | |||||
| Array<File> binaryDataFiles; | |||||
| auto r = resourceFile.write (maxSize); | |||||
| if (r.result.wasOk()) | |||||
| { | |||||
| hasBinaryData = true; | |||||
| for (auto& f : r.filesCreated) | |||||
| { | |||||
| filesCreated.add (f); | |||||
| generatedFilesGroup.addFileRetainingSortOrder (f, ! f.hasFileExtension (".h")); | |||||
| } | |||||
| } | |||||
| else | |||||
| { | |||||
| addError (r.result.getErrorMessage()); | |||||
| } | |||||
| } | |||||
| else | |||||
| { | |||||
| for (int i = 20; --i >= 0;) | |||||
| project.getBinaryDataCppFile (i).deleteFile(); | |||||
| binaryDataH.deleteFile(); | |||||
| } | |||||
| } | |||||
| void ProjectSaver::writeReadmeFile() | |||||
| { | |||||
| MemoryOutputStream out; | |||||
| out.setNewLineString (projectLineFeed); | |||||
| out << newLine | |||||
| << " Important Note!!" << newLine | |||||
| << " ================" << newLine | |||||
| << newLine | |||||
| << "The purpose of this folder is to contain files that are auto-generated by the Projucer," << newLine | |||||
| << "and ALL files in this folder will be mercilessly DELETED and completely re-written whenever" << newLine | |||||
| << "the Projucer saves your project." << newLine | |||||
| << newLine | |||||
| << "Therefore, it's a bad idea to make any manual changes to the files in here, or to" << newLine | |||||
| << "put any of your own files in here if you don't want to lose them. (Of course you may choose" << newLine | |||||
| << "to add the folder's contents to your version-control system so that you can re-merge your own" << newLine | |||||
| << "modifications after the Projucer has saved its changes)." << newLine; | |||||
| replaceFileIfDifferent (generatedCodeFolder.getChildFile ("ReadMe.txt"), out); | |||||
| } | |||||
| String ProjectSaver::getAudioPluginDefines() const | String ProjectSaver::getAudioPluginDefines() const | ||||
| { | { | ||||
| const auto flags = project.getAudioPluginFlags(); | const auto flags = project.getAudioPluginFlags(); | ||||
| @@ -47,7 +690,27 @@ String ProjectSaver::getAudioPluginDefines() const | |||||
| return mem.toString().trim(); | return mem.toString().trim(); | ||||
| } | } | ||||
| void ProjectSaver::writeProjects (const OwnedArray<LibraryModule>& modules, const String& specifiedExporterToSave, bool isCommandLineApp) | |||||
| void ProjectSaver::writeUnityScriptFile() | |||||
| { | |||||
| auto unityScriptContents = replaceLineFeeds (BinaryData::UnityPluginGUIScript_cs_in, | |||||
| projectLineFeed); | |||||
| auto projectName = Project::addUnityPluginPrefixIfNecessary (project.getProjectNameString()); | |||||
| unityScriptContents = unityScriptContents.replace ("${plugin_class_name}", projectName.replace (" ", "_")) | |||||
| .replace ("${plugin_name}", projectName) | |||||
| .replace ("${plugin_vendor}", project.getPluginManufacturerString()) | |||||
| .replace ("${plugin_description}", project.getPluginDescriptionString()); | |||||
| auto f = generatedCodeFolder.getChildFile (project.getUnityScriptName()); | |||||
| MemoryOutputStream out; | |||||
| out << unityScriptContents; | |||||
| replaceFileIfDifferent (f, out); | |||||
| } | |||||
| void ProjectSaver::writeProjects (const OwnedArray<LibraryModule>& modules, ProjectExporter* specifiedExporterToSave) | |||||
| { | { | ||||
| ThreadPool threadPool; | ThreadPool threadPool; | ||||
| @@ -55,24 +718,27 @@ void ProjectSaver::writeProjects (const OwnedArray<LibraryModule>& modules, cons | |||||
| auto originalGeneratedGroup = generatedFilesGroup.state.createCopy(); | auto originalGeneratedGroup = generatedFilesGroup.state.createCopy(); | ||||
| CLionProjectExporter* clionExporter = nullptr; | CLionProjectExporter* clionExporter = nullptr; | ||||
| OwnedArray<ProjectExporter> exporters; | |||||
| std::vector<std::unique_ptr<ProjectExporter>> exporters; | |||||
| try | try | ||||
| { | { | ||||
| for (Project::ExporterIterator exp (project); exp.next();) | for (Project::ExporterIterator exp (project); exp.next();) | ||||
| { | { | ||||
| if (specifiedExporterToSave.isNotEmpty() && exp->getName() != specifiedExporterToSave) | |||||
| if (specifiedExporterToSave != nullptr && exp->getName() != specifiedExporterToSave->getName()) | |||||
| continue; | continue; | ||||
| auto exporter = exporters.add (std::move (exp.exporter)); | |||||
| exporters.push_back (std::move (exp.exporter)); | |||||
| } | |||||
| for (auto& exporter : exporters) | |||||
| { | |||||
| exporter->initialiseDependencyPathValues(); | exporter->initialiseDependencyPathValues(); | ||||
| if (exporter->getTargetFolder().createDirectory()) | if (exporter->getTargetFolder().createDirectory()) | ||||
| { | { | ||||
| if (exporter->isCLion()) | if (exporter->isCLion()) | ||||
| { | { | ||||
| clionExporter = dynamic_cast<CLionProjectExporter*> (exporter); | |||||
| clionExporter = dynamic_cast<CLionProjectExporter*> (exporter.get()); | |||||
| } | } | ||||
| else | else | ||||
| { | { | ||||
| @@ -84,17 +750,17 @@ void ProjectSaver::writeProjects (const OwnedArray<LibraryModule>& modules, cons | |||||
| generatedFilesGroup.state = originalGeneratedGroup.createCopy(); | generatedFilesGroup.state = originalGeneratedGroup.createCopy(); | ||||
| exporter->addSettingsForProjectType (project.getProjectType()); | exporter->addSettingsForProjectType (project.getProjectType()); | ||||
| for (auto& module: modules) | |||||
| for (auto* module : modules) | |||||
| module->addSettingsForModuleToExporter (*exporter, *this); | module->addSettingsForModuleToExporter (*exporter, *this); | ||||
| generatedFilesGroup.sortAlphabetically (true, true); | generatedFilesGroup.sortAlphabetically (true, true); | ||||
| exporter->getAllGroups().add (generatedFilesGroup); | exporter->getAllGroups().add (generatedFilesGroup); | ||||
| } | } | ||||
| if (isCommandLineApp) | |||||
| saveExporter (exporter, modules); | |||||
| if (ProjucerApplication::getApp().isRunningCommandLine) | |||||
| saveExporter (*exporter, modules); | |||||
| else | else | ||||
| threadPool.addJob (new ExporterJob (*this, exporter, modules), true); | |||||
| threadPool.addJob (new ExporterJob (*this, *exporter, modules), true); | |||||
| } | } | ||||
| else | else | ||||
| { | { | ||||
| @@ -107,15 +773,69 @@ void ProjectSaver::writeProjects (const OwnedArray<LibraryModule>& modules, cons | |||||
| addError (saveError.message); | addError (saveError.message); | ||||
| } | } | ||||
| if (! isCommandLineApp) | |||||
| while (threadPool.getNumJobs() > 0) | |||||
| Thread::sleep (10); | |||||
| while (threadPool.getNumJobs() > 0) | |||||
| Thread::sleep (10); | |||||
| if (clionExporter != nullptr) | if (clionExporter != nullptr) | ||||
| { | { | ||||
| for (auto* exporter : exporters) | |||||
| clionExporter->writeCMakeListsExporterSection (exporter); | |||||
| for (auto& exporter : exporters) | |||||
| clionExporter->writeCMakeListsExporterSection (exporter.get()); | |||||
| std::cout << "Finished saving: " << clionExporter->getName() << std::endl; | std::cout << "Finished saving: " << clionExporter->getName() << std::endl; | ||||
| } | } | ||||
| } | } | ||||
| void ProjectSaver::runPostExportScript() | |||||
| { | |||||
| #if JUCE_WINDOWS | |||||
| auto cmdString = project.getPostExportShellCommandWinString(); | |||||
| #else | |||||
| auto cmdString = project.getPostExportShellCommandPosixString(); | |||||
| #endif | |||||
| auto shellCommand = cmdString.replace ("%%1%%", project.getProjectFolder().getFullPathName()); | |||||
| if (shellCommand.isNotEmpty()) | |||||
| { | |||||
| #if JUCE_WINDOWS | |||||
| StringArray argList ("cmd.exe", "/c"); | |||||
| #else | |||||
| StringArray argList ("/bin/sh", "-c"); | |||||
| #endif | |||||
| argList.add (shellCommand); | |||||
| ChildProcess shellProcess; | |||||
| if (! shellProcess.start (argList)) | |||||
| { | |||||
| addError ("Failed to run shell command: " + argList.joinIntoString (" ")); | |||||
| return; | |||||
| } | |||||
| if (! shellProcess.waitForProcessToFinish (10000)) | |||||
| { | |||||
| addError ("Timeout running shell command: " + argList.joinIntoString (" ")); | |||||
| return; | |||||
| } | |||||
| auto exitCode = shellProcess.getExitCode(); | |||||
| if (exitCode != 0) | |||||
| addError ("Shell command: " + argList.joinIntoString (" ") + " failed with exit code: " + String (exitCode)); | |||||
| } | |||||
| } | |||||
| void ProjectSaver::saveExporter (ProjectExporter& exporter, const OwnedArray<LibraryModule>& modules) | |||||
| { | |||||
| try | |||||
| { | |||||
| exporter.create (modules); | |||||
| if (! exporter.isCLion()) | |||||
| std::cout << "Finished saving: " << exporter.getName() << std::endl; | |||||
| } | |||||
| catch (build_tools::SaveError& error) | |||||
| { | |||||
| addError (error.message); | |||||
| } | |||||
| } | |||||
| @@ -18,809 +18,127 @@ | |||||
| #pragma once | #pragma once | ||||
| #include "../Application/jucer_Headers.h" | |||||
| #include "jucer_ResourceFile.h" | #include "jucer_ResourceFile.h" | ||||
| #include "../Project/jucer_Module.h" | |||||
| #include "../Project/Modules/jucer_Modules.h" | |||||
| #include "jucer_ProjectExporter.h" | #include "jucer_ProjectExporter.h" | ||||
| //============================================================================== | //============================================================================== | ||||
| class ProjectSaver | class ProjectSaver | ||||
| { | { | ||||
| public: | public: | ||||
| ProjectSaver (Project& p, const File& file) | |||||
| : project (p), | |||||
| projectFile (file), | |||||
| generatedCodeFolder (project.getGeneratedCodeFolder()), | |||||
| generatedFilesGroup (Project::Item::createGroup (project, getJuceCodeGroupName(), "__generatedcode__", true)) | |||||
| { | |||||
| generatedFilesGroup.setID (getGeneratedGroupID()); | |||||
| } | |||||
| ProjectSaver (Project& projectToSave); | |||||
| Result save (ProjectExporter* exporterToSave = nullptr); | |||||
| Result saveResourcesOnly(); | |||||
| void saveBasicProjectItems (const OwnedArray<LibraryModule>& modules, const String& appConfigUserContent); | |||||
| Result saveContentNeededForLiveBuild(); | |||||
| Project& getProject() { return project; } | |||||
| Project::Item addFileToGeneratedGroup (const File& file); | |||||
| bool copyFolder (const File& source, const File& dest); | |||||
| struct SaveThread : public ThreadWithProgressWindow | |||||
| static String getJuceCodeGroupName() { return "JUCE Library Code"; } | |||||
| private: | |||||
| //============================================================================== | |||||
| struct SaveThreadWithProgressWindow : public ThreadWithProgressWindow | |||||
| { | { | ||||
| public: | public: | ||||
| SaveThread (ProjectSaver& ps, bool wait, const String& exp) | |||||
| SaveThreadWithProgressWindow (ProjectSaver& ps, ProjectExporter* exporterToSave) | |||||
| : ThreadWithProgressWindow ("Saving...", true, false), | : ThreadWithProgressWindow ("Saving...", true, false), | ||||
| saver (ps), | saver (ps), | ||||
| shouldWaitAfterSaving (wait), | |||||
| specifiedExporterToSave (exp) | |||||
| specifiedExporterToSave (exporterToSave) | |||||
| {} | {} | ||||
| void run() override | void run() override | ||||
| { | { | ||||
| setProgress (-1); | setProgress (-1); | ||||
| result = saver.save (false, shouldWaitAfterSaving, specifiedExporterToSave); | |||||
| result = saver.saveProject (specifiedExporterToSave); | |||||
| } | } | ||||
| ProjectSaver& saver; | ProjectSaver& saver; | ||||
| Result result = Result::ok(); | Result result = Result::ok(); | ||||
| bool shouldWaitAfterSaving; | |||||
| String specifiedExporterToSave; | |||||
| ProjectExporter* specifiedExporterToSave; | |||||
| JUCE_DECLARE_NON_COPYABLE (SaveThread) | |||||
| JUCE_DECLARE_NON_COPYABLE (SaveThreadWithProgressWindow) | |||||
| }; | }; | ||||
| Result save (bool showProgressBox, bool waitAfterSaving, const String& specifiedExporterToSave) | |||||
| { | |||||
| if (showProgressBox) | |||||
| { | |||||
| SaveThread thread (*this, waitAfterSaving, specifiedExporterToSave); | |||||
| thread.runThread(); | |||||
| return thread.result; | |||||
| } | |||||
| projectLineFeed = project.getProjectLineFeed(); | |||||
| auto appConfigUserContent = loadUserContentFromAppConfig(); | |||||
| auto oldFile = project.getFile(); | |||||
| project.setFile (projectFile); | |||||
| OwnedArray<LibraryModule> modules; | |||||
| project.getEnabledModules().createRequiredModules (modules); | |||||
| checkModuleValidity (modules); | |||||
| if (errors.size() == 0) | |||||
| { | |||||
| writeMainProjectFile(); | |||||
| project.updateModificationTime(); | |||||
| auto projectRootHash = project.getProjectRoot().toXmlString().hashCode(); | |||||
| if (project.isAudioPluginProject()) | |||||
| { | |||||
| if (project.shouldBuildUnityPlugin()) | |||||
| writeUnityScriptFile(); | |||||
| } | |||||
| saveBasicProjectItems (modules, appConfigUserContent); | |||||
| writeProjects (modules, specifiedExporterToSave, ! showProgressBox); | |||||
| runPostExportScript(); | |||||
| // if the project root has changed after writing the other files then re-save it | |||||
| if (project.getProjectRoot().toXmlString().hashCode() != projectRootHash) | |||||
| { | |||||
| writeMainProjectFile(); | |||||
| project.updateModificationTime(); | |||||
| } | |||||
| if (generatedCodeFolder.exists()) | |||||
| { | |||||
| writeReadmeFile(); | |||||
| deleteUnwantedFilesIn (generatedCodeFolder); | |||||
| } | |||||
| if (errors.size() == 0) | |||||
| { | |||||
| // Workaround for a bug where Xcode thinks the project is invalid if opened immediately | |||||
| // after writing | |||||
| if (waitAfterSaving) | |||||
| Thread::sleep (2000); | |||||
| return Result::ok(); | |||||
| } | |||||
| } | |||||
| project.setFile (oldFile); | |||||
| return Result::fail (errors[0]); | |||||
| } | |||||
| Result saveResourcesOnly() | |||||
| { | |||||
| writeBinaryDataFiles(); | |||||
| if (errors.size() > 0) | |||||
| return Result::fail (errors[0]); | |||||
| return Result::ok(); | |||||
| } | |||||
| void saveBasicProjectItems (const OwnedArray<LibraryModule>& modules, const String& appConfigUserContent) | |||||
| { | |||||
| writePluginDefines(); | |||||
| writeAppConfigFile (modules, appConfigUserContent); | |||||
| writeBinaryDataFiles(); | |||||
| writeAppHeader (modules); | |||||
| writeModuleCppWrappers (modules); | |||||
| } | |||||
| Result saveContentNeededForLiveBuild() | |||||
| { | |||||
| OwnedArray<LibraryModule> modules; | |||||
| project.getEnabledModules().createRequiredModules (modules); | |||||
| checkModuleValidity (modules); | |||||
| if (errors.size() == 0) | |||||
| { | |||||
| saveBasicProjectItems (modules, loadUserContentFromAppConfig()); | |||||
| return Result::ok(); | |||||
| } | |||||
| return Result::fail (errors[0]); | |||||
| } | |||||
| Project::Item saveGeneratedFile (const String& filePath, const MemoryOutputStream& newData) | |||||
| { | |||||
| if (! generatedCodeFolder.createDirectory()) | |||||
| { | |||||
| addError ("Couldn't create folder: " + generatedCodeFolder.getFullPathName()); | |||||
| return Project::Item (project, ValueTree(), false); | |||||
| } | |||||
| auto file = generatedCodeFolder.getChildFile (filePath); | |||||
| if (replaceFileIfDifferent (file, newData)) | |||||
| return addFileToGeneratedGroup (file); | |||||
| return { project, {}, true }; | |||||
| } | |||||
| Project::Item addFileToGeneratedGroup (const File& file) | |||||
| { | |||||
| auto item = generatedFilesGroup.findItemForFile (file); | |||||
| if (item.isValid()) | |||||
| return item; | |||||
| generatedFilesGroup.addFileAtIndex (file, -1, true); | |||||
| return generatedFilesGroup.findItemForFile (file); | |||||
| } | |||||
| static void writeAutoGenWarningComment (OutputStream& out) | |||||
| { | |||||
| out << "/*" << newLine << newLine | |||||
| << " IMPORTANT! This file is auto-generated each time you save your" << newLine | |||||
| << " project - if you alter its contents, your changes may be overwritten!" << newLine | |||||
| << newLine; | |||||
| } | |||||
| static const char* getGeneratedGroupID() noexcept { return "__jucelibfiles"; } | |||||
| Project::Item& getGeneratedCodeGroup() { return generatedFilesGroup; } | |||||
| static String getJuceCodeGroupName() { return "JUCE Library Code"; } | |||||
| File getGeneratedCodeFolder() const { return generatedCodeFolder; } | |||||
| bool replaceFileIfDifferent (const File& f, const MemoryOutputStream& newData) | |||||
| class ExporterJob : public ThreadPoolJob | |||||
| { | { | ||||
| filesCreated.add (f); | |||||
| if (!build_tools::overwriteFileWithNewDataIfDifferent (f, newData)) | |||||
| { | |||||
| addError ("Can't write to file: " + f.getFullPathName()); | |||||
| return false; | |||||
| } | |||||
| public: | |||||
| ExporterJob (ProjectSaver& ps, ProjectExporter& pe, const OwnedArray<LibraryModule>& modulesList) | |||||
| : ThreadPoolJob ("export"), | |||||
| owner (ps), | |||||
| exporter (pe), | |||||
| modules (modulesList) | |||||
| { | |||||
| } | |||||
| JobStatus runJob() override | |||||
| { | |||||
| owner.saveExporter (exporter, modules); | |||||
| return jobHasFinished; | |||||
| } | |||||
| return true; | |||||
| } | |||||
| private: | |||||
| ProjectSaver& owner; | |||||
| ProjectExporter& exporter; | |||||
| const OwnedArray<LibraryModule>& modules; | |||||
| static bool shouldFolderBeIgnoredWhenCopying (const File& f) | |||||
| { | |||||
| return f.getFileName() == ".git" || f.getFileName() == ".svn" || f.getFileName() == ".cvs"; | |||||
| } | |||||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ExporterJob) | |||||
| }; | |||||
| bool copyFolder (const File& source, const File& dest) | |||||
| { | |||||
| if (source.isDirectory() && dest.createDirectory()) | |||||
| { | |||||
| for (auto& f : source.findChildFiles (File::findFiles, false)) | |||||
| { | |||||
| auto target = dest.getChildFile (f.getFileName()); | |||||
| filesCreated.add (target); | |||||
| //============================================================================== | |||||
| Project::Item saveGeneratedFile (const String& filePath, const MemoryOutputStream& newData); | |||||
| bool replaceFileIfDifferent (const File& f, const MemoryOutputStream& newData); | |||||
| bool deleteUnwantedFilesIn (const File& parent); | |||||
| if (! f.copyFileTo (target)) | |||||
| return false; | |||||
| } | |||||
| void addError (const String& message); | |||||
| for (auto& f : source.findChildFiles (File::findDirectories, false)) | |||||
| if (! shouldFolderBeIgnoredWhenCopying (f)) | |||||
| if (! copyFolder (f, dest.getChildFile (f.getFileName()))) | |||||
| return false; | |||||
| File getAppConfigFile() const; | |||||
| File getPluginDefinesFile() const; | |||||
| return true; | |||||
| } | |||||
| String loadUserContentFromAppConfig() const; | |||||
| String getAudioPluginDefines() const; | |||||
| OwnedArray<LibraryModule> getModules(); | |||||
| return false; | |||||
| } | |||||
| Result saveProject (ProjectExporter* specifiedExporterToSave); | |||||
| template <typename WriterCallback> | |||||
| void writeOrRemoveGeneratedFile (const String& name, WriterCallback&& writerCallback); | |||||
| void writePluginDefines (MemoryOutputStream& outStream) const; | |||||
| void writePluginDefines(); | |||||
| void writeAppConfigFile (const OwnedArray<LibraryModule>& modules, const String& userContent); | |||||
| void writeMainProjectFile(); | |||||
| void writeAppConfig (MemoryOutputStream& outStream, const OwnedArray<LibraryModule>& modules, const String& userContent); | |||||
| void writeAppHeader (MemoryOutputStream& outStream, const OwnedArray<LibraryModule>& modules); | |||||
| void writeAppHeader (const OwnedArray<LibraryModule>& modules); | |||||
| void writeModuleCppWrappers (const OwnedArray<LibraryModule>& modules); | |||||
| void writeBinaryDataFiles(); | |||||
| void writeReadmeFile(); | |||||
| void writePluginCharacteristicsFile(); | |||||
| void writeUnityScriptFile(); | |||||
| void writeProjects (const OwnedArray<LibraryModule>&, ProjectExporter*); | |||||
| void runPostExportScript(); | |||||
| void saveExporter (ProjectExporter& exporter, const OwnedArray<LibraryModule>& modules); | |||||
| //============================================================================== | |||||
| Project& project; | Project& project; | ||||
| SortedSet<File> filesCreated; | |||||
| private: | |||||
| const File projectFile, generatedCodeFolder; | |||||
| File generatedCodeFolder; | |||||
| Project::Item generatedFilesGroup; | Project::Item generatedFilesGroup; | ||||
| StringArray errors; | |||||
| SortedSet<File> filesCreated; | |||||
| String projectLineFeed; | |||||
| CriticalSection errorLock; | CriticalSection errorLock; | ||||
| StringArray errors; | |||||
| bool hasBinaryData = false; | bool hasBinaryData = false; | ||||
| String projectLineFeed = "\r\n"; | |||||
| // Recursively clears out any files in a folder that we didn't create, but avoids | |||||
| // any folders containing hidden files that might be used by version-control systems. | |||||
| bool deleteUnwantedFilesIn (const File& parent) | |||||
| { | |||||
| bool folderIsNowEmpty = true; | |||||
| Array<File> filesToDelete; | |||||
| for (const auto& i : RangedDirectoryIterator (parent, false, "*", File::findFilesAndDirectories)) | |||||
| { | |||||
| auto f = i.getFile(); | |||||
| if (filesCreated.contains (f) || shouldFileBeKept (f.getFileName())) | |||||
| { | |||||
| folderIsNowEmpty = false; | |||||
| } | |||||
| else if (i.isDirectory()) | |||||
| { | |||||
| if (deleteUnwantedFilesIn (f)) | |||||
| filesToDelete.add (f); | |||||
| else | |||||
| folderIsNowEmpty = false; | |||||
| } | |||||
| else | |||||
| { | |||||
| filesToDelete.add (f); | |||||
| } | |||||
| } | |||||
| for (int j = filesToDelete.size(); --j >= 0;) | |||||
| filesToDelete.getReference(j).deleteRecursively(); | |||||
| return folderIsNowEmpty; | |||||
| } | |||||
| static bool shouldFileBeKept (const String& filename) | |||||
| { | |||||
| static const char* filesToKeep[] = { ".svn", ".cvs", "CMakeLists.txt" }; | |||||
| for (auto* f : filesToKeep) | |||||
| if (filename == f) | |||||
| return true; | |||||
| return false; | |||||
| } | |||||
| void writeMainProjectFile() | |||||
| { | |||||
| if (auto xml = project.getProjectRoot().createXml()) | |||||
| { | |||||
| XmlElement::TextFormat format; | |||||
| format.newLineChars = projectLineFeed.toRawUTF8(); | |||||
| MemoryOutputStream mo (8192); | |||||
| xml->writeTo (mo, format); | |||||
| replaceFileIfDifferent (projectFile, mo); | |||||
| } | |||||
| else | |||||
| { | |||||
| jassertfalse; | |||||
| } | |||||
| } | |||||
| static int findLongestModuleName (const OwnedArray<LibraryModule>& modules) | |||||
| { | |||||
| int longest = 0; | |||||
| for (auto& m : modules) | |||||
| longest = jmax (longest, m->getID().length()); | |||||
| return longest; | |||||
| } | |||||
| File getAppConfigFile() const { return generatedCodeFolder.getChildFile (Project::getAppConfigFilename()); } | |||||
| File getPluginDefinesFile() const { return generatedCodeFolder.getChildFile (Project::getPluginDefinesFilename()); } | |||||
| String loadUserContentFromAppConfig() const | |||||
| { | |||||
| StringArray userContent; | |||||
| bool foundCodeSection = false; | |||||
| auto lines = StringArray::fromLines (getAppConfigFile().loadFileAsString()); | |||||
| for (int i = 0; i < lines.size(); ++i) | |||||
| { | |||||
| if (lines[i].contains ("[BEGIN_USER_CODE_SECTION]")) | |||||
| { | |||||
| for (int j = i + 1; j < lines.size() && ! lines[j].contains ("[END_USER_CODE_SECTION]"); ++j) | |||||
| userContent.add (lines[j]); | |||||
| foundCodeSection = true; | |||||
| break; | |||||
| } | |||||
| } | |||||
| if (! foundCodeSection) | |||||
| { | |||||
| userContent.add ({}); | |||||
| userContent.add ("// (You can add your own code in this section, and the Projucer will not overwrite it)"); | |||||
| userContent.add ({}); | |||||
| } | |||||
| return userContent.joinIntoString (projectLineFeed) + projectLineFeed; | |||||
| } | |||||
| void checkModuleValidity (OwnedArray<LibraryModule>& modules) | |||||
| { | |||||
| if (project.getNumExporters() == 0) | |||||
| { | |||||
| addError ("No exporters found!\n" | |||||
| "Please add an exporter before saving."); | |||||
| return; | |||||
| } | |||||
| for (auto moduleIter = modules.begin(); moduleIter != modules.end(); ++moduleIter) | |||||
| { | |||||
| if (auto* module = *moduleIter) | |||||
| { | |||||
| if (! module->isValid()) | |||||
| { | |||||
| addError ("At least one of your JUCE module paths is invalid!\n" | |||||
| "Please go to the Modules settings page and ensure each path points to the correct JUCE modules folder."); | |||||
| return; | |||||
| } | |||||
| if (project.getEnabledModules().getExtraDependenciesNeeded (module->getID()).size() > 0) | |||||
| { | |||||
| addError ("At least one of your modules has missing dependencies!\n" | |||||
| "Please go to the settings page of the highlighted modules and add the required dependencies."); | |||||
| return; | |||||
| } | |||||
| } | |||||
| else | |||||
| { | |||||
| // this should never happen! | |||||
| jassertfalse; | |||||
| } | |||||
| } | |||||
| } | |||||
| void writePluginDefines (MemoryOutputStream& out) const | |||||
| { | |||||
| const auto pluginDefines = getAudioPluginDefines(); | |||||
| if (pluginDefines.isEmpty()) | |||||
| return; | |||||
| writeAutoGenWarningComment (out); | |||||
| out << "*/" << newLine << newLine | |||||
| << "#pragma once" << newLine << newLine | |||||
| << pluginDefines << newLine; | |||||
| } | |||||
| void writeAppConfig (MemoryOutputStream& out, const OwnedArray<LibraryModule>& modules, const String& userContent) | |||||
| { | |||||
| if (! project.shouldUseAppConfig()) | |||||
| return; | |||||
| writeAutoGenWarningComment (out); | |||||
| out << " There's a section below where you can add your own custom code safely, and the" << newLine | |||||
| << " Projucer will preserve the contents of that block, but the best way to change" << newLine | |||||
| << " any of these definitions is by using the Projucer's project settings." << newLine | |||||
| << newLine | |||||
| << " Any commented-out settings will assume their default values." << newLine | |||||
| << newLine | |||||
| << "*/" << newLine | |||||
| << newLine; | |||||
| out << "#pragma once" << newLine | |||||
| << newLine | |||||
| << "//==============================================================================" << newLine | |||||
| << "// [BEGIN_USER_CODE_SECTION]" << newLine | |||||
| << userContent | |||||
| << "// [END_USER_CODE_SECTION]" << newLine; | |||||
| if (getPluginDefinesFile().existsAsFile() && getAudioPluginDefines().isNotEmpty()) | |||||
| out << newLine << CodeHelpers::createIncludeStatement (Project::getPluginDefinesFilename()) << newLine; | |||||
| out << newLine | |||||
| << "/*" << newLine | |||||
| << " ==============================================================================" << newLine | |||||
| << newLine | |||||
| << " In accordance with the terms of the JUCE 5 End-Use License Agreement, the" << newLine | |||||
| << " JUCE Code in SECTION A cannot be removed, changed or otherwise rendered" << newLine | |||||
| << " ineffective unless you have a JUCE Indie or Pro license, or are using JUCE" << newLine | |||||
| << " under the GPL v3 license." << newLine | |||||
| << newLine | |||||
| << " End User License Agreement: www.juce.com/juce-5-licence" << newLine | |||||
| << newLine | |||||
| << " ==============================================================================" << newLine | |||||
| << "*/" << newLine | |||||
| << newLine | |||||
| << "// BEGIN SECTION A" << newLine | |||||
| << newLine | |||||
| << "#ifndef JUCE_DISPLAY_SPLASH_SCREEN" << newLine | |||||
| << " #define JUCE_DISPLAY_SPLASH_SCREEN " << (project.shouldDisplaySplashScreen() ? "1" : "0") << newLine | |||||
| << "#endif" << newLine << newLine | |||||
| << "// END SECTION A" << newLine | |||||
| << newLine | |||||
| << "#define JUCE_USE_DARK_SPLASH_SCREEN " << (project.getSplashScreenColourString() == "Dark" ? "1" : "0") << newLine | |||||
| << newLine | |||||
| << "#define JUCE_PROJUCER_VERSION 0x" << String::toHexString (ProjectInfo::versionNumber) << newLine; | |||||
| out << newLine | |||||
| << "//==============================================================================" << newLine; | |||||
| auto longestName = findLongestModuleName (modules); | |||||
| for (auto& m : modules) | |||||
| out << "#define JUCE_MODULE_AVAILABLE_" << m->getID() | |||||
| << String::repeatedString (" ", longestName + 5 - m->getID().length()) << " 1" << newLine; | |||||
| out << newLine << "#define JUCE_GLOBAL_MODULE_SETTINGS_INCLUDED 1" << newLine; | |||||
| for (auto& m : modules) | |||||
| { | |||||
| OwnedArray<Project::ConfigFlag> flags; | |||||
| m->getConfigFlags (project, flags); | |||||
| if (flags.size() > 0) | |||||
| { | |||||
| out << newLine | |||||
| << "//==============================================================================" << newLine | |||||
| << "// " << m->getID() << " flags:" << newLine; | |||||
| for (auto* flag : flags) | |||||
| { | |||||
| out << newLine | |||||
| << "#ifndef " << flag->symbol | |||||
| << newLine | |||||
| << (flag->value.isUsingDefault() ? " //#define " : " #define ") << flag->symbol << " " << (flag->value.get() ? "1" : "0") | |||||
| << newLine | |||||
| << "#endif" | |||||
| << newLine; | |||||
| } | |||||
| } | |||||
| } | |||||
| { | |||||
| auto& type = project.getProjectType(); | |||||
| auto isStandaloneApplication = (! type.isAudioPlugin() && ! type.isDynamicLibrary()); | |||||
| out << newLine | |||||
| << "//==============================================================================" << newLine | |||||
| << "#ifndef JUCE_STANDALONE_APPLICATION" << newLine | |||||
| << " #if defined(JucePlugin_Name) && defined(JucePlugin_Build_Standalone)" << newLine | |||||
| << " #define JUCE_STANDALONE_APPLICATION JucePlugin_Build_Standalone" << newLine | |||||
| << " #else" << newLine | |||||
| << " #define JUCE_STANDALONE_APPLICATION " << (isStandaloneApplication ? "1" : "0") << newLine | |||||
| << " #endif" << newLine | |||||
| << "#endif" << newLine; | |||||
| } | |||||
| } | |||||
| template <typename WriterCallback> | |||||
| void writeOrRemoveGeneratedFile (const String& name, WriterCallback&& writerCallback) | |||||
| { | |||||
| MemoryOutputStream mem; | |||||
| mem.setNewLineString (projectLineFeed); | |||||
| writerCallback (mem); | |||||
| if (mem.getDataSize() != 0) | |||||
| { | |||||
| saveGeneratedFile (name, mem); | |||||
| return; | |||||
| } | |||||
| const auto destFile = generatedCodeFolder.getChildFile (name); | |||||
| if (destFile.existsAsFile()) | |||||
| { | |||||
| if (! destFile.deleteFile()) | |||||
| addError ("Couldn't remove unnecessary file: " + destFile.getFullPathName()); | |||||
| } | |||||
| } | |||||
| void writePluginDefines() | |||||
| { | |||||
| writeOrRemoveGeneratedFile (Project::getPluginDefinesFilename(), [&] (MemoryOutputStream& mem) | |||||
| { | |||||
| writePluginDefines (mem); | |||||
| }); | |||||
| } | |||||
| void writeAppConfigFile (const OwnedArray<LibraryModule>& modules, const String& userContent) | |||||
| { | |||||
| writeOrRemoveGeneratedFile (Project::getAppConfigFilename(), [&] (MemoryOutputStream& mem) | |||||
| { | |||||
| writeAppConfig (mem, modules, userContent); | |||||
| }); | |||||
| } | |||||
| void writeAppHeader (MemoryOutputStream& out, const OwnedArray<LibraryModule>& modules) | |||||
| { | |||||
| writeAutoGenWarningComment (out); | |||||
| out << " This is the header file that your files should include in order to get all the" << newLine | |||||
| << " JUCE library headers. You should avoid including the JUCE headers directly in" << newLine | |||||
| << " your own source files, because that wouldn't pick up the correct configuration" << newLine | |||||
| << " options for your app." << newLine | |||||
| << newLine | |||||
| << "*/" << newLine << newLine; | |||||
| out << "#pragma once" << newLine << newLine; | |||||
| if (getAppConfigFile().exists() && project.shouldUseAppConfig()) | |||||
| out << CodeHelpers::createIncludeStatement (Project::getAppConfigFilename()) << newLine; | |||||
| if (modules.size() > 0) | |||||
| { | |||||
| out << newLine; | |||||
| for (int i = 0; i < modules.size(); ++i) | |||||
| modules.getUnchecked(i)->writeIncludes (*this, out); | |||||
| out << newLine; | |||||
| } | |||||
| if (hasBinaryData && project.shouldIncludeBinaryInJuceHeader()) | |||||
| out << CodeHelpers::createIncludeStatement (project.getBinaryDataHeaderFile(), getAppConfigFile()) << newLine; | |||||
| out << newLine | |||||
| << "#if defined (JUCE_PROJUCER_VERSION) && JUCE_PROJUCER_VERSION < JUCE_VERSION" << newLine | |||||
| << " /** If you've hit this error then the version of the Projucer that was used to generate this project is" << newLine | |||||
| << " older than the version of the JUCE modules being included. To fix this error, re-save your project" << newLine | |||||
| << " using the latest version of the Projucer or, if you aren't using the Projucer to manage your project," << newLine | |||||
| << " remove the JUCE_PROJUCER_VERSION define from the AppConfig.h file." << newLine | |||||
| << " */" << newLine | |||||
| << " #error \"This project was last saved using an outdated version of the Projucer! Re-save this project with the latest version to fix this error.\"" << newLine | |||||
| << "#endif" << newLine | |||||
| << newLine; | |||||
| if (project.shouldAddUsingNamespaceToJuceHeader()) | |||||
| out << "#if ! DONT_SET_USING_JUCE_NAMESPACE" << newLine | |||||
| << " // If your code uses a lot of JUCE classes, then this will obviously save you" << newLine | |||||
| << " // a lot of typing, but can be disabled by setting DONT_SET_USING_JUCE_NAMESPACE." << newLine | |||||
| << " using namespace juce;" << newLine | |||||
| << "#endif" << newLine | |||||
| << newLine; | |||||
| out << "#if ! JUCE_DONT_DECLARE_PROJECTINFO" << newLine | |||||
| << "namespace ProjectInfo" << newLine | |||||
| << "{" << newLine | |||||
| << " const char* const projectName = " << CppTokeniserFunctions::addEscapeChars (project.getProjectNameString()).quoted() << ";" << newLine | |||||
| << " const char* const companyName = " << CppTokeniserFunctions::addEscapeChars (project.getCompanyNameString()).quoted() << ";" << newLine | |||||
| << " const char* const versionString = " << CppTokeniserFunctions::addEscapeChars (project.getVersionString()).quoted() << ";" << newLine | |||||
| << " const int versionNumber = " << project.getVersionAsHex() << ";" << newLine | |||||
| << "}" << newLine | |||||
| << "#endif" << newLine; | |||||
| } | |||||
| void writeAppHeader (const OwnedArray<LibraryModule>& modules) | |||||
| { | |||||
| MemoryOutputStream mem; | |||||
| mem.setNewLineString (projectLineFeed); | |||||
| writeAppHeader (mem, modules); | |||||
| saveGeneratedFile (Project::getJuceSourceHFilename(), mem); | |||||
| } | |||||
| void writeModuleCppWrappers (const OwnedArray<LibraryModule>& modules) | |||||
| { | |||||
| for (auto* module : modules) | |||||
| { | |||||
| for (auto& cu : module->getAllCompileUnits()) | |||||
| { | |||||
| MemoryOutputStream mem; | |||||
| mem.setNewLineString (projectLineFeed); | |||||
| writeAutoGenWarningComment (mem); | |||||
| mem << "*/" << newLine << newLine; | |||||
| if (project.shouldUseAppConfig()) | |||||
| mem << "#include " << Project::getAppConfigFilename().quoted() << newLine; | |||||
| mem << "#include <"; | |||||
| if (cu.file.getFileExtension() != ".r") // .r files are included without the path | |||||
| mem << module->getID() << "/"; | |||||
| mem << cu.file.getFileName() << ">" << newLine; | |||||
| replaceFileIfDifferent (generatedCodeFolder.getChildFile (cu.getFilenameForProxyFile()), mem); | |||||
| } | |||||
| } | |||||
| } | |||||
| void writeBinaryDataFiles() | |||||
| { | |||||
| auto binaryDataH = project.getBinaryDataHeaderFile(); | |||||
| JucerResourceFile resourceFile (project); | |||||
| if (resourceFile.getNumFiles() > 0) | |||||
| { | |||||
| auto dataNamespace = project.getBinaryDataNamespaceString().trim(); | |||||
| if (dataNamespace.isEmpty()) | |||||
| dataNamespace = "BinaryData"; | |||||
| resourceFile.setClassName (dataNamespace); | |||||
| auto maxSize = project.getMaxBinaryFileSize(); | |||||
| if (maxSize <= 0) | |||||
| maxSize = 10 * 1024 * 1024; | |||||
| auto r = resourceFile.write (maxSize); | |||||
| if (r.result.wasOk()) | |||||
| { | |||||
| hasBinaryData = true; | |||||
| for (auto& f : r.filesCreated) | |||||
| { | |||||
| filesCreated.add (f); | |||||
| generatedFilesGroup.addFileRetainingSortOrder (f, ! f.hasFileExtension (".h")); | |||||
| } | |||||
| } | |||||
| else | |||||
| { | |||||
| addError (r.result.getErrorMessage()); | |||||
| } | |||||
| } | |||||
| else | |||||
| { | |||||
| for (int i = 20; --i >= 0;) | |||||
| project.getBinaryDataCppFile (i).deleteFile(); | |||||
| binaryDataH.deleteFile(); | |||||
| } | |||||
| } | |||||
| void writeReadmeFile() | |||||
| { | |||||
| MemoryOutputStream out; | |||||
| out.setNewLineString (projectLineFeed); | |||||
| out << newLine | |||||
| << " Important Note!!" << newLine | |||||
| << " ================" << newLine | |||||
| << newLine | |||||
| << "The purpose of this folder is to contain files that are auto-generated by the Projucer," << newLine | |||||
| << "and ALL files in this folder will be mercilessly DELETED and completely re-written whenever" << newLine | |||||
| << "the Projucer saves your project." << newLine | |||||
| << newLine | |||||
| << "Therefore, it's a bad idea to make any manual changes to the files in here, or to" << newLine | |||||
| << "put any of your own files in here if you don't want to lose them. (Of course you may choose" << newLine | |||||
| << "to add the folder's contents to your version-control system so that you can re-merge your own" << newLine | |||||
| << "modifications after the Projucer has saved its changes)." << newLine; | |||||
| replaceFileIfDifferent (generatedCodeFolder.getChildFile ("ReadMe.txt"), out); | |||||
| } | |||||
| void addError (const String& message) | |||||
| { | |||||
| const ScopedLock sl (errorLock); | |||||
| errors.add (message); | |||||
| } | |||||
| String getAudioPluginDefines() const; | |||||
| void writeUnityScriptFile() | |||||
| { | |||||
| auto unityScriptContents = replaceLineFeeds (BinaryData::UnityPluginGUIScript_cs_in, | |||||
| projectLineFeed); | |||||
| auto projectName = Project::addUnityPluginPrefixIfNecessary (project.getProjectNameString()); | |||||
| unityScriptContents = unityScriptContents.replace ("${plugin_class_name}", projectName.replace (" ", "_")) | |||||
| .replace ("${plugin_name}", projectName) | |||||
| .replace ("${plugin_vendor}", project.getPluginManufacturerString()) | |||||
| .replace ("${plugin_description}", project.getPluginDescriptionString()); | |||||
| auto f = getGeneratedCodeFolder().getChildFile (project.getUnityScriptName()); | |||||
| MemoryOutputStream out; | |||||
| out << unityScriptContents; | |||||
| replaceFileIfDifferent (f, out); | |||||
| } | |||||
| void writeProjects (const OwnedArray<LibraryModule>&, const String&, bool); | |||||
| void runPostExportScript() | |||||
| { | |||||
| #if JUCE_WINDOWS | |||||
| auto cmdString = project.getPostExportShellCommandWinString(); | |||||
| #else | |||||
| auto cmdString = project.getPostExportShellCommandPosixString(); | |||||
| #endif | |||||
| auto shellCommand = cmdString.replace ("%%1%%", project.getProjectFolder().getFullPathName()); | |||||
| if (shellCommand.isNotEmpty()) | |||||
| { | |||||
| #if JUCE_WINDOWS | |||||
| StringArray argList ("cmd.exe", "/c"); | |||||
| #else | |||||
| StringArray argList ("/bin/sh", "-c"); | |||||
| #endif | |||||
| argList.add (shellCommand); | |||||
| ChildProcess shellProcess; | |||||
| if (! shellProcess.start (argList)) | |||||
| { | |||||
| addError ("Failed to run shell command: " + argList.joinIntoString (" ")); | |||||
| return; | |||||
| } | |||||
| if (! shellProcess.waitForProcessToFinish (10000)) | |||||
| { | |||||
| addError ("Timeout running shell command: " + argList.joinIntoString (" ")); | |||||
| return; | |||||
| } | |||||
| auto exitCode = shellProcess.getExitCode(); | |||||
| if (exitCode != 0) | |||||
| addError ("Shell command: " + argList.joinIntoString (" ") + " failed with exit code: " + String (exitCode)); | |||||
| } | |||||
| } | |||||
| void saveExporter (ProjectExporter* exporter, const OwnedArray<LibraryModule>& modules) | |||||
| { | |||||
| try | |||||
| { | |||||
| exporter->create (modules); | |||||
| if (! exporter->isCLion()) | |||||
| std::cout << "Finished saving: " << exporter->getName() << std::endl; | |||||
| } | |||||
| catch (build_tools::SaveError& error) | |||||
| { | |||||
| addError (error.message); | |||||
| } | |||||
| } | |||||
| class ExporterJob : public ThreadPoolJob | |||||
| { | |||||
| public: | |||||
| ExporterJob (ProjectSaver& ps, ProjectExporter* pe, | |||||
| const OwnedArray<LibraryModule>& moduleList) | |||||
| : ThreadPoolJob ("export"), | |||||
| owner (ps), exporter (pe), modules (moduleList) | |||||
| { | |||||
| } | |||||
| JobStatus runJob() override | |||||
| { | |||||
| owner.saveExporter (exporter.get(), modules); | |||||
| return jobHasFinished; | |||||
| } | |||||
| private: | |||||
| ProjectSaver& owner; | |||||
| std::unique_ptr<ProjectExporter> exporter; | |||||
| const OwnedArray<LibraryModule>& modules; | |||||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ExporterJob) | |||||
| }; | |||||
| //============================================================================== | |||||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ProjectSaver) | JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ProjectSaver) | ||||
| }; | }; | ||||
| @@ -350,18 +350,9 @@ void StoredSettings::checkJUCEPaths() | |||||
| projectDefaults.getPropertyAsValue (Ids::defaultJuceModulePath, nullptr) = File (juceFolder).getChildFile ("modules").getFullPathName(); | projectDefaults.getPropertyAsValue (Ids::defaultJuceModulePath, nullptr) = File (juceFolder).getChildFile ("modules").getFullPathName(); | ||||
| } | } | ||||
| bool StoredSettings::shouldAskUserToSetJUCEPath() noexcept | |||||
| bool StoredSettings::isJUCEPathIncorrect() | |||||
| { | { | ||||
| if (! isGlobalPathValid ({}, Ids::jucePath, getStoredPath (Ids::jucePath, TargetOS::getThisOS()).get().toString()) | |||||
| && getGlobalProperties().getValue ("dontAskAboutJUCEPath", {}).isEmpty()) | |||||
| return true; | |||||
| return false; | |||||
| } | |||||
| void StoredSettings::setDontAskAboutJUCEPathAgain() noexcept | |||||
| { | |||||
| getGlobalProperties().setValue ("dontAskAboutJUCEPath", 1); | |||||
| return ! isGlobalPathValid ({}, Ids::jucePath, getStoredPath (Ids::jucePath, TargetOS::getThisOS()).get().toString()); | |||||
| } | } | ||||
| static String getFallbackPathForOS (const Identifier& key, DependencyPathOS os) | static String getFallbackPathForOS (const Identifier& key, DependencyPathOS os) | ||||
| @@ -55,9 +55,7 @@ public: | |||||
| //============================================================================== | //============================================================================== | ||||
| ValueWithDefault getStoredPath (const Identifier& key, DependencyPathOS os); | ValueWithDefault getStoredPath (const Identifier& key, DependencyPathOS os); | ||||
| bool shouldAskUserToSetJUCEPath() noexcept; | |||||
| void setDontAskAboutJUCEPathAgain() noexcept; | |||||
| bool isJUCEPathIncorrect(); | |||||
| //============================================================================== | //============================================================================== | ||||
| AppearanceSettings appearance; | AppearanceSettings appearance; | ||||
| @@ -23,7 +23,7 @@ | |||||
| // Handy list of static Identifiers.. | // Handy list of static Identifiers.. | ||||
| namespace Ids | namespace Ids | ||||
| { | { | ||||
| #define DECLARE_ID(name) const Identifier name (#name) | |||||
| #define DECLARE_ID(name) const Identifier name (#name) | |||||
| DECLARE_ID (name); | DECLARE_ID (name); | ||||
| DECLARE_ID (file); | DECLARE_ID (file); | ||||
| @@ -359,8 +359,11 @@ namespace Ids | |||||
| DECLARE_ID (compilerFlagSchemes); | DECLARE_ID (compilerFlagSchemes); | ||||
| DECLARE_ID (compilerFlagScheme); | DECLARE_ID (compilerFlagScheme); | ||||
| DECLARE_ID (dontQueryForUpdate); | DECLARE_ID (dontQueryForUpdate); | ||||
| DECLARE_ID (dontAskAboutJUCEPath); | |||||
| DECLARE_ID (postExportShellCommandPosix); | DECLARE_ID (postExportShellCommandPosix); | ||||
| DECLARE_ID (postExportShellCommandWin); | DECLARE_ID (postExportShellCommandWin); | ||||
| DECLARE_ID (liveBuildEnabled); | |||||
| DECLARE_ID (guiEditorEnabled); | |||||
| const Identifier ID ("id"); | const Identifier ID ("id"); | ||||
| const Identifier ID_uppercase ("ID"); | const Identifier ID_uppercase ("ID"); | ||||
| @@ -19,7 +19,6 @@ | |||||
| #include "../../Application/jucer_Headers.h" | #include "../../Application/jucer_Headers.h" | ||||
| #include "../../ProjectSaving/jucer_ProjectExporter.h" | #include "../../ProjectSaving/jucer_ProjectExporter.h" | ||||
| #include "jucer_PIPGenerator.h" | #include "jucer_PIPGenerator.h" | ||||
| #include "../../Project/jucer_Module.h" | |||||
| //============================================================================== | //============================================================================== | ||||
| static void ensureSingleNewLineAfterIncludes (StringArray& lines) | static void ensureSingleNewLineAfterIncludes (StringArray& lines) | ||||
| @@ -105,7 +104,7 @@ PIPGenerator::PIPGenerator (const File& pip, const File& output, const File& juc | |||||
| if (userModulesPath != File()) | if (userModulesPath != File()) | ||||
| { | { | ||||
| availableUserModules.reset (new AvailableModuleList()); | |||||
| availableUserModules.reset (new AvailableModulesList()); | |||||
| availableUserModules->scanPaths ({ userModulesPath }); | availableUserModules->scanPaths ({ userModulesPath }); | ||||
| } | } | ||||
| } | } | ||||
| @@ -357,6 +356,8 @@ Result PIPGenerator::setProjectSettings (ValueTree& jucerTree) | |||||
| : "\"File->Global Paths...\"") | : "\"File->Global Paths...\"") | ||||
| + " menu item."); | + " menu item."); | ||||
| } | } | ||||
| jucerTree.setProperty (Ids::displaySplashScreen, true, nullptr); | |||||
| } | } | ||||
| setPropertyIfNotEmpty (Ids::defines, defines); | setPropertyIfNotEmpty (Ids::defines, defines); | ||||
| @@ -19,6 +19,7 @@ | |||||
| #pragma once | #pragma once | ||||
| #include "../Helpers/jucer_MiscUtilities.h" | #include "../Helpers/jucer_MiscUtilities.h" | ||||
| #include "../../Project/Modules/jucer_AvailableModulesList.h" | |||||
| //============================================================================== | //============================================================================== | ||||
| class PIPGenerator | class PIPGenerator | ||||
| @@ -70,13 +71,9 @@ private: | |||||
| //============================================================================== | //============================================================================== | ||||
| File pipFile, outputDirectory, juceModulesPath, userModulesPath; | File pipFile, outputDirectory, juceModulesPath, userModulesPath; | ||||
| std::unique_ptr<AvailableModuleList> availableUserModules; | |||||
| std::unique_ptr<AvailableModulesList> availableUserModules; | |||||
| var metadata; | var metadata; | ||||
| bool isTemp = false; | |||||
| bool useLocalCopy = false; | |||||
| bool isTemp = false, useLocalCopy = false; | |||||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (PIPGenerator) | JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (PIPGenerator) | ||||
| }; | }; | ||||
| @@ -20,19 +20,52 @@ | |||||
| //============================================================================== | //============================================================================== | ||||
| struct IconButton : public Button | |||||
| class IconButton : public Button | |||||
| { | { | ||||
| IconButton (String name, const Path* p) | |||||
| : Button (name), | |||||
| icon (p, Colours::transparentBlack) | |||||
| public: | |||||
| IconButton (String buttonName, Image imageToDisplay) | |||||
| : Button (buttonName), | |||||
| iconImage (imageToDisplay) | |||||
| { | { | ||||
| lookAndFeelChanged(); | |||||
| setTooltip (name); | |||||
| setTooltip (buttonName); | |||||
| } | |||||
| IconButton (String buttonName, Path pathToDisplay) | |||||
| : Button (buttonName), | |||||
| iconPath (pathToDisplay), | |||||
| iconImage (createImageFromPath (iconPath)) | |||||
| { | |||||
| setTooltip (buttonName); | |||||
| } | |||||
| void setImage (Image newImage) | |||||
| { | |||||
| iconImage = newImage; | |||||
| repaint(); | |||||
| } | |||||
| void setPath (Path newPath) | |||||
| { | |||||
| iconImage = createImageFromPath (newPath); | |||||
| repaint(); | |||||
| } | |||||
| void setBackgroundColour (Colour backgroundColourToUse) | |||||
| { | |||||
| backgroundColour = backgroundColourToUse; | |||||
| usingNonDefaultBackgroundColour = true; | |||||
| } | |||||
| void setIconInset (int newIconInset) | |||||
| { | |||||
| iconInset = newIconInset; | |||||
| repaint(); | |||||
| } | } | ||||
| void paintButton (Graphics& g, bool isMouseOverButton, bool isButtonDown) override | void paintButton (Graphics& g, bool isMouseOverButton, bool isButtonDown) override | ||||
| { | { | ||||
| auto alpha = 1.0f; | |||||
| float alpha = 1.0f; | |||||
| if (! isEnabled()) | if (! isEnabled()) | ||||
| { | { | ||||
| isMouseOverButton = false; | isMouseOverButton = false; | ||||
| @@ -41,43 +74,56 @@ struct IconButton : public Button | |||||
| alpha = 0.2f; | alpha = 0.2f; | ||||
| } | } | ||||
| auto backgroundColour = isIDEButton ? Colours::white | |||||
| : isUserButton ? findColour (userButtonBackgroundColourId) | |||||
| : findColour (defaultButtonBackgroundColourId); | |||||
| auto fill = isButtonDown ? backgroundColour.darker (0.5f) | |||||
| : isMouseOverButton ? backgroundColour.darker (0.2f) | |||||
| : backgroundColour; | |||||
| backgroundColour = isButtonDown ? backgroundColour.darker (0.5f) | |||||
| : isMouseOverButton ? backgroundColour.darker (0.2f) | |||||
| : backgroundColour; | |||||
| auto bounds = getLocalBounds().toFloat(); | |||||
| auto bounds = getLocalBounds(); | |||||
| if (isButtonDown) | if (isButtonDown) | ||||
| bounds.reduce (2, 2); | bounds.reduce (2, 2); | ||||
| Path ellipse; | Path ellipse; | ||||
| ellipse.addEllipse (bounds); | |||||
| g.reduceClipRegion(ellipse); | |||||
| ellipse.addEllipse (bounds.toFloat()); | |||||
| g.reduceClipRegion (ellipse); | |||||
| g.setColour (backgroundColour.withAlpha (alpha)); | |||||
| g.setColour (fill.withAlpha (alpha)); | |||||
| g.fillAll(); | g.fillAll(); | ||||
| if (iconImage != Image()) | |||||
| { | |||||
| if (isIDEButton) | |||||
| bounds.reduce (7, 7); | |||||
| g.setOpacity (alpha); | |||||
| g.drawImage (iconImage, bounds.reduced (iconInset).toFloat(), RectanglePlacement::fillDestination, false); | |||||
| } | |||||
| g.setOpacity (alpha); | |||||
| g.drawImage (iconImage, bounds, RectanglePlacement::fillDestination, false); | |||||
| } | |||||
| else | |||||
| { | |||||
| icon.withColour (findColour (defaultIconColourId).withAlpha (alpha)).draw (g, bounds.reduced (2, 2), false); | |||||
| } | |||||
| private: | |||||
| void lookAndFeelChanged() override | |||||
| { | |||||
| if (! usingNonDefaultBackgroundColour) | |||||
| backgroundColour = findColour (defaultButtonBackgroundColourId); | |||||
| if (iconPath != Path()) | |||||
| iconImage = createImageFromPath (iconPath); | |||||
| repaint(); | |||||
| } | |||||
| Image createImageFromPath (Path path) | |||||
| { | |||||
| Image image (Image::ARGB, 250, 250, true); | |||||
| Graphics g (image); | |||||
| g.setColour (findColour (defaultIconColourId)); | |||||
| g.fillPath (path, RectanglePlacement (RectanglePlacement::centred) | |||||
| .getTransformToFit (path.getBounds(), image.getBounds().toFloat())); | |||||
| return image; | |||||
| } | } | ||||
| Icon icon; | |||||
| Path iconPath; | |||||
| Image iconImage; | Image iconImage; | ||||
| Colour backgroundColour { findColour (defaultButtonBackgroundColourId) }; | |||||
| bool usingNonDefaultBackgroundColour = false; | |||||
| int iconInset = 2; | |||||
| bool isIDEButton = false; | |||||
| bool isUserButton = false; | |||||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (IconButton) | |||||
| }; | }; | ||||
| @@ -1515,29 +1515,6 @@ const uint8 clion[] = { 110,109,0,0,0,0,0,0,0,0,98,0,0,0,0,170,170,38,67,0,0,0,0 | |||||
| 0,192,218,67,98,0,128,44,67,0,192,218,67,0,0,220,66,0,192,218,67,252,255,61,66,0,192,218,67,98,252,255,61,66,170,138,213,67,252,255,61,66,84,85,208,67,252,255,61,66,254,31,203,67,99,101,0,0 }; | 0,192,218,67,98,0,128,44,67,0,192,218,67,0,0,220,66,0,192,218,67,252,255,61,66,0,192,218,67,98,252,255,61,66,170,138,213,67,252,255,61,66,84,85,208,67,252,255,61,66,254,31,203,67,99,101,0,0 }; | ||||
| } | } | ||||
| /*static void convertSVGPathToCppData (const String& pathString) | |||||
| { | |||||
| XmlElement svg ("svg"); | |||||
| XmlElement* path = svg.createNewChildElement ("path"); | |||||
| path->setAttribute ("d", pathString); | |||||
| std::unique_ptr<Drawable> d (Drawable::createFromSVG (svg)); | |||||
| DrawablePath* dp = dynamic_cast<DrawablePath*> (d->getChildComponent(0)); | |||||
| jassert (dp != nullptr); | |||||
| Path p (dp->getPath()); | |||||
| p.applyTransform (RectanglePlacement (RectanglePlacement::centred).getTransformToFit (p.getBounds(), | |||||
| Rectangle<float> (500.0f, 500.0f))); | |||||
| MemoryOutputStream data; | |||||
| p.writePathToStream (data); | |||||
| MemoryOutputStream out; | |||||
| CodeHelpers::writeDataAsCppLiteral (data.getMemoryBlock(), out, false, true); | |||||
| DBG (out.toString() << newLine); | |||||
| }*/ | |||||
| Icons::Icons() | Icons::Icons() | ||||
| { | { | ||||
| #define JUCE_LOAD_PATH_DATA(name) \ | #define JUCE_LOAD_PATH_DATA(name) \ | ||||
| @@ -22,18 +22,22 @@ | |||||
| //============================================================================== | //============================================================================== | ||||
| struct Icon | struct Icon | ||||
| { | { | ||||
| Icon() : path (nullptr) {} | |||||
| Icon (const Path& p, Colour c) : path (&p), colour (c) {} | |||||
| Icon (const Path* p, Colour c) : path (p), colour (c) {} | |||||
| Icon() = default; | |||||
| Icon (const Path& pathToUse, Colour pathColour) | |||||
| : path (pathToUse), | |||||
| colour (pathColour) | |||||
| { | |||||
| } | |||||
| void draw (Graphics& g, const juce::Rectangle<float>& area, bool isCrossedOut) const | void draw (Graphics& g, const juce::Rectangle<float>& area, bool isCrossedOut) const | ||||
| { | { | ||||
| if (path != nullptr) | |||||
| if (! path.isEmpty()) | |||||
| { | { | ||||
| g.setColour (colour); | g.setColour (colour); | ||||
| const RectanglePlacement placement (RectanglePlacement::centred | RectanglePlacement::onlyReduceInSize); | const RectanglePlacement placement (RectanglePlacement::centred | RectanglePlacement::onlyReduceInSize); | ||||
| g.fillPath (*path, placement.getTransformToFit (path->getBounds(), area)); | |||||
| g.fillPath (path, placement.getTransformToFit (path.getBounds(), area)); | |||||
| if (isCrossedOut) | if (isCrossedOut) | ||||
| { | { | ||||
| @@ -54,7 +58,7 @@ struct Icon | |||||
| return Icon (path, newColour); | return Icon (path, newColour); | ||||
| } | } | ||||
| const Path* path; | |||||
| Path path; | |||||
| Colour colour; | Colour colour; | ||||
| }; | }; | ||||
| @@ -19,6 +19,7 @@ | |||||
| #include "../../Application/jucer_Headers.h" | #include "../../Application/jucer_Headers.h" | ||||
| #include "jucer_ProjucerLookAndFeel.h" | #include "jucer_ProjucerLookAndFeel.h" | ||||
| #include "../../Application/jucer_Application.h" | #include "../../Application/jucer_Application.h" | ||||
| #include "../../Project/UI/jucer_ProjectContentComponent.h" | |||||
| //============================================================================== | //============================================================================== | ||||
| ProjucerLookAndFeel::ProjucerLookAndFeel() | ProjucerLookAndFeel::ProjucerLookAndFeel() | ||||
| @@ -40,24 +41,40 @@ void ProjucerLookAndFeel::drawTabButton (TabBarButton& button, Graphics& g, bool | |||||
| const auto alpha = button.isEnabled() ? ((isMouseOver || isMouseDown) ? 1.0f : 0.8f) : 0.3f; | const auto alpha = button.isEnabled() ? ((isMouseOver || isMouseDown) ? 1.0f : 0.8f) : 0.3f; | ||||
| #ifndef BUILDING_JUCE_COMPILEENGINE | #ifndef BUILDING_JUCE_COMPILEENGINE | ||||
| auto textColour = findColour (defaultTextColourId).withMultipliedAlpha (alpha); | |||||
| auto iconColour = findColour (button.isFrontTab() ? activeTabIconColourId | auto iconColour = findColour (button.isFrontTab() ? activeTabIconColourId | ||||
| : inactiveTabIconColourId); | : inactiveTabIconColourId); | ||||
| if (button.getName() == "Project") | |||||
| { | |||||
| auto icon = Icon (getIcons().closedFolder, iconColour.withMultipliedAlpha (alpha)); | |||||
| icon.draw (g, button.getTextArea().reduced (8, 8).toFloat(), false); | |||||
| } | |||||
| else if (button.getName() == "Build") | |||||
| auto isProjectTab = button.getName() == ProjectContentComponent::getProjectTabName(); | |||||
| auto isBuildTab = button.getName() == ProjectContentComponent::getBuildTabName(); | |||||
| if (isProjectTab || isBuildTab) | |||||
| { | { | ||||
| auto icon = Icon (getIcons().buildTab, iconColour.withMultipliedAlpha (alpha)); | |||||
| icon.draw (g, button.getTextArea().reduced (8, 8).toFloat(), false); | |||||
| auto icon = Icon (isProjectTab ? getIcons().closedFolder : getIcons().buildTab, | |||||
| iconColour.withMultipliedAlpha (alpha)); | |||||
| auto isSingleTab = (button.getTabbedButtonBar().getNumTabs() == 1); | |||||
| if (isSingleTab) | |||||
| { | |||||
| auto activeArea = button.getActiveArea().reduced (5); | |||||
| activeArea.removeFromLeft (15); | |||||
| icon.draw (g, activeArea.removeFromLeft (activeArea.getHeight()).toFloat(), false); | |||||
| activeArea.removeFromLeft (10); | |||||
| g.setColour (textColour); | |||||
| g.drawFittedText (isProjectTab ? ProjectContentComponent::getProjectTabName() : ProjectContentComponent::getBuildTabName(), | |||||
| activeArea, Justification::centredLeft, 1); | |||||
| } | |||||
| else | |||||
| { | |||||
| icon.draw (g, button.getTextArea().reduced (8, 8).toFloat(), false); | |||||
| } | |||||
| } | } | ||||
| else | else | ||||
| #endif | #endif | ||||
| { | { | ||||
| auto textColour = findColour (defaultTextColourId).withMultipliedAlpha (alpha); | |||||
| TextLayout textLayout; | TextLayout textLayout; | ||||
| LookAndFeel_V3::createTabTextLayout (button, (float) area.getWidth(), (float) area.getHeight(), textColour, textLayout); | LookAndFeel_V3::createTabTextLayout (button, (float) area.getWidth(), (float) area.getHeight(), textColour, textLayout); | ||||
| @@ -19,8 +19,6 @@ | |||||
| #include "../Application/jucer_Headers.h" | #include "../Application/jucer_Headers.h" | ||||
| #include "jucer_NewFileWizard.h" | #include "jucer_NewFileWizard.h" | ||||
| NewFileWizard::Type* createGUIComponentWizard(); | |||||
| //============================================================================== | //============================================================================== | ||||
| namespace | namespace | ||||
| { | { | ||||
| @@ -232,7 +230,6 @@ NewFileWizard::NewFileWizard() | |||||
| registerWizard (new NewCppAndHeaderFileWizard()); | registerWizard (new NewCppAndHeaderFileWizard()); | ||||
| registerWizard (new NewComponentFileWizard()); | registerWizard (new NewComponentFileWizard()); | ||||
| registerWizard (new NewSingleFileComponentFileWizard()); | registerWizard (new NewSingleFileComponentFileWizard()); | ||||
| registerWizard (createGUIComponentWizard()); | |||||
| } | } | ||||
| NewFileWizard::~NewFileWizard() | NewFileWizard::~NewFileWizard() | ||||
| @@ -122,11 +122,10 @@ struct NewProjectWizard | |||||
| projectFile = targetFolder.getChildFile (File::createLegalFileName (appTitle)) | projectFile = targetFolder.getChildFile (File::createLegalFileName (appTitle)) | ||||
| .withFileExtension (Project::projectFileExtension); | .withFileExtension (Project::projectFileExtension); | ||||
| std::unique_ptr<Project> project (new Project (projectFile)); | |||||
| auto project = std::make_unique<Project> (projectFile); | |||||
| if (failedFiles.size() == 0) | if (failedFiles.size() == 0) | ||||
| { | { | ||||
| project->setFile (projectFile); | |||||
| project->setTitle (appTitle); | project->setTitle (appTitle); | ||||
| if (! initialiseProject (*project)) | if (! initialiseProject (*project)) | ||||
| @@ -136,6 +135,9 @@ struct NewProjectWizard | |||||
| project->getProjectValue (Ids::useAppConfig) = false; | project->getProjectValue (Ids::useAppConfig) = false; | ||||
| project->getProjectValue (Ids::addUsingNamespaceToJuceHeader) = false; | project->getProjectValue (Ids::addUsingNamespaceToJuceHeader) = false; | ||||
| if (! ProjucerApplication::getApp().getLicenseController().getCurrentState().isPaidOrGPL()) | |||||
| project->getProjectValue (Ids::displaySplashScreen) = true; | |||||
| addExporters (*project, wc); | addExporters (*project, wc); | ||||
| addDefaultModules (*project, useGlobalPath); | addDefaultModules (*project, useGlobalPath); | ||||
| @@ -174,7 +176,7 @@ struct NewProjectWizard | |||||
| { | { | ||||
| auto defaultModules = getDefaultModules(); | auto defaultModules = getDefaultModules(); | ||||
| AvailableModuleList list; | |||||
| AvailableModulesList list; | |||||
| list.scanPaths ({ modulesFolder }); | list.scanPaths ({ modulesFolder }); | ||||
| for (auto& mod : list.getAllModules()) | for (auto& mod : list.getAllModules()) | ||||