Browse Source

Projucer: Added a simple sign-in form, added notification tray for project messages, general refactoring

tags/2021-05-28
ed 5 years ago
parent
commit
fba0295a44
69 changed files with 4895 additions and 2185 deletions
  1. +5
    -5
      extras/Projucer/Builds/LinuxMakefile/Makefile
  2. +106
    -26
      extras/Projucer/Builds/MacOSX/Projucer.xcodeproj/project.pbxproj
  3. +11
    -3
      extras/Projucer/Builds/VisualStudio2015/Projucer_App.vcxproj
  4. +36
    -6
      extras/Projucer/Builds/VisualStudio2015/Projucer_App.vcxproj.filters
  5. +11
    -3
      extras/Projucer/Builds/VisualStudio2017/Projucer_App.vcxproj
  6. +36
    -6
      extras/Projucer/Builds/VisualStudio2017/Projucer_App.vcxproj.filters
  7. +11
    -3
      extras/Projucer/Builds/VisualStudio2019/Projucer_App.vcxproj
  8. +36
    -6
      extras/Projucer/Builds/VisualStudio2019/Projucer_App.vcxproj.filters
  9. +2
    -3
      extras/Projucer/CMakeLists.txt
  10. +126
    -55
      extras/Projucer/JuceLibraryCode/BinaryData.cpp
  11. +2
    -2
      extras/Projucer/JuceLibraryCode/BinaryData.h
  12. +24
    -5
      extras/Projucer/Projucer.jucer
  13. +194
    -0
      extras/Projucer/Source/Application/UserAccount/jucer_LicenseController.h
  14. +274
    -0
      extras/Projucer/Source/Application/UserAccount/jucer_LicenseQueryThread.h
  15. +68
    -0
      extras/Projucer/Source/Application/UserAccount/jucer_LicenseState.h
  16. +273
    -0
      extras/Projucer/Source/Application/UserAccount/jucer_LoginFormComponent.h
  17. +2
    -16
      extras/Projucer/Source/Application/Windows/jucer_AboutWindowComponent.h
  18. +29
    -10
      extras/Projucer/Source/Application/Windows/jucer_GlobalPathsWindowComponent.h
  19. +1
    -0
      extras/Projucer/Source/Application/Windows/jucer_PIPCreatorWindowComponent.h
  20. +273
    -142
      extras/Projucer/Source/Application/jucer_Application.cpp
  21. +101
    -63
      extras/Projucer/Source/Application/jucer_Application.h
  22. +47
    -11
      extras/Projucer/Source/Application/jucer_AutoUpdater.cpp
  23. +5
    -2
      extras/Projucer/Source/Application/jucer_AutoUpdater.h
  24. +11
    -4
      extras/Projucer/Source/Application/jucer_CommandIDs.h
  25. +4
    -4
      extras/Projucer/Source/Application/jucer_CommandLine.cpp
  26. +1
    -0
      extras/Projucer/Source/Application/jucer_Main.cpp
  27. +129
    -69
      extras/Projucer/Source/Application/jucer_MainWindow.cpp
  28. +19
    -7
      extras/Projucer/Source/Application/jucer_MainWindow.h
  29. +23
    -0
      extras/Projucer/Source/BinaryData/Icons/gpl_logo.svg
  30. +0
    -50
      extras/Projucer/Source/BinaryData/Icons/huckleberry_icon.svg
  31. +1
    -1
      extras/Projucer/Source/CodeEditor/jucer_DocumentEditorComponent.cpp
  32. +6
    -6
      extras/Projucer/Source/CodeEditor/jucer_OpenDocumentManager.cpp
  33. +7
    -5
      extras/Projucer/Source/CodeEditor/jucer_OpenDocumentManager.h
  34. +6
    -2
      extras/Projucer/Source/ComponentEditor/UI/jucer_JucerDocumentEditor.cpp
  35. +9
    -6
      extras/Projucer/Source/ComponentEditor/jucer_JucerDocument.cpp
  36. +1
    -4
      extras/Projucer/Source/LiveBuildEngine/jucer_ClientServerMessages.h
  37. +1
    -5
      extras/Projucer/Source/LiveBuildEngine/jucer_CompileEngineClient.cpp
  38. +4
    -3
      extras/Projucer/Source/LiveBuildEngine/jucer_MessageIDs.h
  39. +195
    -0
      extras/Projucer/Source/Project/Modules/jucer_AvailableModulesList.h
  40. +86
    -0
      extras/Projucer/Source/Project/Modules/jucer_ModuleDescription.h
  41. +98
    -210
      extras/Projucer/Source/Project/Modules/jucer_Modules.cpp
  42. +15
    -74
      extras/Projucer/Source/Project/Modules/jucer_Modules.h
  43. +1
    -8
      extras/Projucer/Source/Project/UI/Sidebar/jucer_ExporterTreeItems.h
  44. +2
    -2
      extras/Projucer/Source/Project/UI/Sidebar/jucer_FileTreeItems.h
  45. +6
    -7
      extras/Projucer/Source/Project/UI/Sidebar/jucer_LiveBuildTab.h
  46. +69
    -79
      extras/Projucer/Source/Project/UI/Sidebar/jucer_ModuleTreeItems.h
  47. +3
    -11
      extras/Projucer/Source/Project/UI/Sidebar/jucer_TabComponents.h
  48. +135
    -89
      extras/Projucer/Source/Project/UI/jucer_HeaderComponent.cpp
  49. +22
    -14
      extras/Projucer/Source/Project/UI/jucer_HeaderComponent.h
  50. +3
    -3
      extras/Projucer/Source/Project/UI/jucer_ModulesInformationComponent.h
  51. +137
    -119
      extras/Projucer/Source/Project/UI/jucer_ProjectContentComponent.cpp
  52. +45
    -35
      extras/Projucer/Source/Project/UI/jucer_ProjectContentComponent.h
  53. +544
    -0
      extras/Projucer/Source/Project/UI/jucer_ProjectMessagesComponent.h
  54. +123
    -0
      extras/Projucer/Source/Project/UI/jucer_UserAvatarComponent.h
  55. +417
    -99
      extras/Projucer/Source/Project/jucer_Project.cpp
  56. +148
    -22
      extras/Projucer/Source/Project/jucer_Project.h
  57. +736
    -16
      extras/Projucer/Source/ProjectSaving/jucer_ProjectSaver.cpp
  58. +82
    -764
      extras/Projucer/Source/ProjectSaving/jucer_ProjectSaver.h
  59. +2
    -11
      extras/Projucer/Source/Settings/jucer_StoredSettings.cpp
  60. +1
    -3
      extras/Projucer/Source/Settings/jucer_StoredSettings.h
  61. +4
    -1
      extras/Projucer/Source/Utility/Helpers/jucer_PresetIDs.h
  62. +3
    -2
      extras/Projucer/Source/Utility/PIPs/jucer_PIPGenerator.cpp
  63. +3
    -6
      extras/Projucer/Source/Utility/PIPs/jucer_PIPGenerator.h
  64. +78
    -32
      extras/Projucer/Source/Utility/UI/jucer_IconButton.h
  65. +0
    -23
      extras/Projucer/Source/Utility/UI/jucer_Icons.cpp
  66. +10
    -6
      extras/Projucer/Source/Utility/UI/jucer_Icons.h
  67. +27
    -10
      extras/Projucer/Source/Utility/UI/jucer_ProjucerLookAndFeel.cpp
  68. +0
    -3
      extras/Projucer/Source/Wizards/jucer_NewFileWizard.cpp
  69. +5
    -3
      extras/Projucer/Source/Wizards/jucer_NewProjectWizard.h

+ 5
- 5
extras/Projucer/Builds/LinuxMakefile/Makefile View File

@@ -100,8 +100,8 @@ OBJECTS_APP := \
$(JUCE_OBJDIR)/jucer_CompileEngineClient_aee8c99c.o \
$(JUCE_OBJDIR)/jucer_CompileEngineServer_5d8914.o \
$(JUCE_OBJDIR)/jucer_DownloadCompileEngineThread_19bb4bb3.o \
$(JUCE_OBJDIR)/jucer_Modules_e20cbd10.o \
$(JUCE_OBJDIR)/jucer_HeaderComponent_1ebf72ba.o \
$(JUCE_OBJDIR)/jucer_Module_3f7666a5.o \
$(JUCE_OBJDIR)/jucer_Project_c131864a.o \
$(JUCE_OBJDIR)/jucer_ProjectExporter_cf377b25.o \
$(JUCE_OBJDIR)/jucer_ProjectSaver_4276639b.o \
@@ -302,14 +302,14 @@ $(JUCE_OBJDIR)/jucer_DownloadCompileEngineThread_19bb4bb3.o: ../../Source/LiveBu
@echo "Compiling jucer_DownloadCompileEngineThread.cpp"
$(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)
@echo "Compiling jucer_HeaderComponent.cpp"
@echo "Compiling jucer_Modules.cpp"
$(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)
@echo "Compiling jucer_Module.cpp"
@echo "Compiling jucer_HeaderComponent.cpp"
$(V_AT)$(CXX) $(JUCE_CXXFLAGS) $(JUCE_CPPFLAGS_APP) $(JUCE_CFLAGS_APP) -o "$@" -c "$<"

$(JUCE_OBJDIR)/jucer_Project_c131864a.o: ../../Source/Project/jucer_Project.cpp


+ 106
- 26
extras/Projucer/Builds/MacOSX/Projucer.xcodeproj/project.pbxproj View File

@@ -205,13 +205,13 @@
isa = PBXBuildFile;
fileRef = ADA538034910F52FDD2DC88D;
};
05A08E366EBF8D650974E695 = {
0E783907C6214ADD59EC95DC = {
isa = PBXBuildFile;
fileRef = 516D6D7C564DD5DF5C15CB06;
fileRef = F58B23995765C9FDBE28F871;
};
3FCA61C401007B243E2E9035 = {
05A08E366EBF8D650974E695 = {
isa = PBXBuildFile;
fileRef = F797071D88542C813CF7222A;
fileRef = 516D6D7C564DD5DF5C15CB06;
};
30B921C38DCEE787B294B746 = {
isa = PBXBuildFile;
@@ -548,13 +548,6 @@
path = "../../Source/Utility/PIPs/jucer_PIPGenerator.cpp";
sourceTree = "SOURCE_ROOT";
};
194457D806A26E793584AC0C = {
isa = PBXFileReference;
lastKnownFileType = file.svg;
name = "huckleberry_icon.svg";
path = "../../Source/BinaryData/Icons/huckleberry_icon.svg";
sourceTree = "SOURCE_ROOT";
};
1B0F18E1D96F727C062B05FA = {
isa = PBXFileReference;
lastKnownFileType = sourcecode.cpp.cpp;
@@ -660,6 +653,13 @@
path = "../../Source/LiveBuildEngine/jucer_ActivityList.h";
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 = {
isa = PBXFileReference;
lastKnownFileType = sourcecode.cpp.cpp;
@@ -709,6 +709,13 @@
path = "../../Source/Application/jucer_CommandLine.h";
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 = {
isa = PBXFileReference;
lastKnownFileType = sourcecode.c.h;
@@ -1094,6 +1101,13 @@
path = "../../Source/ComponentEditor/Properties/jucer_ComponentTextProperty.h";
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 = {
isa = PBXFileReference;
lastKnownFileType = sourcecode.cpp.objcpp;
@@ -1304,13 +1318,6 @@
path = "../../Source/ComponentEditor/UI/jucer_RelativePositionedRectangle.h";
sourceTree = "SOURCE_ROOT";
};
7211101FFA28400ADBB1D47A = {
isa = PBXFileReference;
lastKnownFileType = sourcecode.c.h;
name = "jucer_Module.h";
path = "../../Source/Project/jucer_Module.h";
sourceTree = "SOURCE_ROOT";
};
728FE25157E9874D50BBECB2 = {
isa = PBXFileReference;
lastKnownFileType = wrapper.framework;
@@ -1584,6 +1591,13 @@
path = "../../Source/Project/UI/jucer_ProjectContentComponent.h";
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 = {
isa = PBXFileReference;
lastKnownFileType = file.icns;
@@ -1899,6 +1913,13 @@
path = "../../Source/Utility/Helpers/jucer_FileHelpers.cpp";
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 = {
isa = PBXFileReference;
lastKnownFileType = file;
@@ -2102,6 +2123,13 @@
path = "../../Source/LiveBuildEngine/UI/jucer_BuildTabStatusComponent.h";
sourceTree = "SOURCE_ROOT";
};
CD267A28C16C4E79EB749005 = {
isa = PBXFileReference;
lastKnownFileType = file.svg;
name = "gpl_logo.svg";
path = "../../Source/BinaryData/Icons/gpl_logo.svg";
sourceTree = "SOURCE_ROOT";
};
CF6C8BD0DA3D8CD4E99EBADA = {
isa = PBXFileReference;
lastKnownFileType = wrapper.framework;
@@ -2438,6 +2466,13 @@
path = "../../Source/Utility/Helpers/jucer_CodeHelpers.cpp";
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 = {
isa = PBXFileReference;
lastKnownFileType = file.in;
@@ -2459,6 +2494,13 @@
path = "../../Source/ComponentEditor/Properties/jucer_ComponentBooleanProperty.h";
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 = {
isa = PBXFileReference;
lastKnownFileType = sourcecode.cpp.cpp;
@@ -2466,11 +2508,11 @@
path = "../../Source/Settings/jucer_StoredSettings.cpp";
sourceTree = "SOURCE_ROOT";
};
F797071D88542C813CF7222A = {
F63F46CA0A51C679867855A7 = {
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";
};
F7C74E934C954F6F1A3BE4F9 = {
@@ -2501,6 +2543,13 @@
path = "../../Source/CodeEditor/jucer_OpenDocumentManager.cpp";
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 = {
isa = PBXFileReference;
lastKnownFileType = sourcecode.c.h;
@@ -2522,6 +2571,13 @@
path = "../../Source/ComponentEditor/Properties/jucer_ComponentColourProperty.h";
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 = {
isa = PBXFileReference;
lastKnownFileType = sourcecode.c.h;
@@ -2550,6 +2606,17 @@
path = "../../Source/ComponentEditor/UI/jucer_PaintRoutinePanel.cpp";
sourceTree = "SOURCE_ROOT";
};
9D43579A76E23FBCE6B36333 = {
isa = PBXGroup;
children = (
F30DF63DBEFA4BEEF7C369FC,
2F0A7CA808B2FCCC9ED68992,
94146B40B41BF0AACF4359DD,
B6444A4A8DFD6828FF6BD1CB,
);
name = UserAccount;
sourceTree = "<group>";
};
EB1D55A76652399EB81CC1F0 = {
isa = PBXGroup;
children = (
@@ -2568,6 +2635,7 @@
BC67FD952A6F210A11A1ECB8 = {
isa = PBXGroup;
children = (
9D43579A76E23FBCE6B36333,
EB1D55A76652399EB81CC1F0,
7CA44FF0BA319517C6E39651,
EE690110171E1648FF2118B8,
@@ -2606,7 +2674,7 @@
69B478C992FA0B8C885946A6,
EAC1731150A7F79D59BAA0B6,
8F4D281E98808204E2846A7D,
194457D806A26E793584AC0C,
CD267A28C16C4E79EB749005,
432EC251A122071809471804,
B83C9BD89F31EA9E5E12A3C6,
8FEF6F5EA676B824C021EB6F,
@@ -2865,6 +2933,17 @@
name = LiveBuildEngine;
sourceTree = "<group>";
};
5108FDF7F62E617332FB13B0 = {
isa = PBXGroup;
children = (
FDABEE6B64546586368A4729,
F9A363BFBB6B1143C2E967C3,
F58B23995765C9FDBE28F871,
582F659B801F656C2B7C51B1,
);
name = Modules;
sourceTree = "<group>";
};
236D186F5A6536C59D6E751C = {
isa = PBXGroup;
children = (
@@ -2891,6 +2970,8 @@
B3528C08B84CBC950252EA69,
1B0F18E1D96F727C062B05FA,
92A66A8BD87F98EB6B4FB6D0,
F63F46CA0A51C679867855A7,
247768B490B9D759DDA79359,
);
name = UI;
sourceTree = "<group>";
@@ -2898,9 +2979,8 @@
89E9055A179B4C2019B4E1AE = {
isa = PBXGroup;
children = (
5108FDF7F62E617332FB13B0,
EBC037ECAAC8156B8B19DC69,
F797071D88542C813CF7222A,
7211101FFA28400ADBB1D47A,
BAC43B20E14A340CCF14119C,
BF3CEF080FA013E2778DCE90,
);
@@ -3424,8 +3504,8 @@
D25EBE02B55DB244BE0D5635,
85E7FCB0516EFF853FA7B380,
CC6C4D351BA9B473E5F95791,
0E783907C6214ADD59EC95DC,
05A08E366EBF8D650974E695,
3FCA61C401007B243E2E9035,
30B921C38DCEE787B294B746,
244567D3AE2E417A8CB2B95E,
26D6AEA321E80ABCC3CCCCD1,


+ 11
- 3
extras/Projucer/Builds/VisualStudio2015/Projucer_App.vcxproj View File

@@ -214,11 +214,11 @@
<ClCompile Include="..\..\Source\LiveBuildEngine\jucer_CompileEngineClient.cpp"/>
<ClCompile Include="..\..\Source\LiveBuildEngine\jucer_CompileEngineServer.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_ProjectContentComponent.cpp">
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\Source\Project\jucer_Module.cpp"/>
<ClCompile Include="..\..\Source\Project\jucer_Project.cpp"/>
<ClCompile Include="..\..\Source\ProjectSaving\jucer_ProjectExporter.cpp"/>
<ClCompile Include="..\..\Source\ProjectSaving\jucer_ProjectSaver.cpp"/>
@@ -1483,6 +1483,10 @@
<ClCompile Include="..\..\JuceLibraryCode\include_juce_gui_extra.cpp"/>
</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_EditorColourSchemeWindowComponent.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_ProjectBuildInfo.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_FileTreeItems.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_ModulesInformationComponent.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\ProjectSaving\jucer_ProjectExport_Android.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_visualStudio.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_icon.png"/>
<None Include="..\..\Source\BinaryData\Icons\wizard_AnimatedApp.svg"/>


+ 36
- 6
extras/Projucer/Builds/VisualStudio2015/Projucer_App.vcxproj.filters View File

@@ -2,6 +2,9 @@
<Project ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Projucer\Application\UserAccount">
<UniqueIdentifier>{DA27985D-8427-CE70-CA06-EAF7009CCC60}</UniqueIdentifier>
</Filter>
<Filter Include="Projucer\Application\Windows">
<UniqueIdentifier>{DC7E18A5-E854-3D99-627F-AAA88246B712}</UniqueIdentifier>
</Filter>
@@ -47,6 +50,9 @@
<Filter Include="Projucer\LiveBuildEngine">
<UniqueIdentifier>{0A3B9446-F50B-3D4E-230F-7ED493541A07}</UniqueIdentifier>
</Filter>
<Filter Include="Projucer\Project\Modules">
<UniqueIdentifier>{F5C79836-30DE-9DC7-9392-DAAB3F04C18E}</UniqueIdentifier>
</Filter>
<Filter Include="Projucer\Project\UI\Sidebar">
<UniqueIdentifier>{A0A94AE6-B447-151A-D0DA-FAE9B5410EBF}</UniqueIdentifier>
</Filter>
@@ -448,15 +454,15 @@
<ClCompile Include="..\..\Source\LiveBuildEngine\jucer_DownloadCompileEngineThread.cpp">
<Filter>Projucer\LiveBuildEngine</Filter>
</ClCompile>
<ClCompile Include="..\..\Source\Project\Modules\jucer_Modules.cpp">
<Filter>Projucer\Project\Modules</Filter>
</ClCompile>
<ClCompile Include="..\..\Source\Project\UI\jucer_HeaderComponent.cpp">
<Filter>Projucer\Project\UI</Filter>
</ClCompile>
<ClCompile Include="..\..\Source\Project\UI\jucer_ProjectContentComponent.cpp">
<Filter>Projucer\Project\UI</Filter>
</ClCompile>
<ClCompile Include="..\..\Source\Project\jucer_Module.cpp">
<Filter>Projucer\Project</Filter>
</ClCompile>
<ClCompile Include="..\..\Source\Project\jucer_Project.cpp">
<Filter>Projucer\Project</Filter>
</ClCompile>
@@ -1857,6 +1863,18 @@
</ClCompile>
</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">
<Filter>Projucer\Application\Windows</Filter>
</ClInclude>
@@ -2217,6 +2235,15 @@
<ClInclude Include="..\..\Source\LiveBuildEngine\jucer_SourceCodeRange.h">
<Filter>Projucer\LiveBuildEngine</Filter>
</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">
<Filter>Projucer\Project\UI\Sidebar</Filter>
</ClInclude>
@@ -2256,8 +2283,11 @@
<ClInclude Include="..\..\Source\Project\UI\jucer_ProjectContentComponent.h">
<Filter>Projucer\Project\UI</Filter>
</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 Include="..\..\Source\Project\jucer_Project.h">
<Filter>Projucer\Project</Filter>
@@ -3719,7 +3749,7 @@
<None Include="..\..\Source\BinaryData\Icons\export_xcode.svg">
<Filter>Projucer\BinaryData\Icons</Filter>
</None>
<None Include="..\..\Source\BinaryData\Icons\huckleberry_icon.svg">
<None Include="..\..\Source\BinaryData\Icons\gpl_logo.svg">
<Filter>Projucer\BinaryData\Icons</Filter>
</None>
<None Include="..\..\Source\BinaryData\Icons\juce-logo-with-text.svg">


+ 11
- 3
extras/Projucer/Builds/VisualStudio2017/Projucer_App.vcxproj View File

@@ -214,11 +214,11 @@
<ClCompile Include="..\..\Source\LiveBuildEngine\jucer_CompileEngineClient.cpp"/>
<ClCompile Include="..\..\Source\LiveBuildEngine\jucer_CompileEngineServer.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_ProjectContentComponent.cpp">
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\Source\Project\jucer_Module.cpp"/>
<ClCompile Include="..\..\Source\Project\jucer_Project.cpp"/>
<ClCompile Include="..\..\Source\ProjectSaving\jucer_ProjectExporter.cpp"/>
<ClCompile Include="..\..\Source\ProjectSaving\jucer_ProjectSaver.cpp"/>
@@ -1483,6 +1483,10 @@
<ClCompile Include="..\..\JuceLibraryCode\include_juce_gui_extra.cpp"/>
</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_EditorColourSchemeWindowComponent.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_ProjectBuildInfo.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_FileTreeItems.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_ModulesInformationComponent.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\ProjectSaving\jucer_ProjectExport_Android.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_visualStudio.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_icon.png"/>
<None Include="..\..\Source\BinaryData\Icons\wizard_AnimatedApp.svg"/>


+ 36
- 6
extras/Projucer/Builds/VisualStudio2017/Projucer_App.vcxproj.filters View File

@@ -2,6 +2,9 @@
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Projucer\Application\UserAccount">
<UniqueIdentifier>{DA27985D-8427-CE70-CA06-EAF7009CCC60}</UniqueIdentifier>
</Filter>
<Filter Include="Projucer\Application\Windows">
<UniqueIdentifier>{DC7E18A5-E854-3D99-627F-AAA88246B712}</UniqueIdentifier>
</Filter>
@@ -47,6 +50,9 @@
<Filter Include="Projucer\LiveBuildEngine">
<UniqueIdentifier>{0A3B9446-F50B-3D4E-230F-7ED493541A07}</UniqueIdentifier>
</Filter>
<Filter Include="Projucer\Project\Modules">
<UniqueIdentifier>{F5C79836-30DE-9DC7-9392-DAAB3F04C18E}</UniqueIdentifier>
</Filter>
<Filter Include="Projucer\Project\UI\Sidebar">
<UniqueIdentifier>{A0A94AE6-B447-151A-D0DA-FAE9B5410EBF}</UniqueIdentifier>
</Filter>
@@ -448,15 +454,15 @@
<ClCompile Include="..\..\Source\LiveBuildEngine\jucer_DownloadCompileEngineThread.cpp">
<Filter>Projucer\LiveBuildEngine</Filter>
</ClCompile>
<ClCompile Include="..\..\Source\Project\Modules\jucer_Modules.cpp">
<Filter>Projucer\Project\Modules</Filter>
</ClCompile>
<ClCompile Include="..\..\Source\Project\UI\jucer_HeaderComponent.cpp">
<Filter>Projucer\Project\UI</Filter>
</ClCompile>
<ClCompile Include="..\..\Source\Project\UI\jucer_ProjectContentComponent.cpp">
<Filter>Projucer\Project\UI</Filter>
</ClCompile>
<ClCompile Include="..\..\Source\Project\jucer_Module.cpp">
<Filter>Projucer\Project</Filter>
</ClCompile>
<ClCompile Include="..\..\Source\Project\jucer_Project.cpp">
<Filter>Projucer\Project</Filter>
</ClCompile>
@@ -1857,6 +1863,18 @@
</ClCompile>
</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">
<Filter>Projucer\Application\Windows</Filter>
</ClInclude>
@@ -2217,6 +2235,15 @@
<ClInclude Include="..\..\Source\LiveBuildEngine\jucer_SourceCodeRange.h">
<Filter>Projucer\LiveBuildEngine</Filter>
</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">
<Filter>Projucer\Project\UI\Sidebar</Filter>
</ClInclude>
@@ -2256,8 +2283,11 @@
<ClInclude Include="..\..\Source\Project\UI\jucer_ProjectContentComponent.h">
<Filter>Projucer\Project\UI</Filter>
</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 Include="..\..\Source\Project\jucer_Project.h">
<Filter>Projucer\Project</Filter>
@@ -3719,7 +3749,7 @@
<None Include="..\..\Source\BinaryData\Icons\export_xcode.svg">
<Filter>Projucer\BinaryData\Icons</Filter>
</None>
<None Include="..\..\Source\BinaryData\Icons\huckleberry_icon.svg">
<None Include="..\..\Source\BinaryData\Icons\gpl_logo.svg">
<Filter>Projucer\BinaryData\Icons</Filter>
</None>
<None Include="..\..\Source\BinaryData\Icons\juce-logo-with-text.svg">


+ 11
- 3
extras/Projucer/Builds/VisualStudio2019/Projucer_App.vcxproj View File

@@ -214,11 +214,11 @@
<ClCompile Include="..\..\Source\LiveBuildEngine\jucer_CompileEngineClient.cpp"/>
<ClCompile Include="..\..\Source\LiveBuildEngine\jucer_CompileEngineServer.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_ProjectContentComponent.cpp">
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\Source\Project\jucer_Module.cpp"/>
<ClCompile Include="..\..\Source\Project\jucer_Project.cpp"/>
<ClCompile Include="..\..\Source\ProjectSaving\jucer_ProjectExporter.cpp"/>
<ClCompile Include="..\..\Source\ProjectSaving\jucer_ProjectSaver.cpp"/>
@@ -1483,6 +1483,10 @@
<ClCompile Include="..\..\JuceLibraryCode\include_juce_gui_extra.cpp"/>
</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_EditorColourSchemeWindowComponent.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_ProjectBuildInfo.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_FileTreeItems.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_ModulesInformationComponent.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\ProjectSaving\jucer_ProjectExport_Android.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_visualStudio.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_icon.png"/>
<None Include="..\..\Source\BinaryData\Icons\wizard_AnimatedApp.svg"/>


+ 36
- 6
extras/Projucer/Builds/VisualStudio2019/Projucer_App.vcxproj.filters View File

@@ -2,6 +2,9 @@
<Project ToolsVersion="16.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Projucer\Application\UserAccount">
<UniqueIdentifier>{DA27985D-8427-CE70-CA06-EAF7009CCC60}</UniqueIdentifier>
</Filter>
<Filter Include="Projucer\Application\Windows">
<UniqueIdentifier>{DC7E18A5-E854-3D99-627F-AAA88246B712}</UniqueIdentifier>
</Filter>
@@ -47,6 +50,9 @@
<Filter Include="Projucer\LiveBuildEngine">
<UniqueIdentifier>{0A3B9446-F50B-3D4E-230F-7ED493541A07}</UniqueIdentifier>
</Filter>
<Filter Include="Projucer\Project\Modules">
<UniqueIdentifier>{F5C79836-30DE-9DC7-9392-DAAB3F04C18E}</UniqueIdentifier>
</Filter>
<Filter Include="Projucer\Project\UI\Sidebar">
<UniqueIdentifier>{A0A94AE6-B447-151A-D0DA-FAE9B5410EBF}</UniqueIdentifier>
</Filter>
@@ -448,15 +454,15 @@
<ClCompile Include="..\..\Source\LiveBuildEngine\jucer_DownloadCompileEngineThread.cpp">
<Filter>Projucer\LiveBuildEngine</Filter>
</ClCompile>
<ClCompile Include="..\..\Source\Project\Modules\jucer_Modules.cpp">
<Filter>Projucer\Project\Modules</Filter>
</ClCompile>
<ClCompile Include="..\..\Source\Project\UI\jucer_HeaderComponent.cpp">
<Filter>Projucer\Project\UI</Filter>
</ClCompile>
<ClCompile Include="..\..\Source\Project\UI\jucer_ProjectContentComponent.cpp">
<Filter>Projucer\Project\UI</Filter>
</ClCompile>
<ClCompile Include="..\..\Source\Project\jucer_Module.cpp">
<Filter>Projucer\Project</Filter>
</ClCompile>
<ClCompile Include="..\..\Source\Project\jucer_Project.cpp">
<Filter>Projucer\Project</Filter>
</ClCompile>
@@ -1857,6 +1863,18 @@
</ClCompile>
</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">
<Filter>Projucer\Application\Windows</Filter>
</ClInclude>
@@ -2217,6 +2235,15 @@
<ClInclude Include="..\..\Source\LiveBuildEngine\jucer_SourceCodeRange.h">
<Filter>Projucer\LiveBuildEngine</Filter>
</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">
<Filter>Projucer\Project\UI\Sidebar</Filter>
</ClInclude>
@@ -2256,8 +2283,11 @@
<ClInclude Include="..\..\Source\Project\UI\jucer_ProjectContentComponent.h">
<Filter>Projucer\Project\UI</Filter>
</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 Include="..\..\Source\Project\jucer_Project.h">
<Filter>Projucer\Project</Filter>
@@ -3719,7 +3749,7 @@
<None Include="..\..\Source\BinaryData\Icons\export_xcode.svg">
<Filter>Projucer\BinaryData\Icons</Filter>
</None>
<None Include="..\..\Source\BinaryData\Icons\huckleberry_icon.svg">
<None Include="..\..\Source\BinaryData\Icons\gpl_logo.svg">
<Filter>Projucer\BinaryData\Icons</Filter>
</None>
<None Include="..\..\Source\BinaryData\Icons\juce-logo-with-text.svg">


+ 2
- 3
extras/Projucer/CMakeLists.txt View File

@@ -57,12 +57,11 @@ target_sources(Projucer PRIVATE
Source/ComponentEditor/jucer_JucerDocument.cpp
Source/ComponentEditor/jucer_ObjectTypes.cpp
Source/ComponentEditor/jucer_PaintRoutine.cpp
Source/Licenses/jucer_LicenseController.cpp
Source/LiveBuildEngine/jucer_CompileEngineClient.cpp
Source/LiveBuildEngine/jucer_CompileEngineServer.cpp
Source/LiveBuildEngine/jucer_DownloadCompileEngineThread.cpp
Source/Project/Modules/jucer_Modules.cpp
Source/Project/UI/jucer_HeaderComponent.cpp
Source/Project/jucer_Module.cpp
Source/Project/jucer_Project.cpp
Source/ProjectSaving/jucer_ProjectExporter.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_visualStudio.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_icon.png
Source/BinaryData/Icons/wizard_AnimatedApp.svg


+ 126
- 55
extras/Projucer/JuceLibraryCode/BinaryData.cpp View File

@@ -1981,60 +1981,131 @@ static const unsigned 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[] =
"<?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 ==================
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 0x2505bd06: numBytes = 1706; return export_visualStudio_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 0x154a7275: numBytes = 45854; return juce_icon_png;
case 0x1f3b6d2f: numBytes = 5978; return wizard_AnimatedApp_svg;
@@ -7793,7 +7864,7 @@ const char* namedResourceList[] =
"export_linux_svg",
"export_visualStudio_svg",
"export_xcode_svg",
"huckleberry_icon_svg",
"gpl_logo_svg",
"jucelogowithtext_svg",
"juce_icon_png",
"wizard_AnimatedApp_svg",
@@ -7861,7 +7932,7 @@ const char* originalFilenames[] =
"export_linux.svg",
"export_visualStudio.svg",
"export_xcode.svg",
"huckleberry_icon.svg",
"gpl_logo.svg",
"juce-logo-with-text.svg",
"juce_icon.png",
"wizard_AnimatedApp.svg",


+ 2
- 2
extras/Projucer/JuceLibraryCode/BinaryData.h View File

@@ -59,8 +59,8 @@ namespace BinaryData
extern const char* export_xcode_svg;
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;
const int jucelogowithtext_svgSize = 5312;


+ 24
- 5
extras/Projucer/Projucer.jucer View File

@@ -101,6 +101,16 @@
</EXPORTFORMATS>
<MAINGROUP name="Projucer" id="NhrJq66R">
<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">
<FILE id="w1XB4w" name="jucer_AboutWindowComponent.h" compile="0" resource="0"
file="Source/Application/Windows/jucer_AboutWindowComponent.h"/>
@@ -177,8 +187,7 @@
file="Source/BinaryData/Icons/export_visualStudio.svg"/>
<FILE id="G0oYd6" name="export_xcode.svg" compile="0" resource="1"
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="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"/>
@@ -523,6 +532,15 @@
file="Source/LiveBuildEngine/jucer_SourceCodeRange.h"/>
</GROUP>
<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="{19B83596-13BE-A80E-2722-BB5CCDA111FA}" name="Sidebar">
<FILE id="bItg9o" name="jucer_ExporterTreeItems.h" compile="0" resource="0"
@@ -556,10 +574,11 @@
resource="0" file="Source/Project/UI/jucer_ProjectContentComponent.cpp"/>
<FILE id="z576fZ" name="jucer_ProjectContentComponent.h" compile="0"
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>
<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="Source/Project/jucer_Project.cpp"/>
<FILE id="bUjtVS" name="jucer_Project.h" compile="0" resource="0" file="Source/Project/jucer_Project.h"/>


+ 194
- 0
extras/Projucer/Source/Application/UserAccount/jucer_LicenseController.h View File

@@ -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)
};

+ 274
- 0
extras/Projucer/Source/Application/UserAccount/jucer_LicenseQueryThread.h View File

@@ -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)
};

+ 68
- 0
extras/Projucer/Source/Application/UserAccount/jucer_LicenseState.h View File

@@ -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;
};

+ 273
- 0
extras/Projucer/Source/Application/UserAccount/jucer_LoginFormComponent.h View File

@@ -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)
};

+ 2
- 16
extras/Projucer/Source/Application/Windows/jucer_AboutWindowComponent.h View File

@@ -50,23 +50,15 @@ public:
auto bounds = getLocalBounds();
bounds.removeFromBottom (20);
auto rightSlice = bounds.removeFromRight (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.setWidth (juceLogoBounds.getWidth() + 100);
juceLogoBounds.setHeight (juceLogoBounds.getHeight() + 100);
copyrightLabel.setBounds (leftSlice.removeFromBottom (20));
//==============================================================================
auto titleHeight = 40;
centreSlice.removeFromTop ((centreSlice.getHeight() / 2) - (titleHeight / 2));
@@ -86,9 +78,6 @@ public:
if (juceLogo != nullptr)
juceLogo->drawWithin (g, juceLogoBounds.translated (-75, -75), RectanglePlacement::centred, 1.0);
if (huckleberryLogo != nullptr)
huckleberryLogo->drawWithin (g, huckleberryLogoBounds, RectanglePlacement::centred, 1.0);
}
private:
@@ -98,13 +87,10 @@ private:
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,
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)
};

+ 29
- 10
extras/Projucer/Source/Application/Windows/jucer_GlobalPathsWindowComponent.h View File

@@ -23,7 +23,8 @@
//==============================================================================
class GlobalPathsWindowComponent : public Component,
private Timer,
private Value::Listener
private Value::Listener,
private ChangeListener
{
public:
GlobalPathsWindowComponent()
@@ -42,6 +43,16 @@ public:
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);
resetToDefaultsButton.onClick = [this] { resetCurrentOSPathsToDefaults(); };
@@ -64,6 +75,8 @@ public:
~GlobalPathsWindowComponent() override
{
getGlobalProperties().removeChangeListener (this);
auto juceValue = getAppSettings().getStoredPath (Ids::defaultJuceModulePath, TargetOS::getThisOS());
auto userValue = getAppSettings().getStoredPath (Ids::defaultUserModulePath, TargetOS::getThisOS());
@@ -86,12 +99,15 @@ public:
{
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));
rescanUserPathButton.setBounds (buttonBounds.removeFromLeft (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());
propertyViewport.setBounds (b);
@@ -145,6 +161,12 @@ private:
resized();
}
void changeListenerCallback (ChangeBroadcaster*) override
{
warnAboutJUCEPathButton.setToggleState (ProjucerApplication::getApp().shouldPromptUserAboutIncorrectJUCEPath(),
dontSendNotification);
}
//==============================================================================
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.");
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.");
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);
}
@@ -279,6 +297,7 @@ private:
Viewport propertyViewport;
PropertyGroupComponent propertyGroup { "Global Paths", { getIcons().openFolder, Colours::transparentBlack } };
ToggleButton warnAboutJUCEPathButton { "Warn about incorrect JUCE path" };
TextButton rescanJUCEPathButton { "Re-scan JUCE Modules" },
rescanUserPathButton { "Re-scan User Modules" },
resetToDefaultsButton { "Reset to Defaults" };


+ 1
- 0
extras/Projucer/Source/Application/Windows/jucer_PIPCreatorWindowComponent.h View File

@@ -18,6 +18,7 @@
#pragma once
//==============================================================================
static String getWidthLimitedStringFromVarArray (const var& varArray) noexcept
{


+ 273
- 142
extras/Projucer/Source/Application/jucer_Application.cpp View File

@@ -16,7 +16,7 @@
==============================================================================
*/
void createGUIEditorMenu (PopupMenu&);
PopupMenu createGUIEditorMenu();
void handleGUIEditorMenuCommand (int);
void registerGUIEditorCommands();
@@ -36,9 +36,7 @@ struct ProjucerApplication::MainMenuModel : public MenuBarModel
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
@@ -48,10 +46,6 @@ struct ProjucerApplication::MainMenuModel : public MenuBarModel
};
//==============================================================================
ProjucerApplication::ProjucerApplication() : isRunningCommandLine (false)
{
}
void ProjucerApplication::initialise (const String& commandLine)
{
if (commandLine.trimStart().startsWith ("--server"))
@@ -99,22 +93,6 @@ void ProjucerApplication::initialise (const String& commandLine)
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
triggerAsyncUpdate();
}
@@ -153,29 +131,37 @@ void ProjucerApplication::initialiseWindows (const String& commandLine)
void ProjucerApplication::handleAsyncUpdate()
{
licenseController = std::make_unique<LicenseController>();
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
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()
@@ -314,25 +300,52 @@ MenuBarModel* ProjucerApplication::getMenuModel()
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::newProjectFromClipboard);
menu.addCommandItem (commandManager.get(), CommandIDs::newPIP);
@@ -353,12 +366,7 @@ void ProjucerApplication::createFileMenu (PopupMenu& menu)
menu.addSubMenu ("Open Recent", recentFiles);
}
{
PopupMenu examples;
createExamplesPopupMenu (examples);
menu.addSubMenu ("Open Example", examples);
}
menu.addSubMenu ("Open Example", createExamplesPopupMenu());
menu.addSeparator();
menu.addCommandItem (commandManager.get(), CommandIDs::closeDocument);
@@ -373,17 +381,25 @@ void ProjucerApplication::createFileMenu (PopupMenu& menu)
menu.addCommandItem (commandManager.get(), CommandIDs::saveAndOpenInIDE);
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::redo);
menu.addSeparator();
@@ -398,10 +414,12 @@ void ProjucerApplication::createEditMenu (PopupMenu& menu)
menu.addCommandItem (commandManager.get(), CommandIDs::findSelection);
menu.addCommandItem (commandManager.get(), CommandIDs::findNext);
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::showProjectTab);
menu.addCommandItem (commandManager.get(), CommandIDs::showBuildTab);
@@ -412,10 +430,13 @@ void ProjucerApplication::createViewMenu (PopupMenu& menu)
menu.addSeparator();
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::buildNow);
menu.addCommandItem (commandManager.get(), CommandIDs::toggleContinuousBuild);
@@ -429,55 +450,60 @@ void ProjucerApplication::createBuildMenu (PopupMenu& menu)
menu.addSeparator();
menu.addCommandItem (commandManager.get(), CommandIDs::nextError);
menu.addCommandItem (commandManager.get(), CommandIDs::prevError);
return 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::goToNextWindow);
menu.addCommandItem (commandManager.get(), CommandIDs::closeWindow);
@@ -486,16 +512,22 @@ void ProjucerApplication::createWindowMenu (PopupMenu& menu)
int counter = 0;
for (auto* window : mainWindowList.windows)
{
if (window != nullptr)
{
if (auto* project = window->getProject())
menu.addItem (openWindowsBaseID + counter++, project->getProjectNameString());
}
}
menu.addSeparator();
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::goToNextDoc);
menu.addCommandItem (commandManager.get(), CommandIDs::goToCounterpart);
@@ -511,34 +543,46 @@ void ProjucerApplication::createDocumentMenu (PopupMenu& menu)
menu.addSeparator();
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::showSVGPathTool);
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.addSeparator();
menu.addCommandItem (commandManager.get(), CommandIDs::showAPIModules);
menu.addCommandItem (commandManager.get(), CommandIDs::showAPIClasses);
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::checkForNewVersion);
menu.addCommandItem (commandManager.get(), CommandIDs::enableNewVersionCheck);
menu.addSeparator();
menu.addCommandItem (commandManager.get(), CommandIDs::showGlobalPathsWindow);
return menu;
}
void ProjucerApplication::createExamplesPopupMenu (PopupMenu& menu) noexcept
PopupMenu ProjucerApplication::createExamplesPopupMenu() noexcept
{
PopupMenu menu;
numExamples = 0;
for (auto& dir : getSortedExampleDirectories())
{
@@ -561,8 +605,21 @@ void ProjucerApplication::createExamplesPopupMenu (PopupMenu& menu) noexcept
menu.addSeparator();
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()
{
@@ -907,12 +964,16 @@ void ProjucerApplication::getAllCommands (Array <CommandID>& commands)
CommandIDs::showGlobalPathsWindow,
CommandIDs::showUTF8Tool,
CommandIDs::showSVGPathTool,
CommandIDs::enableLiveBuild,
CommandIDs::enableGUIEditor,
CommandIDs::showAboutWindow,
CommandIDs::checkForNewVersion,
CommandIDs::enableNewVersionCheck,
CommandIDs::showForum,
CommandIDs::showAPIModules,
CommandIDs::showAPIClasses,
CommandIDs::showTutorials };
CommandIDs::showTutorials,
CommandIDs::loginLogout };
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);
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:
result.setInfo ("About Projucer", "Shows the Projucer's 'About' page.", CommandCategories::general, 0);
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);
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:
result.setInfo ("JUCE Community Forum", "Shows the JUCE community forum in a browser", CommandCategories::general, 0);
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);
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:
JUCEApplication::getCommandInfo (commandID, result);
break;
@@ -1031,17 +1126,21 @@ bool ProjucerApplication::perform (const InvocationInfo& info)
case CommandIDs::launchDemoRunner: launchDemoRunner(); break;
case CommandIDs::saveAll: saveAllDocuments(); 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::showUTF8Tool: showUTF8ToolWindow(); break;
case CommandIDs::showSVGPathTool: showSVGPathDataToolWindow(); break;
case CommandIDs::enableLiveBuild: enableOrDisableLiveBuild(); break;
case CommandIDs::enableGUIEditor: enableOrDisableGUIEditor(); break;
case CommandIDs::showGlobalPathsWindow: showPathsWindow (false); 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::showAPIModules: launchModulesBrowser(); break;
case CommandIDs::showAPIClasses: launchClassesBrowser(); break;
case CommandIDs::showTutorials: launchTutorialsBrowser(); break;
case CommandIDs::loginLogout: doLoginOrLogout(); break;
default: return JUCEApplication::perform (info);
}
@@ -1104,7 +1203,7 @@ void ProjucerApplication::saveAllDocuments()
pcc->refreshProjectTreeFileStatuses();
}
bool ProjucerApplication::closeAllDocuments (bool askUserToSave)
bool ProjucerApplication::closeAllDocuments (OpenDocumentManager::SaveIfNeeded askUserToSave)
{
return openDocumentManager.closeAll (askUserToSave);
}
@@ -1154,6 +1253,26 @@ void ProjucerApplication::showSVGPathDataToolWindow()
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()
{
if (aboutWindow != nullptr)
@@ -1230,6 +1349,26 @@ void ProjucerApplication::launchTutorialsBrowser()
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
{
@@ -1286,13 +1425,6 @@ PropertiesFile::Options ProjucerApplication::getPropertyFileOptionsFor (const St
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()
{
commandManager.reset (new ApplicationCommandManager());
@@ -1307,28 +1439,7 @@ void ProjucerApplication::initCommandManager()
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)
list.scanPathsAsync (paths);
@@ -1338,12 +1449,32 @@ void rescanModules (AvailableModuleList& list, const Array<File>& paths, bool as
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()
{
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)
@@ -1383,7 +1514,7 @@ void ProjucerApplication::setColourScheme (int index, bool saveSetting)
if (saveSetting)
{
auto& properties = settings->getGlobalProperties();
auto& properties = getGlobalProperties();
properties.setValue ("COLOUR SCHEME", index);
}
@@ -1403,7 +1534,7 @@ void ProjucerApplication::setEditorColourScheme (int index, bool saveSetting)
if (saveSetting)
{
auto& properties = settings->getGlobalProperties();
auto& properties = getGlobalProperties();
properties.setValue ("EDITOR COLOUR SCHEME", index);
}
@@ -1412,13 +1543,13 @@ void ProjucerApplication::setEditorColourScheme (int index, bool saveSetting)
getCommandManager().commandStatusChanged();
}
bool ProjucerApplication::isEditorColourSchemeADefaultScheme (const StringArray& schemes, int editorColourSchemeIndex)
bool isEditorColourSchemeADefaultScheme (const StringArray& schemes, int editorColourSchemeIndex)
{
auto& schemeName = schemes[editorColourSchemeIndex];
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 defaultLightEditorIndex = schemes.indexOf ("Default (Light)");


+ 101
- 63
extras/Projucer/Source/Application/jucer_Application.h View File

@@ -18,8 +18,9 @@
#pragma once
#include "UserAccount/jucer_LicenseController.h"
#include "jucer_MainWindow.h"
#include "../Project/jucer_Module.h"
#include "../Project/Modules/jucer_Modules.h"
#include "jucer_AutoUpdater.h"
#include "../CodeEditor/jucer_SourceCodeEditor.h"
#include "../Utility/UI/jucer_ProjucerLookAndFeel.h"
@@ -31,7 +32,7 @@ class ProjucerApplication : public JUCEApplication,
private AsyncUpdater
{
public:
ProjucerApplication();
ProjucerApplication() = default;
static ProjucerApplication& getApp();
static ApplicationCommandManager& getCommandManager();
@@ -42,7 +43,6 @@ public:
void systemRequestedQuit() override;
void deleteLogger();
//==============================================================================
const String getApplicationName() override { return "Projucer"; }
const String getApplicationVersion() override { return ProjectInfo::versionString; }
@@ -53,67 +53,34 @@ public:
//==============================================================================
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 getCommandInfo (CommandID commandID, ApplicationCommandInfo&) 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 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);
static bool isEditorColourSchemeADefaultScheme (const StringArray& schemes, int editorColourSchemeIndex);
static int getEditorColourSchemeForGUIColourScheme (const StringArray& schemes, int guiColourSchemeIndex);
//==============================================================================
void rescanJUCEPathModules();
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;
@@ -128,23 +95,42 @@ public:
OpenDocumentManager openDocumentManager;
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;
private:
//==============================================================================
void handleAsyncUpdate() override;
void initCommandManager();
void initCommandManager();
bool initialiseLogger (const char* filePrefix);
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> getSortedExampleFilesInDirectory (const File&) const noexcept;
bool findWindowAndOpenPIP (const File&);
@@ -155,22 +141,74 @@ private:
File tryToFindDemoRunnerProject();
void launchDemoRunner();
void showSetJUCEPathAlert();
void setColourScheme (int index, bool saveSetting);
void setEditorColourScheme (int index, bool saveSetting);
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;
AvailableModulesList jucePathModulesList, userPathsModulesList;
AvailableModuleList jucePathModuleList, userPathsModuleList;
std::unique_ptr<Component> utf8Window, svgPathWindow, aboutWindow, pathsWindow,
editorColourSchemeWindow, pipCreatorWindow;
std::unique_ptr<FileLogger> logger;
int numExamples = 0;
std::unique_ptr<AlertWindow> demoRunnerAlert;
std::unique_ptr<AlertWindow> pathAlert;
bool hasScannedForDemoRunnerExecutable = false, hasScannedForDemoRunnerProject = false;
File lastJUCEPath, lastDemoRunnerExectuableFile, lastDemoRunnerProjectFile;
#if JUCE_LINUX


+ 47
- 11
extras/Projucer/Source/Application/jucer_AutoUpdater.cpp View File

@@ -32,11 +32,11 @@ LatestVersionCheckerAndUpdater::~LatestVersionCheckerAndUpdater()
clearSingletonInstance();
}
void LatestVersionCheckerAndUpdater::checkForNewVersion (bool showAlerts)
void LatestVersionCheckerAndUpdater::checkForNewVersion (bool background)
{
if (! isThreadRunning())
{
showAlertWindows = showAlerts;
backgroundCheck = background;
startThread (3);
}
}
@@ -48,7 +48,7 @@ void LatestVersionCheckerAndUpdater::run()
if (info == nullptr)
{
if (showAlertWindows)
if (! backgroundCheck)
AlertWindow::showMessageBoxAsync (AlertWindow::WarningIcon,
"Update Server Communication Error",
"Failed to communicate with the JUCE update server.\n"
@@ -60,7 +60,7 @@ void LatestVersionCheckerAndUpdater::run()
if (! info->isNewerVersionThanCurrent())
{
if (showAlertWindows)
if (! backgroundCheck)
AlertWindow::showMessageBoxAsync (AlertWindow::InfoIcon,
"No New Version Available",
"Your JUCE version is up to date.");
@@ -99,7 +99,7 @@ void LatestVersionCheckerAndUpdater::run()
}
}
if (showAlertWindows)
if (! backgroundCheck)
AlertWindow::showMessageBoxAsync (AlertWindow::WarningIcon,
"Failed to find any new downloads",
"Please try again in a few minutes.");
@@ -137,15 +137,11 @@ public:
addAndMakeVisible (cancelButton);
cancelButton.onClick = [this]
{
if (dontAskAgainButton.getToggleState())
getGlobalProperties().setValue (Ids::dontQueryForUpdate.toString(), 1);
else
getGlobalProperties().removeValue (Ids::dontQueryForUpdate);
ProjucerApplication::getApp().setAutomaticVersionCheckingEnabled (! dontAskAgainButton.getToggleState());
exitModalStateWithResult (-1);
};
dontAskAgainButton.setToggleState (getGlobalProperties().getValue (Ids::dontQueryForUpdate, {}).isNotEmpty(), dontSendNotification);
dontAskAgainButton.setToggleState (! ProjucerApplication::getApp().isAutomaticVersionCheckingEnabled(), dontSendNotification);
addAndMakeVisible (dontAskAgainButton);
juceIcon = Drawable::createFromImageData (BinaryData::juce_icon_png,
@@ -287,10 +283,21 @@ void LatestVersionCheckerAndUpdater::askUserForLocationToDownload (const Version
void LatestVersionCheckerAndUpdater::askUserAboutNewVersion (const String& newVersionString,
const String& releaseNotes,
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);
if (auto* mm = ModalComponentManager::getInstance())
{
mm->attachCallback (dialogWindow.get(),
ModalCallbackFunction::create ([this, asset] (int result)
{
@@ -299,6 +306,35 @@ void LatestVersionCheckerAndUpdater::askUserAboutNewVersion (const String& newVe
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) } });
}
}
}
//==============================================================================


+ 5
- 2
extras/Projucer/Source/Application/jucer_AutoUpdater.h View File

@@ -29,7 +29,7 @@ public:
LatestVersionCheckerAndUpdater();
~LatestVersionCheckerAndUpdater() override;
void checkForNewVersion (bool showAlerts);
void checkForNewVersion (bool isBackgroundCheck);
//==============================================================================
JUCE_DECLARE_SINGLETON_SINGLETHREADED_MINIMAL (LatestVersionCheckerAndUpdater)
@@ -41,8 +41,11 @@ private:
void askUserForLocationToDownload (const VersionInfo::Asset&);
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<Component> dialogWindow;


+ 11
- 4
extras/Projucer/Source/Application/jucer_CommandIDs.h View File

@@ -48,6 +48,9 @@ namespace CommandIDs
showSVGPathTool = 0x300023,
showAboutWindow = 0x300024,
checkForNewVersion = 0x300025,
enableNewVersionCheck = 0x300026,
enableLiveBuild = 0x300027,
enableGUIEditor = 0x300028,
showProjectSettings = 0x300030,
showProjectTab = 0x300031,
@@ -91,10 +94,14 @@ namespace CommandIDs
nextError = 0x300080,
prevError = 0x300081,
showForum = 0x300090,
showAPIModules = 0x300091,
showAPIClasses = 0x300092,
showTutorials = 0x300093,
loginLogout = 0x300090,
showForum = 0x300100,
showAPIModules = 0x300101,
showAPIClasses = 0x300102,
showTutorials = 0x300103,
addNewGUIFile = 0x300200,
lastCommandIDEntry
};


+ 4
- 4
extras/Projucer/Source/Application/jucer_CommandLine.cpp View File

@@ -90,8 +90,8 @@ namespace
if (! justSaveResources)
rescanModulePathsIfNecessary();
auto error = justSaveResources ? project->saveResourcesOnly (project->getFile())
: project->saveProject (project->getFile(), true);
auto error = justSaveResources ? project->saveResourcesOnly()
: project->saveProject();
project.reset();
@@ -230,7 +230,7 @@ namespace
<< "Name: " << proj.project->getProjectNameString() << std::endl
<< "UID: " << proj.project->getProjectUIDString() << std::endl;
EnabledModuleList& modules = proj.project->getEnabledModules();
auto& modules = proj.project->getEnabledModules();
if (int numModules = modules.getNumModules())
{
@@ -307,7 +307,7 @@ namespace
var moduleInfo (new DynamicObject());
moduleInfo.getDynamicObject()->setProperty ("file", getModulePackageName (module));
moduleInfo.getDynamicObject()->setProperty ("info", module.moduleInfo.moduleInfo);
moduleInfo.getDynamicObject()->setProperty ("info", module.moduleInfo.getModuleInfo());
infoList.append (moduleInfo);
}
}


+ 1
- 0
extras/Projucer/Source/Application/jucer_Main.cpp View File

@@ -22,6 +22,7 @@
#include "../CodeEditor/jucer_OpenDocumentManager.h"
#include "../CodeEditor/jucer_SourceCodeEditor.h"
#include "../Utility/UI/PropertyComponents/jucer_FilePathPropertyComponent.h"
#include "../Project/UI/jucer_ProjectContentComponent.h"
#include "../Project/UI/Sidebar/jucer_TreeItemTypes.h"
#include "Windows/jucer_UTF8WindowComponent.h"
#include "Windows/jucer_SVGPathDataWindowComponent.h"


+ 129
- 69
extras/Projucer/Source/Application/jucer_MainWindow.cpp View File

@@ -22,6 +22,90 @@
#include "../Wizards/jucer_NewProjectWizardClasses.h"
#include "../Utility/UI/jucer_JucerTreeViewBase.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()
@@ -32,6 +116,8 @@ MainWindow::MainWindow()
false)
{
setUsingNativeTitleBar (true);
setResizable (true, false);
setResizeLimits (600, 500, 32000, 32000);
#if ! JUCE_MAC
setMenuBar (ProjucerApplication::getApp().getMenuModel());
@@ -39,9 +125,6 @@ MainWindow::MainWindow()
createProjectContentCompIfNeeded();
setResizable (true, false);
centreWithSize (800, 600);
auto& commandManager = ProjucerApplication::getCommandManager();
auto registerAllAppCommands = [&]
@@ -65,9 +148,10 @@ MainWindow::MainWindow()
setWantsKeyboardFocus (false);
getLookAndFeel().setColour (ColourSelector::backgroundColourId, Colours::transparentBlack);
projectNameValue.addListener (this);
setResizeLimits (600, 500, 32000, 32000);
centreWithSize (800, 600);
}
MainWindow::~MainWindow()
@@ -77,10 +161,11 @@ MainWindow::~MainWindow()
#endif
removeKeyListener (ProjucerApplication::getCommandManager().getKeyMappings());
// save the current size and position to our settings file..
getGlobalProperties().setValue ("lastMainWindowPos", getWindowStateAsString());
clearContentComponent();
currentProject.reset();
}
void MainWindow::createProjectContentCompIfNeeded()
@@ -127,7 +212,7 @@ void MainWindow::closeButtonPressed()
ProjucerApplication::getApp().mainWindowList.closeWindow (this);
}
bool MainWindow::closeCurrentProject (bool askUserToSave)
bool MainWindow::closeCurrentProject (OpenDocumentManager::SaveIfNeeded askUserToSave)
{
if (currentProject == nullptr)
return true;
@@ -144,7 +229,8 @@ bool MainWindow::closeCurrentProject (bool askUserToSave)
if (ProjucerApplication::getApp().openDocumentManager
.closeAllDocumentsUsingProject (*currentProject, askUserToSave))
{
if (! askUserToSave || (currentProject->saveIfNeededAndUserAgrees() == FileBasedDocument::savedOk))
if (askUserToSave == OpenDocumentManager::SaveIfNeeded::no
|| (currentProject->saveIfNeededAndUserAgrees() == FileBasedDocument::savedOk))
{
setProject (nullptr);
return true;
@@ -154,19 +240,16 @@ bool MainWindow::closeCurrentProject (bool askUserToSave)
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);
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)
@@ -174,7 +257,7 @@ void MainWindow::setProject (std::unique_ptr<Project> newProject)
if (newProject == nullptr)
{
getProjectContentComponent()->setProject (nullptr);
projectNameValue.referTo (Value());
projectNameValue.referTo ({});
currentProject.reset();
}
@@ -222,7 +305,7 @@ bool MainWindow::openFile (const File& file)
auto newDoc = std::make_unique<Project> (file);
auto result = newDoc->loadFrom (file, true);
if (result.wasOk() && closeCurrentProject (true))
if (result.wasOk() && closeCurrentProject (OpenDocumentManager::SaveIfNeeded::yes))
{
setProject (std::move (newDoc));
currentProject->setChangedFlag (false);
@@ -350,7 +433,7 @@ void MainWindow::openPIP (PIPGenerator& generator)
{
project->setTemporaryDirectory (generator.getOutputDirectory());
ProjectSaver liveBuildSaver (*project, project->getFile());
ProjectSaver liveBuildSaver (*project);
liveBuildSaver.saveContentNeededForLiveBuild();
if (auto* pcc = window->getProjectContentComponent())
@@ -440,49 +523,6 @@ void MainWindow::activeWindowStatusChanged()
pcc->updateMissingFileStatuses();
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()
@@ -498,6 +538,18 @@ void MainWindow::showStartPage()
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()
{
@@ -565,12 +617,11 @@ bool MainWindow::perform (const InvocationInfo& info)
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)
{
if (! windows[0]->closeCurrentProject (true))
if (! windows[0]->closeCurrentProject (OpenDocumentManager::SaveIfNeeded::yes))
return false;
windows.remove (0);
@@ -616,7 +667,7 @@ void MainWindowList::closeWindow (MainWindow* w)
else
#endif
{
if (w->closeCurrentProject (true))
if (w->closeCurrentProject (OpenDocumentManager::SaveIfNeeded::yes))
{
windows.removeObject (w);
saveCurrentlyOpenProjectList();
@@ -766,6 +817,15 @@ MainWindow* MainWindowList::getMainWindowForFile (const File& file)
return nullptr;
}
MainWindow* MainWindowList::getMainWindowWithLoginFormOpen()
{
for (auto* window : windows)
if (window->isShowingLoginForm())
return window;
return nullptr;
}
void MainWindowList::checkWindowBounds (MainWindow& windowToCheck)
{
auto avoidSuperimposedWindows = [&]


+ 19
- 7
extras/Projucer/Source/Application/jucer_MainWindow.h View File

@@ -18,8 +18,11 @@
#pragma once
#include "../Project/UI/jucer_ProjectContentComponent.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() override;
enum class OpenInIDE { no, yes };
//==============================================================================
void closeButtonPressed() override;
@@ -50,11 +55,15 @@ public:
void makeVisible();
void restoreWindowPosition();
bool closeCurrentProject (bool askToSave);
void moveProject (File newProjectFile);
bool closeCurrentProject (OpenDocumentManager::SaveIfNeeded askToSave);
void moveProject (File newProjectFile, OpenInIDE openInIDE);
void showStartPage();
void showLoginFormOverlay();
void hideLoginFormOverlay();
bool isShowingLoginForm() const noexcept { return loginFormOpen; }
bool isInterestedInFileDrag (const StringArray& files) override;
void filesDropped (const StringArray& filenames, int mouseX, int mouseY) override;
@@ -71,16 +80,18 @@ public:
bool shouldDropFilesWhenDraggedExternally (const DragAndDropTarget::SourceDetails& sourceDetails,
StringArray& files, bool& canMoveFiles) override;
private:
std::unique_ptr<Project> currentProject;
Value projectNameValue;
void valueChanged (Value&) override;
static const char* getProjectWindowPosName() { return "projectWindowPos"; }
void createProjectContentCompIfNeeded();
void setTitleBarIcon();
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)
};
@@ -105,6 +116,7 @@ public:
MainWindow* getFrontmostWindow (bool createIfNotFound = true);
MainWindow* getOrCreateEmptyWindow();
MainWindow* getMainWindowForFile (const File&);
MainWindow* getMainWindowWithLoginFormOpen();
Project* getFrontmostProject();


+ 23
- 0
extras/Projucer/Source/BinaryData/Icons/gpl_logo.svg
File diff suppressed because it is too large
View File


+ 0
- 50
extras/Projucer/Source/BinaryData/Icons/huckleberry_icon.svg View File

@@ -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>

+ 1
- 1
extras/Projucer/Source/CodeEditor/jucer_DocumentEditorComponent.cpp View File

@@ -19,7 +19,7 @@
#include "../Application/jucer_Headers.h"
#include "jucer_DocumentEditorComponent.h"
#include "../Application/jucer_Application.h"
#include "../Project/UI/jucer_ProjectContentComponent.h"
//==============================================================================
DocumentEditorComponent::DocumentEditorComponent (OpenDocumentManager::Document* doc)


+ 6
- 6
extras/Projucer/Source/CodeEditor/jucer_OpenDocumentManager.cpp View File

@@ -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 (saveIfNeeded)
if (saveIfNeeded == SaveIfNeeded::yes)
if (saveIfNeededAndUserAgrees (doc) != FileBasedDocument::savedOk)
return false;
@@ -206,12 +206,12 @@ bool OpenDocumentManager::closeDocument (int index, bool saveIfNeeded)
return true;
}
bool OpenDocumentManager::closeDocument (Document* document, bool saveIfNeeded)
bool OpenDocumentManager::closeDocument (Document* document, SaveIfNeeded 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;)
if (Document* d = documents[i])
@@ -219,7 +219,7 @@ void OpenDocumentManager::closeFile (const File& f, bool saveIfNeeded)
closeDocument (i, saveIfNeeded);
}
bool OpenDocumentManager::closeAll (bool askUserToSave)
bool OpenDocumentManager::closeAll (SaveIfNeeded askUserToSave)
{
for (int i = getNumOpenDocuments(); --i >= 0;)
if (! closeDocument (i, askUserToSave))
@@ -228,7 +228,7 @@ bool OpenDocumentManager::closeAll (bool askUserToSave)
return true;
}
bool OpenDocumentManager::closeAllDocumentsUsingProject (Project& project, bool saveIfNeeded)
bool OpenDocumentManager::closeAllDocumentsUsingProject (Project& project, SaveIfNeeded saveIfNeeded)
{
for (int i = documents.size(); --i >= 0;)
if (Document* d = documents[i])


+ 7
- 5
extras/Projucer/Source/CodeEditor/jucer_OpenDocumentManager.h View File

@@ -61,13 +61,15 @@ public:
Document* getOpenDocument (int index) const;
void clear();
enum class SaveIfNeeded { no, yes };
bool canOpenFile (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 saveAll();
FileBasedDocument::SaveResult saveIfNeededAndUserAgrees (Document* doc);


+ 6
- 2
extras/Projucer/Source/ComponentEditor/UI/jucer_JucerDocumentEditor.cpp View File

@@ -1210,11 +1210,14 @@ Image JucerDocumentEditor::createComponentLayerSnapshot() const
const int gridSnapMenuItemBase = 0x8723620;
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();
menu.addCommandItem (commandManager, CommandIDs::addNewGUIFile);
menu.addSeparator();
menu.addCommandItem (commandManager, JucerCommandIDs::editCompLayout);
menu.addCommandItem (commandManager, JucerCommandIDs::editCompGraphics);
menu.addSeparator();
@@ -1283,6 +1286,7 @@ void createGUIEditorMenu (PopupMenu& menu)
menu.addSubMenu ("Component Overlay", overlays, holder != nullptr);
}
return menu;
}
void handleGUIEditorMenuCommand (int);


+ 9
- 6
extras/Projucer/Source/ComponentEditor/jucer_JucerDocument.cpp View File

@@ -697,7 +697,7 @@ public:
{
if (header->save())
{
odm.closeFile (getFile().withFileExtension(".h"), false);
odm.closeFile (getFile().withFileExtension(".h"), OpenDocumentManager::SaveIfNeeded::no);
return true;
}
}
@@ -707,10 +707,13 @@ public:
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();
}
@@ -766,8 +769,8 @@ struct NewGUIComponentWizard : public NewFileWizard::Type
cpp->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 (cppFile, true);


+ 1
- 4
extras/Projucer/Source/LiveBuildEngine/jucer_ClientServerMessages.h View File

@@ -81,13 +81,10 @@ namespace MessageTypes
{
inline bool send (MessageHandler& target, const ValueTree& v)
{
//DBG ("Send: " << v.getType().toString());
bool result = target.sendMessage (v);
if (! result)
{
DBG ("*** Message failed: " << v.getType().toString());
}
Logger::outputDebugString ("*** Message failed: " + v.getType().toString());
return result;
}


+ 1
- 5
extras/Projucer/Source/LiveBuildEngine/jucer_CompileEngineClient.cpp View File

@@ -29,6 +29,7 @@
#include "jucer_CompileEngineClient.h"
#include "jucer_CompileEngineServer.h"
#include "jucer_CompileEngineSettings.h"
#include "../Project/UI/jucer_ProjectContentComponent.h"
#ifndef RUN_CLANG_IN_CHILD_PROCESS
#error
@@ -208,8 +209,6 @@ public:
if (isRunningApp && server != nullptr)
server->killServerWithoutMercy();
server.reset();
}
void restartServer()
@@ -511,9 +510,6 @@ CompileEngineChildProcess::CompileEngineChildProcess (Project& p)
CompileEngineChildProcess::~CompileEngineChildProcess()
{
ProjucerApplication::getApp().openDocumentManager.removeListener (this);
process.reset();
lastComponentList.clear();
}
void CompileEngineChildProcess::createProcess()


+ 4
- 3
extras/Projucer/Source/LiveBuildEngine/jucer_MessageIDs.h View File

@@ -18,10 +18,11 @@
#pragma once
#define DECLARE_ID(name) static const Identifier name (#name)
namespace MessageTypes
{
#define DECLARE_ID(name) const Identifier name (#name)
DECLARE_ID (PING);
DECLARE_ID (BUILDINFO);
DECLARE_ID (COMPILEUNIT);
@@ -50,6 +51,6 @@ namespace MessageTypes
DECLARE_ID (LAUNCH_APP);
DECLARE_ID (FOREGROUND);
DECLARE_ID (QUIT_SERVER);
}
#undef DECLARE_ID
#undef DECLARE_ID
}

+ 195
- 0
extras/Projucer/Source/Project/Modules/jucer_AvailableModulesList.h View File

@@ -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)
};

+ 86
- 0
extras/Projucer/Source/Project/Modules/jucer_ModuleDescription.h View File

@@ -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;
};

extras/Projucer/Source/Project/jucer_Module.cpp → extras/Projucer/Source/Project/Modules/jucer_Modules.cpp View File

@@ -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)
@@ -201,7 +29,7 @@ LibraryModule::LibraryModule (const ModuleDescription& d)
void LibraryModule::writeIncludes (ProjectSaver& projectSaver, OutputStream& out)
{
auto& project = projectSaver.project;
auto& project = projectSaver.getProject();
auto& modules = project.getEnabledModules();
auto moduleID = getID();
@@ -215,7 +43,7 @@ void LibraryModule::writeIncludes (ProjectSaver& projectSaver, OutputStream& out
projectSaver.copyFolder (juceModuleFolder, localModuleFolder);
}
out << "#include <" << moduleInfo.moduleFolder.getFileName() << "/"
out << "#include <" << moduleInfo.getModuleFolder().getFileName() << "/"
<< moduleInfo.getHeader().getFileName()
<< ">" << newLine;
}
@@ -300,26 +128,26 @@ void LibraryModule::addLibsToExporter (ProjectExporter& exporter) const
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, ", ", {});
parseAndAddLibsToList (xcodeExporter.xcodeLibs, moduleInfo.moduleInfo [exporter.isOSX() ? "OSXLibs" : "iOSLibs"].toString());
parseAndAddLibsToList (xcodeExporter.xcodeLibs, moduleInfo.getModuleInfo() [exporter.isOSX() ? "OSXLibs" : "iOSLibs"].toString());
}
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())
{
if (exporter.isCodeBlocks())
parseAndAddLibsToList (exporter.mingwLibs, moduleInfo.moduleInfo ["mingwLibs"].toString());
parseAndAddLibsToList (exporter.mingwLibs, moduleInfo.getModuleInfo() ["mingwLibs"].toString());
else
parseAndAddLibsToList (exporter.windowsLibs, moduleInfo.moduleInfo ["windowsLibs"].toString());
parseAndAddLibsToList (exporter.windowsLibs, moduleInfo.getModuleInfo() ["windowsLibs"].toString());
}
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)
{
}
StringArray EnabledModuleList::getAllModules() const
StringArray EnabledModulesList::getAllModules() const
{
StringArray moduleIDs;
@@ -559,13 +387,13 @@ StringArray EnabledModuleList::getAllModules() const
return moduleIDs;
}
void EnabledModuleList::createRequiredModules (OwnedArray<LibraryModule>& modules)
void EnabledModulesList::createRequiredModules (OwnedArray<LibraryModule>& modules)
{
for (int i = 0; i < getNumModules(); ++i)
modules.add (new LibraryModule (getModuleInfo (getModuleID (i))));
}
void EnabledModuleList::sortAlphabetically()
void EnabledModulesList::sortAlphabetically()
{
struct ModuleTreeSorter
{
@@ -579,14 +407,14 @@ void EnabledModuleList::sortAlphabetically()
state.sort (sorter, getUndoManager(), false);
}
File EnabledModuleList::getDefaultModulesFolder() const
File EnabledModulesList::getDefaultModulesFolder() const
{
File globalPath (getAppSettings().getStoredPath (Ids::defaultJuceModulePath, TargetOS::getThisOS()).get().toString());
if (globalPath.exists())
return globalPath;
for (auto& exporterPathModule : project.getExporterPathsModuleList().getAllModules())
for (auto& exporterPathModule : project.getExporterPathsModulesList().getAllModules())
{
auto f = exporterPathModule.second;
@@ -597,12 +425,12 @@ File EnabledModuleList::getDefaultModulesFolder() const
return File::getCurrentWorkingDirectory();
}
ModuleDescription EnabledModuleList::getModuleInfo (const String& moduleID)
ModuleDescription EnabledModulesList::getModuleInfo (const String& moduleID) const
{
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();
}
@@ -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;
getDependencies (project, moduleID, dependencies);
@@ -633,11 +461,31 @@ StringArray EnabledModuleList::getExtraDependenciesNeeded (const String& moduleI
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();
if (projectCppStandard == "latest")
if (projectCppStandard == Project::getCppStandardVars().getLast().toString())
return false;
auto moduleCppStandard = getModuleInfo (moduleID).getMinimumCppStandard();
@@ -645,40 +493,40 @@ bool EnabledModuleList::doesModuleHaveHigherCppStandardThanProject (const String
return (moduleCppStandard.getIntValue() > projectCppStandard.getIntValue());
}
bool EnabledModuleList::shouldUseGlobalPath (const String& moduleID) const
bool EnabledModulesList::shouldUseGlobalPath (const String& moduleID) const
{
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)
.getPropertyAsValue (Ids::useGlobalPath, getUndoManager());
}
bool EnabledModuleList::shouldShowAllModuleFilesInProject (const String& moduleID) const
bool EnabledModulesList::shouldShowAllModuleFilesInProject (const String& moduleID) const
{
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)
.getPropertyAsValue (Ids::showAllCode, getUndoManager());
}
bool EnabledModuleList::shouldCopyModuleFilesLocally (const String& moduleID) const
bool EnabledModulesList::shouldCopyModuleFilesLocally (const String& moduleID) const
{
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)
.getPropertyAsValue (Ids::useLocalCopy, getUndoManager());
}
bool EnabledModuleList::areMostModulesUsingGlobalPath() const
bool EnabledModulesList::areMostModulesUsingGlobalPath() const
{
int numYes = 0, numNo = 0;
@@ -693,7 +541,7 @@ bool EnabledModuleList::areMostModulesUsingGlobalPath() const
return numYes > numNo;
}
bool EnabledModuleList::areMostModulesCopiedLocally() const
bool EnabledModulesList::areMostModulesCopiedLocally() const
{
int numYes = 0, numNo = 0;
@@ -708,7 +556,47 @@ bool EnabledModuleList::areMostModulesCopiedLocally() const
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);
@@ -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;
@@ -754,7 +642,7 @@ void EnabledModuleList::addModuleInteractive (const String& moduleID)
addModuleFromUserSelectedFile();
}
void EnabledModuleList::addModuleFromUserSelectedFile()
void EnabledModulesList::addModuleFromUserSelectedFile()
{
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);
@@ -785,11 +673,11 @@ void EnabledModuleList::addModuleOfferingToCopy (const File& f, bool isFromUserS
return;
}
addModule (m.moduleFolder, areMostModulesCopiedLocally(),
addModule (m.getModuleFolder(), areMostModulesCopiedLocally(),
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;)
if (state.getChild(i) [Ids::ID] == moduleID)

extras/Projucer/Source/Project/jucer_Module.h → extras/Projucer/Source/Project/Modules/jucer_Modules.h View File

@@ -18,37 +18,11 @@
#pragma once
#include "jucer_Project.h"
#include "../jucer_Project.h"
class ProjectExporter;
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
{
@@ -102,51 +76,10 @@ private:
};
//==============================================================================
class AvailableModuleList
class EnabledModulesList
{
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; }
@@ -160,11 +93,14 @@ public:
int getNumModules() const { return state.getNumChildren(); }
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;
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;
Value shouldUseGlobalPathValue (const String& moduleID) const;
@@ -178,6 +114,11 @@ public:
bool areMostModulesUsingGlobalPath() const;
bool areMostModulesCopiedLocally() const;
StringArray getModulesWithHigherCppStandardThanProject() const;
StringArray getModulesWithMissingDependencies() const;
String getHighestModuleCppStandard() const;
//==============================================================================
void addModule (const File& moduleManifestFile, bool copyLocally, bool useGlobalPath);
void addModuleInteractive (const String& moduleID);
@@ -192,5 +133,5 @@ private:
Project& project;
ValueTree state;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (EnabledModuleList)
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (EnabledModulesList)
};

+ 1
- 8
extras/Projucer/Source/Project/UI/Sidebar/jucer_ExporterTreeItems.h View File

@@ -108,18 +108,11 @@ public:
void handlePopupMenuResult (int resultCode) override
{
if (resultCode == 1)
{
exporter->addNewConfiguration (false);
}
else if (resultCode == 2)
{
const ScopedValueSetter<String> valueSetter (project.specifiedExporterToSave, exporter->getName(), {});
project.save (true, true);
}
project.saveProject (exporter.get());
else if (resultCode == 3)
{
deleteAllSelectedItems();
}
}
var getDragSourceDescription() override


+ 2
- 2
extras/Projucer/Source/Project/UI/Sidebar/jucer_FileTreeItems.h View File

@@ -110,7 +110,7 @@ public:
{
auto f = filesToTrash.getUnchecked(i);
om.closeFile (f, false);
om.closeFile (f, OpenDocumentManager::SaveIfNeeded::no);
if (! f.moveToTrash())
{
@@ -129,7 +129,7 @@ public:
pcc->hideEditor();
}
om.closeFile (itemToRemove->getFile(), false);
om.closeFile (itemToRemove->getFile(), OpenDocumentManager::SaveIfNeeded::no);
itemToRemove->deleteItem();
}
}


+ 6
- 7
extras/Projucer/Source/Project/UI/Sidebar/jucer_LiveBuildTab.h View File

@@ -61,9 +61,8 @@ class LiveBuildTab : public Component,
public:
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>())
pcc->showLiveBuildSettings();
@@ -122,9 +121,9 @@ public:
{
auto bounds = getLocalBounds();
settingsButton->setBounds (bounds.removeFromBottom (25)
.removeFromRight (25)
.reduced (3));
settingsButton.setBounds (bounds.removeFromBottom (25)
.removeFromRight (25)
.reduced (3));
if (errorMessageLabel != nullptr)
{
@@ -155,7 +154,7 @@ public:
private:
OwnedArray<ConcertinaHeader> headers;
ConcertinaPanel concertinaPanel;
std::unique_ptr<IconButton> settingsButton;
IconButton settingsButton { "Settings", getIcons().settings };
std::unique_ptr<TextButton> downloadButton, enableButton;
std::unique_ptr<Label> errorMessageLabel;


+ 69
- 79
extras/Projucer/Source/Project/UI/Sidebar/jucer_ModuleTreeItems.h View File

@@ -361,17 +361,21 @@ private:
void fixDependencies()
{
if (! tryToFix())
auto& enabledModules = project.getEnabledModules();
if (enabledModules.tryToFixMissingDependencies (moduleID))
{
missingDependencies.clear();
}
else
{
missingDependencies = enabledModules.getExtraDependenciesNeeded (moduleID);
AlertWindow::showMessageBoxAsync (AlertWindow::WarningIcon,
"Adding Missing Dependencies",
"Couldn't locate some of these modules - you'll need to find their "
"folders manually and add them to the list.");
return;
}
refreshAndReselectItem();
}
void resized() override
@@ -381,59 +385,11 @@ private:
private:
Project& project;
String moduleID;
StringArray missingDependencies;
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)
};
@@ -468,28 +424,30 @@ private:
//==============================================================================
class EnabledModulesItem : public ProjectTreeItemBase,
private Value::Listener,
private AvailableModuleList::Listener
private AvailableModulesList::Listener
{
public:
EnabledModulesItem (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.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
{
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; }
@@ -539,7 +497,7 @@ public:
}
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().areMostModulesUsingGlobalPath());
}
@@ -560,7 +518,7 @@ public:
// JUCE path
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.addSeparator();
@@ -572,7 +530,7 @@ public:
index = 200;
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.addSeparator();
@@ -584,7 +542,7 @@ public:
index = 300;
PopupMenu exporterPathModules;
for (auto& mod : project.getExporterPathsModuleList().getAllModules())
for (auto& mod : project.getExporterPathsModulesList().getAllModules())
exporterPathModules.addItem (index++, mod.first, ! enabledModules.isModuleEnabled (mod.first));
exporterPathModules.addSeparator();
@@ -615,22 +573,22 @@ public:
}
else if (resultCode > 0)
{
std::vector<AvailableModuleList::ModuleIDAndFolder> list;
std::vector<AvailableModulesList::ModuleIDAndFolder> list;
int offset = -1;
if (resultCode < 200)
{
list = ProjucerApplication::getApp().getJUCEPathModuleList().getAllModules();
list = ProjucerApplication::getApp().getJUCEPathModulesList().getAllModules();
offset = 100;
}
else if (resultCode < 300)
{
list = ProjucerApplication::getApp().getUserPathsModuleList().getAllModules();
list = ProjucerApplication::getApp().getUserPathsModulesList().getAllModules();
offset = 200;
}
else if (resultCode < 400)
{
list = project.getExporterPathsModuleList().getAllModules();
list = project.getExporterPathsModulesList().getAllModules();
offset = 300;
}
@@ -646,13 +604,20 @@ public:
void refreshIfNeeded (ValueTree& changedTree)
{
if (changedTree == moduleListTree)
if (changedTree == modulesListTree)
{
auto selectedID = getSelectedItemID();
refreshSubItems();
if (selectedID.isNotEmpty())
setSelectedItem (selectedID);
}
}
private:
Project& project;
ValueTree moduleListTree;
ValueTree modulesListTree;
Value projectCppStandardValue;
//==============================================================================
@@ -676,22 +641,47 @@ private:
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());
}
void availableModulesChanged() override
void availableModulesChanged (AvailableModulesList*) override
{
removeDuplicateModules();
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)
};

+ 3
- 11
extras/Projucer/Source/Project/UI/Sidebar/jucer_TabComponents.h View File

@@ -165,35 +165,27 @@ public:
{
if (hasAddButton)
{
addButton.reset (new IconButton ("Add", &getIcons().plus));
addButton = std::make_unique<IconButton> ("Add", getIcons().plus);
addAndMakeVisible (addButton.get());
addButton->onClick = [this] { showAddMenu(); };
}
if (hasSettingsButton)
{
settingsButton.reset (new IconButton ("Settings", &getIcons().settings));
settingsButton = std::make_unique<IconButton> ("Settings", getIcons().settings);
addAndMakeVisible (settingsButton.get());
settingsButton->onClick = [this] { showSettings(); };
}
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 (treeToDisplay.get());
}
~ConcertinaTreeComponent() override
{
treeToDisplay.reset();
addButton.reset();
findPanel.reset();
settingsButton.reset();
}
void resized() override
{
auto bounds = getLocalBounds();


+ 135
- 89
extras/Projucer/Source/Project/UI/jucer_HeaderComponent.cpp View File

@@ -30,17 +30,19 @@
#include "../../LiveBuildEngine/jucer_CompileEngineClient.h"
//==============================================================================
HeaderComponent::HeaderComponent()
HeaderComponent::HeaderComponent (ProjectContentComponent* pcc)
: projectContentComponent (pcc)
{
addAndMakeVisible (configLabel);
addAndMakeVisible (exporterBox);
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);
addAndMakeVisible (projectNameLabel);
@@ -52,7 +54,7 @@ HeaderComponent::~HeaderComponent()
{
if (childProcess != nullptr)
{
childProcess->activityList.removeChangeListener(this);
childProcess->activityList.removeChangeListener (this);
childProcess->errorList.removeChangeListener (this);
}
}
@@ -63,33 +65,36 @@ void HeaderComponent::resized()
auto bounds = getLocalBounds();
configLabel.setFont ({ bounds.getHeight() / 3.0f });
//==============================================================================
{
auto headerBounds = bounds.removeFromLeft (tabsWidth);
const int buttonSize = 25;
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);
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)
@@ -98,48 +103,53 @@ void HeaderComponent::paint (Graphics& g)
if (isBuilding)
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;
stopTimer();
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();
auto preferredExporterIndex = -1;
@@ -149,7 +159,7 @@ void HeaderComponent::updateExporters() noexcept
{
exporterBox.addItem (exporter->getName(), i + 1);
if (selectedName == exporter->getName())
if (selectedExporter != nullptr && exporter->getName() == selectedExporter->getName())
exporterBox.setSelectedId (i + 1);
if (exporter->getName().contains (ProjectExporter::getCurrentPlatformExporterName()) && preferredExporterIndex == -1)
@@ -177,31 +187,56 @@ void HeaderComponent::updateExporters() noexcept
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;
}
//==============================================================================
void HeaderComponent::sidebarTabsWidthChanged (int newWidth) noexcept
void HeaderComponent::sidebarTabsWidthChanged (int newWidth)
{
tabsWidth = newWidth;
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)
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)
childProcess->launchApp();
@@ -253,22 +295,26 @@ void HeaderComponent::initialiseButtons() noexcept
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())
{
isBuilding = true;
runAppButton->setEnabled (false);
runAppButton->setTooltip ("Building...");
runAppButton.setEnabled (false);
runAppButton.setTooltip ("Building...");
startTimer (50);
}
@@ -306,23 +352,23 @@ void HeaderComponent::setRunAppButtonState (bool buildWasSuccessful)
{
if (childProcess->isAppRunning() || (! childProcess->isAppRunning() && childProcess->canLaunchApp()))
{
runAppButton->setTooltip ("Launch application");
runAppButton.setTooltip ("Launch application");
shouldEnableButton = true;
}
else
{
runAppButton->setTooltip ("Application can't be launched");
runAppButton.setTooltip ("Application can't be launched");
}
}
else
{
runAppButton->setTooltip ("Enable live-build engine to launch application");
runAppButton.setTooltip ("Enable live-build engine to launch application");
}
}
else
{
runAppButton->setTooltip ("Error building application");
runAppButton.setTooltip ("Error building application");
}
runAppButton->setEnabled (shouldEnableButton);
runAppButton.setEnabled (shouldEnableButton);
}

+ 22
- 14
extras/Projucer/Source/Project/UI/jucer_HeaderComponent.h View File

@@ -20,8 +20,11 @@
#include "../../Application/jucer_Headers.h"
#include "../../Utility/UI/jucer_IconButton.h"
#include "jucer_UserAvatarComponent.h"
class Project;
class ProjectContentComponent;
class ProjectExporter;
class CompileEngineChildProcess;
//==============================================================================
@@ -32,7 +35,7 @@ class HeaderComponent : public Component,
private Timer
{
public:
HeaderComponent();
HeaderComponent (ProjectContentComponent* projectContentComponent);
~HeaderComponent() override;
//==============================================================================
@@ -40,13 +43,14 @@ public:
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:
//==============================================================================
@@ -59,17 +63,17 @@ private:
void valueTreeChildRemoved (ValueTree& parentTree, ValueTree&, 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)
updateExporters();
}
//==============================================================================
void initialiseButtons() noexcept;
void initialiseButtons();
void updateName() noexcept;
void updateExporterButton() noexcept;
void updateName();
void updateExporterButton();
//==============================================================================
void buildPing();
@@ -80,17 +84,21 @@ private:
int tabsWidth = 200;
bool isBuilding = false;
ProjectContentComponent* projectContentComponent = nullptr;
Project* project = nullptr;
ValueTree exportersTree;
Value projectNameValue;
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;


+ 3
- 3
extras/Projucer/Source/Project/UI/jucer_ModulesInformationComponent.h View File

@@ -275,13 +275,13 @@ private:
m.addItem (PopupMenu::Item ("Copy the paths from the module '" + moduleToCopy + "' to all other modules")
.setAction ([this, moduleToCopy]
{
auto& moduleList = project.getEnabledModules();
auto& modulesList = project.getEnabledModules();
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)
exporter->getPathForModuleValue (modID) = exporter->getPathForModuleValue (moduleToCopy).get();


+ 137
- 119
extras/Projucer/Source/Project/UI/jucer_ProjectContentComponent.cpp View File

@@ -21,44 +21,50 @@
#include "../../LiveBuildEngine/jucer_DownloadCompileEngineThread.h"
#include "../../LiveBuildEngine/jucer_CompileEngineSettings.h"
#include "jucer_HeaderComponent.h"
#include "Sidebar/jucer_TabComponents.h"
#include "Sidebar/jucer_ProjectTab.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()
@@ -66,15 +72,12 @@ ProjectContentComponent::ProjectContentComponent()
setOpaque (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.setMaximumWidth (500);
@@ -84,6 +87,10 @@ ProjectContentComponent::ProjectContentComponent()
ProjucerApplication::getApp().openDocumentManager.addListener (this);
isLiveBuildEnabled = getGlobalProperties().getBoolValue (Ids::liveBuildEnabled);
getGlobalProperties().addChangeListener (this);
liveBuildEnablementChanged (isLiveBuildEnabled);
Desktop::getInstance().addFocusChangeListener (this);
startTimer (1600);
}
@@ -93,15 +100,11 @@ ProjectContentComponent::~ProjectContentComponent()
Desktop::getInstance().removeFocusChangeListener (this);
killChildProcess();
getGlobalProperties().removeChangeListener (this);
ProjucerApplication::getApp().openDocumentManager.removeListener (this);
logo.reset();
header.reset();
setProject (nullptr);
contentView.reset();
fileNameLabel.reset();
removeChildComponent (&bubbleMessage);
jassert (getNumChildComponents() <= 1);
}
void ProjectContentComponent::paint (Graphics& g)
@@ -115,11 +118,10 @@ void ProjectContentComponent::resized()
r.removeFromRight (10);
r.removeFromLeft (15);
r.removeFromBottom (40);
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);
@@ -132,19 +134,15 @@ void ProjectContentComponent::resized()
if (resizerBar != nullptr)
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 (fileNameLabel != nullptr && fileNameLabel->isVisible())
fileNameLabel->setBounds (r.removeFromTop (15));
fileNameLabel.setBounds (r.removeFromTop (15));
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()
@@ -165,7 +163,7 @@ void ProjectContentComponent::setProject (Project* newProject)
{
if (project != newProject)
{
lastCrashMessage = String();
lastCrashMessage = {};
killChildProcess();
if (project != nullptr)
@@ -179,6 +177,8 @@ void ProjectContentComponent::setProject (Project* newProject)
if (project != nullptr)
rebuildProjectTabs();
projectMessagesComponent.setProject (newProject);
}
}
@@ -201,20 +201,21 @@ void ProjectContentComponent::createProjectTabs()
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.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();
for (int i = 2; i >= 0; --i)
@@ -272,16 +278,13 @@ void ProjectContentComponent::rebuildProjectTabs()
updateMissingFileStatuses();
if (auto* h = dynamic_cast<HeaderComponent*> (header.get()))
{
h->setVisible (true);
h->setCurrentProject (project);
}
headerComponent.setVisible (true);
headerComponent.setCurrentProject (project);
}
else
{
sidebarTabs.setVisible (false);
header->setVisible (false);
headerComponent.setVisible (false);
}
resized();
@@ -320,9 +323,19 @@ bool ProjectContentComponent::documentAboutToClose (OpenDocumentManager::Documen
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()
@@ -344,8 +357,7 @@ bool ProjectContentComponent::showEditorForFile (const File& f, bool grabFocus)
if (getCurrentFile() == f
|| showDocument (ProjucerApplication::getApp().openDocumentManager.openFile (project, f), grabFocus))
{
fileNameLabel->setText (f.getFileName(), dontSendNotification);
fileNameLabel.setText (f.getFileName(), dontSendNotification);
return true;
}
@@ -394,8 +406,7 @@ void ProjectContentComponent::hideEditor()
currentDocument = nullptr;
contentView.reset();
if (fileNameLabel != nullptr)
fileNameLabel->setVisible (false);
fileNameLabel.setVisible (false);
ProjucerApplication::getCommandManager().commandStatusChanged();
resized();
@@ -425,7 +436,7 @@ bool ProjectContentComponent::setEditorComponent (Component* editor,
contentView.reset (viewport);
currentDocument = nullptr;
fileNameLabel->setVisible (false);
fileNameLabel.setVisible (false);
addAndMakeVisible (viewport);
}
@@ -433,8 +444,8 @@ bool ProjectContentComponent::setEditorComponent (Component* editor,
{
contentView.reset (editor);
currentDocument = doc;
fileNameLabel->setText (doc->getFile().getFileName(), dontSendNotification);
fileNameLabel->setVisible (true);
fileNameLabel.setText (doc->getFile().getFileName(), dontSendNotification);
fileNameLabel.setVisible (true);
addAndMakeVisible (editor);
}
@@ -460,7 +471,8 @@ Component* ProjectContentComponent::getEditorComponentContent() const
void ProjectContentComponent::closeDocument()
{
if (currentDocument != nullptr)
ProjucerApplication::getApp().openDocumentManager.closeDocument (currentDocument, true);
ProjucerApplication::getApp().openDocumentManager
.closeDocument (currentDocument, OpenDocumentManager::SaveIfNeeded::yes);
else if (contentView != nullptr)
if (! goToPreviousFile())
hideEditor();
@@ -534,15 +546,10 @@ bool ProjectContentComponent::goToCounterpart()
return false;
}
bool ProjectContentComponent::saveProject (bool shouldWait, bool openInIDE)
bool ProjectContentComponent::saveProject()
{
if (project != nullptr)
{
const ScopedValueSetter<bool> valueSetter (project->shouldWaitAfterSaving, shouldWait, false);
project->setOpenInIDEAfterSaving (openInIDE);
return (project->save (true, true) == FileBasedDocument::savedOk);
}
return false;
}
@@ -550,7 +557,7 @@ bool ProjectContentComponent::saveProject (bool shouldWait, bool openInIDE)
void ProjectContentComponent::closeProject()
{
if (auto* mw = findParentComponentOfClass<MainWindow>())
mw->closeCurrentProject (true);
mw->closeCurrentProject (OpenDocumentManager::SaveIfNeeded::yes);
}
void ProjectContentComponent::showProjectSettings()
@@ -560,8 +567,8 @@ void ProjectContentComponent::showProjectSettings()
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)
@@ -573,7 +580,7 @@ void ProjectContentComponent::showExporterSettings (const String& exporterName)
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)
{
@@ -637,29 +644,8 @@ StringArray ProjectContentComponent::getExportersWhichCanLaunch() const
void ProjectContentComponent::openInSelectedIDE (bool saveFirst)
{
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)
@@ -801,7 +787,8 @@ void ProjectContentComponent::getAllCommands (Array <CommandID>& commands)
CommandIDs::reinstantiateComp,
CommandIDs::showWarnings,
CommandIDs::nextError,
CommandIDs::prevError });
CommandIDs::prevError,
CommandIDs::addNewGUIFile });
}
void ProjectContentComponent::getCommandInfo (const CommandID commandID, ApplicationCommandInfo& result)
@@ -822,7 +809,7 @@ void ProjectContentComponent::getCommandInfo (const CommandID commandID, Applica
result.setInfo ("Save Project",
"Saves the current project",
CommandCategories::general, 0);
result.setActive (project != nullptr && ! project->isCurrentlySaving());
result.setActive (project != nullptr && ! project->isSaveAndExportDisabled() && ! project->isCurrentlySaving());
result.defaultKeypresses.add ({ 'p', ModifierKeys::commandModifier, 0 });
break;
@@ -901,7 +888,7 @@ void ProjectContentComponent::getCommandInfo (const CommandID commandID, Applica
result.setInfo ("Show Build Tab",
"Shows the tab containing the build panel",
CommandCategories::general, 0);
result.setActive (project != nullptr);
result.setActive (project != nullptr && isLiveBuildEnabled);
result.defaultKeypresses.add ({ 'b', cmdCtrl, 0 });
break;
@@ -941,14 +928,14 @@ void ProjectContentComponent::getCommandInfo (const CommandID commandID, Applica
result.setInfo ("Open in IDE...",
"Launches the project in an external IDE",
CommandCategories::general, 0);
result.setActive (ProjectExporter::canProjectBeLaunched (project));
result.setActive (ProjectExporter::canProjectBeLaunched (project) && ! project->isSaveAndExportDisabled());
break;
case CommandIDs::saveAndOpenInIDE:
result.setInfo ("Save Project and Open in IDE...",
"Saves the project and launches it in an external IDE",
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 });
break;
@@ -1055,6 +1042,13 @@ void ProjectContentComponent::getCommandInfo (const CommandID commandID, Applica
result.setActive (childProcess != nullptr && ! childProcess->errorList.isEmpty());
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:
break;
}
@@ -1131,6 +1125,8 @@ bool ProjectContentComponent::perform (const InvocationInfo& info)
case CommandIDs::nextError: showNextError(); break;
case CommandIDs::prevError: showPreviousError(); break;
case CommandIDs::addNewGUIFile: addNewGUIFile(); break;
default:
return false;
}
@@ -1175,7 +1171,7 @@ void ProjectContentComponent::setBuildEnabled (bool isEnabled, bool displayError
void ProjectContentComponent::cleanAll()
{
lastCrashMessage = String();
lastCrashMessage = {};
if (childProcess != nullptr)
childProcess->cleanAll();
@@ -1204,9 +1200,11 @@ bool ProjectContentComponent::isBuildEnabled() const
void ProjectContentComponent::refreshTabsIfBuildStatusChanged()
{
if (project != nullptr
&& (sidebarTabs.getNumTabs() < 2
|| isBuildEnabled() != isBuildTabEnabled()))
&& isLiveBuildEnabled
&& (sidebarTabs.getNumTabs() < 2 || isBuildEnabled() != isBuildTabEnabled()))
{
rebuildProjectTabs();
}
}
bool ProjectContentComponent::areWarningsEnabled() const
@@ -1261,6 +1259,15 @@ void ProjectContentComponent::reinstantiateLivePreviewWindows()
childProcess->reinstantiatePreviews();
}
void ProjectContentComponent::addNewGUIFile()
{
if (project != nullptr)
{
std::unique_ptr<NewFileWizard::Type> wizard (createGUIComponentWizard());
wizard->createNewFile (*project, project->getMainGroup());
}
}
void ProjectContentComponent::launchApp()
{
if (childProcess != nullptr)
@@ -1301,6 +1308,17 @@ void ProjectContentComponent::timerCallback()
refreshTabsIfBuildStatusChanged();
}
void ProjectContentComponent::liveBuildEnablementChanged (bool isEnabled)
{
isLiveBuildEnabled = isEnabled;
if (! isLiveBuildEnabled)
killChildProcess();
rebuildProjectTabs();
headerComponent.liveBuildEnablementChanged (isLiveBuildEnabled);
}
bool ProjectContentComponent::isContinuousRebuildEnabled()
{
return project != nullptr && project->getCompileEngineSettings().isContinuousRebuildEnabled();


+ 45
- 35
extras/Projucer/Source/Project/UI/jucer_ProjectContentComponent.h View File

@@ -19,11 +19,12 @@
#pragma once
#include "../../CodeEditor/jucer_OpenDocumentManager.h"
#include "jucer_HeaderComponent.h"
#include "jucer_ProjectMessagesComponent.h"
class CompileEngineChildProcess;
class ProjectTab;
class LiveBuildTab;
class HeaderComponent;
//==============================================================================
class ProjectContentComponent : public Component,
@@ -39,7 +40,7 @@ public:
~ProjectContentComponent() override;
Project* getProject() const noexcept { return project; }
virtual void setProject (Project*);
void setProject (Project*);
void saveTreeViewState();
void saveOpenDocumentList();
@@ -67,14 +68,14 @@ public:
bool canGoToCounterpart() const;
bool goToCounterpart();
bool saveProject (bool shouldWait = false, bool openInIDE = false);
bool saveProject();
void closeProject();
void openInSelectedIDE (bool saveFirst);
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 showModulesPanel() { showProjectPanel (1); }
@@ -99,6 +100,7 @@ public:
void showNextError();
void showPreviousError();
void reinstantiateLivePreviewWindows();
void addNewGUIFile();
void showBubbleMessage (Rectangle<int>, const String&);
@@ -129,28 +131,31 @@ public:
void childBoundsChanged (Component*) 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;
@@ -160,6 +165,8 @@ private:
void globalFocusChanged (Component*) override;
void timerCallback() override;
void liveBuildEnablementChanged (bool isEnabled);
bool isContinuousRebuildEnabled();
void setContinuousRebuildEnabled (bool b);
@@ -178,23 +185,26 @@ private:
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)
};

+ 544
- 0
extras/Projucer/Source/Project/UI/jucer_ProjectMessagesComponent.h View File

@@ -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)
};

+ 123
- 0
extras/Projucer/Source/Project/UI/jucer_UserAvatarComponent.h View File

@@ -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;
};

+ 417
- 99
extras/Projucer/Source/Project/jucer_Project.cpp View File

@@ -22,17 +22,63 @@
#include "../Application/jucer_Application.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)
: FileBasedDocument (projectFileExtension,
@@ -48,16 +94,37 @@ Project::Project (const File& f)
initialiseMainGroup();
initialiseAudioPluginValues();
exporterPathsModuleList.reset (new AvailableModuleList());
setChangedFlag (false);
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()
{
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";
@@ -93,6 +160,8 @@ void Project::updateCompanyNameDependencies()
bundleIdentifierValue.setDefault (getDefaultBundleIdentifierString());
pluginAAXIdentifierValue.setDefault (getDefaultAAXIdentifierString());
pluginManufacturerValue.setDefault (getDefaultPluginManufacturerString());
updateLicenseWarning();
}
void Project::updateProjectSettings()
@@ -105,7 +174,7 @@ bool Project::setCppVersionFromOldExporterSettings()
{
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
{
@@ -130,7 +199,7 @@ bool Project::setCppVersionFromOldExporterSettings()
{
if (cppLanguageStandard.toString().containsIgnoreCase ("latest"))
{
cppStandardValue = "latest";
cppStandardValue = Project::getCppStandardVars().getLast();
return true;
}
@@ -153,7 +222,7 @@ bool Project::setCppVersionFromOldExporterSettings()
void Project::updateDeprecatedProjectSettings()
{
for (Project::ExporterIterator exporter (*this); exporter.next();)
for (ExporterIterator exporter (*this); exporter.next();)
exporter->updateDeprecatedSettings();
}
@@ -161,7 +230,7 @@ void Project::updateDeprecatedProjectSettingsInteractively()
{
jassert (! ProjucerApplication::getApp().isRunningCommandLine);
for (Project::ExporterIterator exporter (*this); exporter.next();)
for (ExporterIterator exporter (*this); exporter.next();)
exporter->updateDeprecatedSettingsInteractively();
}
@@ -226,6 +295,14 @@ void Project::initialiseProjectValues()
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(),
Array<var> (Ids::buildVST3.toString(), Ids::buildAU.toString(), Ids::buildStandalone.toString()), ",");
pluginCharacteristicsValue.referTo (projectRoot, Ids::pluginCharacteristicsValue, getUndoManager(), Array<var> (), ",");
@@ -259,7 +336,7 @@ void Project::updateOldStyleConfigList()
{
projectRoot.removeChild (deprecatedConfigsList, nullptr);
for (Project::ExporterIterator exporter (*this); exporter.next();)
for (ExporterIterator exporter (*this); exporter.next();)
{
if (exporter->getNumConfigurations() == 0)
{
@@ -287,7 +364,7 @@ void Project::moveOldPropertyFromProjectToAllExporters (Identifier 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);
projectRoot.removeProperty (name, nullptr);
@@ -325,7 +402,7 @@ void Project::removeDefunctExporters()
void Project::updateOldModulePaths()
{
for (Project::ExporterIterator exporter (*this); exporter.next();)
for (ExporterIterator exporter (*this); exporter.next();)
exporter->updateOldModulePaths();
}
@@ -499,32 +576,6 @@ static constexpr int getBuiltJuceVersion()
+ 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;
@@ -560,7 +611,7 @@ Result Project::loadDocument (const File& file)
registerRecentFile (file);
enabledModuleList.reset();
enabledModulesList.reset();
projectRoot = newTree;
projectRoot.addListener (this);
@@ -582,60 +633,303 @@ Result Project::loadDocument (const File& file)
moveOldPropertyFromProjectToAllExporters (Ids::smallIcon);
getEnabledModules().sortAlphabetically();
compileEngineSettings.reset (new CompileEngineSettings (projectRoot));
exporterPathsModulesList.reset (new AvailableModulesList());
rescanExporterPathModules (! ProjucerApplication::getApp().isRunningCommandLine);
exporterPathsModulesList->addListener (this);
setCppVersionFromOldExporterSettings();
updateDeprecatedProjectSettings();
setChangedFlag (false);
if (! ProjucerApplication::getApp().isRunningCommandLine)
warnAboutOldProjucerVersion();
compileEngineSettings.reset (new CompileEngineSettings (projectRoot));
exporterPathsModuleList.reset (new AvailableModuleList());
rescanExporterPathModules (! ProjucerApplication::getApp().isRunningCommandLine);
updateLicenseWarning();
return Result::ok();
}
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)
return Result::ok();
if (isTemporaryProject())
{
askUserWhereToSaveProject();
saveAndMoveTemporaryProject (false);
return Result::ok();
}
updateProjectSettings();
if (! isCommandLineApp)
if (! ProjucerApplication::getApp().isRunningCommandLine)
{
ProjucerApplication::getApp().openDocumentManager.saveAll();
if (! isTemporaryProject())
registerRecentFile (file);
registerRecentFile (getFile());
}
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();
}
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
{
@@ -645,17 +939,16 @@ void Project::setTemporaryDirectory (const File& dir) noexcept
forgetRecentFile (getFile());
}
void Project::askUserWhereToSaveProject()
void Project::saveAndMoveTemporaryProject (bool openInIDE)
{
FileChooser fc ("Save Project");
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 oldJucerFileName = getFile().getFileName();
@@ -670,10 +963,12 @@ void Project::moveTemporaryDirectory (const File& newParentDirectory)
{
Component::SafePointer<MainWindow> safeWindow (window);
MessageManager::callAsync ([safeWindow, newDirectory, oldJucerFileName]
MessageManager::callAsync ([safeWindow, newDirectory, oldJucerFileName, openInIDE]() mutable
{
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)
writeLegacyPluginCharacteristicsSettings();
}
else if (property == Ids::displaySplashScreen)
{
updateLicenseWarning();
}
else if (property == Ids::cppLanguageStandard)
{
updateModuleWarnings();
}
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()
@@ -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 "
"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.");
{
StringArray projectTypeNames;
Array<var> projectTypeCodes;
@@ -954,8 +1271,8 @@ void Project::createPropertyEditors (PropertyListBuilder& props)
"The namespace containing the binary assets.");
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.");
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)
@@ -1979,37 +2296,38 @@ static Array<File> getExporterModulePathsToScan (Project& project)
return files;
}
AvailableModuleList& Project::getExporterPathsModuleList()
AvailableModulesList& Project::getExporterPathsModulesList()
{
return *exporterPathsModuleList;
jassert (exporterPathsModulesList != nullptr);
return *exporterPathsModulesList;
}
void Project::rescanExporterPathModules (bool async)
{
if (async)
exporterPathsModuleList->scanPathsAsync (getExporterModulePathsToScan (*this));
exporterPathsModulesList->scanPathsAsync (getExporterModulePathsToScan (*this));
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))
{
const auto& mod = exporterPathsModuleList->getModuleWithID (id);
const auto& mod = exporterPathsModulesList->getModuleWithID (id);
if (mod.second != File())
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)
if (m.first == id)
return m;
return exporterPathsModuleList->getModuleWithID (id);
return exporterPathsModulesList->getModuleWithID (id);
}
//==============================================================================


+ 148
- 22
extras/Projucer/Source/Project/jucer_Project.h View File

@@ -18,16 +18,96 @@
#pragma once
#include "../Application/UserAccount/jucer_LicenseController.h"
#include "Modules/jucer_AvailableModulesList.h"
class ProjectExporter;
class LibraryModule;
class EnabledModuleList;
class AvailableModuleList;
class ProjectContentComponent;
class EnabledModulesList;
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,
public ValueTree::Listener
public ValueTree::Listener,
private LicenseController::LicenseStateListener,
private ChangeListener,
private AvailableModulesList::Listener
{
public:
//==============================================================================
@@ -35,12 +115,14 @@ public:
~Project() override;
//==============================================================================
// FileBasedDocument stuff..
String getDocumentTitle() override;
Result loadDocument (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;
void setLastDocumentOpened (const File& file) override;
@@ -116,6 +198,9 @@ public:
bool shouldDisplaySplashScreen() const { return displaySplashScreenValue.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(); }
StringArray getCompilerFlagSchemes() const;
@@ -363,9 +448,9 @@ public:
bool isConfigFlagEnabled (const String& name, bool defaultIsEnabled = false) const;
//==============================================================================
EnabledModuleList& getEnabledModules();
EnabledModulesList& getEnabledModules();
AvailableModuleList& getExporterPathsModuleList();
AvailableModulesList& getExporterPathsModulesList();
void rescanExporterPathModules (bool async = false);
std::pair<String, File> getModuleWithID (const String&);
@@ -397,22 +482,45 @@ public:
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(); }
File getTemporaryDirectory() const noexcept { return tempDirectory; }
void setTemporaryDirectory (const File&) noexcept;
void setOpenInIDEAfterSaving (bool open) noexcept { openInIDEAfterSaving = open; }
bool shouldOpenInIDEAfterSaving() const noexcept { return openInIDEAfterSaving; }
//==============================================================================
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:
//==============================================================================
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 };
ValueWithDefault projectNameValue, projectUIDValue, projectLineFeedValue, projectTypeValue, versionValue, bundleIdentifierValue, companyNameValue,
@@ -427,8 +535,8 @@ private:
//==============================================================================
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();
@@ -449,10 +557,8 @@ private:
//==============================================================================
File tempDirectory = {};
bool openInIDEAfterSaving = false;
void askUserWhereToSaveProject();
void moveTemporaryDirectory (const File&);
void saveAndMoveTemporaryProject (bool openInIDE);
bool saveProjectRootToFile();
//==============================================================================
@@ -481,7 +587,27 @@ private:
void moveOldPropertyFromProjectToAllExporters (Identifier name);
void removeDefunctExporters();
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)
};

+ 736
- 16
extras/Projucer/Source/ProjectSaving/jucer_ProjectSaver.cpp View File

@@ -16,12 +16,655 @@
==============================================================================
*/
#include "../Application/jucer_Headers.h"
#include "jucer_ProjectSaver.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
{
const auto flags = project.getAudioPluginFlags();
@@ -47,7 +690,27 @@ String ProjectSaver::getAudioPluginDefines() const
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;
@@ -55,24 +718,27 @@ void ProjectSaver::writeProjects (const OwnedArray<LibraryModule>& modules, cons
auto originalGeneratedGroup = generatedFilesGroup.state.createCopy();
CLionProjectExporter* clionExporter = nullptr;
OwnedArray<ProjectExporter> exporters;
std::vector<std::unique_ptr<ProjectExporter>> exporters;
try
{
for (Project::ExporterIterator exp (project); exp.next();)
{
if (specifiedExporterToSave.isNotEmpty() && exp->getName() != specifiedExporterToSave)
if (specifiedExporterToSave != nullptr && exp->getName() != specifiedExporterToSave->getName())
continue;
auto exporter = exporters.add (std::move (exp.exporter));
exporters.push_back (std::move (exp.exporter));
}
for (auto& exporter : exporters)
{
exporter->initialiseDependencyPathValues();
if (exporter->getTargetFolder().createDirectory())
{
if (exporter->isCLion())
{
clionExporter = dynamic_cast<CLionProjectExporter*> (exporter);
clionExporter = dynamic_cast<CLionProjectExporter*> (exporter.get());
}
else
{
@@ -84,17 +750,17 @@ void ProjectSaver::writeProjects (const OwnedArray<LibraryModule>& modules, cons
generatedFilesGroup.state = originalGeneratedGroup.createCopy();
exporter->addSettingsForProjectType (project.getProjectType());
for (auto& module: modules)
for (auto* module : modules)
module->addSettingsForModuleToExporter (*exporter, *this);
generatedFilesGroup.sortAlphabetically (true, true);
exporter->getAllGroups().add (generatedFilesGroup);
}
if (isCommandLineApp)
saveExporter (exporter, modules);
if (ProjucerApplication::getApp().isRunningCommandLine)
saveExporter (*exporter, modules);
else
threadPool.addJob (new ExporterJob (*this, exporter, modules), true);
threadPool.addJob (new ExporterJob (*this, *exporter, modules), true);
}
else
{
@@ -107,15 +773,69 @@ void ProjectSaver::writeProjects (const OwnedArray<LibraryModule>& modules, cons
addError (saveError.message);
}
if (! isCommandLineApp)
while (threadPool.getNumJobs() > 0)
Thread::sleep (10);
while (threadPool.getNumJobs() > 0)
Thread::sleep (10);
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;
}
}
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);
}
}

+ 82
- 764
extras/Projucer/Source/ProjectSaving/jucer_ProjectSaver.h View File

@@ -18,809 +18,127 @@
#pragma once
#include "../Application/jucer_Headers.h"
#include "jucer_ResourceFile.h"
#include "../Project/jucer_Module.h"
#include "../Project/Modules/jucer_Modules.h"
#include "jucer_ProjectExporter.h"
//==============================================================================
class ProjectSaver
{
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:
SaveThread (ProjectSaver& ps, bool wait, const String& exp)
SaveThreadWithProgressWindow (ProjectSaver& ps, ProjectExporter* exporterToSave)
: ThreadWithProgressWindow ("Saving...", true, false),
saver (ps),
shouldWaitAfterSaving (wait),
specifiedExporterToSave (exp)
specifiedExporterToSave (exporterToSave)
{}
void run() override
{
setProgress (-1);
result = saver.save (false, shouldWaitAfterSaving, specifiedExporterToSave);
result = saver.saveProject (specifiedExporterToSave);
}
ProjectSaver& saver;
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;
SortedSet<File> filesCreated;
private:
const File projectFile, generatedCodeFolder;
File generatedCodeFolder;
Project::Item generatedFilesGroup;
StringArray errors;
SortedSet<File> filesCreated;
String projectLineFeed;
CriticalSection errorLock;
StringArray errors;
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)
};

+ 2
- 11
extras/Projucer/Source/Settings/jucer_StoredSettings.cpp View File

@@ -350,18 +350,9 @@ void StoredSettings::checkJUCEPaths()
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)


+ 1
- 3
extras/Projucer/Source/Settings/jucer_StoredSettings.h View File

@@ -55,9 +55,7 @@ public:
//==============================================================================
ValueWithDefault getStoredPath (const Identifier& key, DependencyPathOS os);
bool shouldAskUserToSetJUCEPath() noexcept;
void setDontAskAboutJUCEPathAgain() noexcept;
bool isJUCEPathIncorrect();
//==============================================================================
AppearanceSettings appearance;


+ 4
- 1
extras/Projucer/Source/Utility/Helpers/jucer_PresetIDs.h View File

@@ -23,7 +23,7 @@
// Handy list of static Identifiers..
namespace Ids
{
#define DECLARE_ID(name) const Identifier name (#name)
#define DECLARE_ID(name) const Identifier name (#name)
DECLARE_ID (name);
DECLARE_ID (file);
@@ -359,8 +359,11 @@ namespace Ids
DECLARE_ID (compilerFlagSchemes);
DECLARE_ID (compilerFlagScheme);
DECLARE_ID (dontQueryForUpdate);
DECLARE_ID (dontAskAboutJUCEPath);
DECLARE_ID (postExportShellCommandPosix);
DECLARE_ID (postExportShellCommandWin);
DECLARE_ID (liveBuildEnabled);
DECLARE_ID (guiEditorEnabled);
const Identifier ID ("id");
const Identifier ID_uppercase ("ID");


+ 3
- 2
extras/Projucer/Source/Utility/PIPs/jucer_PIPGenerator.cpp View File

@@ -19,7 +19,6 @@
#include "../../Application/jucer_Headers.h"
#include "../../ProjectSaving/jucer_ProjectExporter.h"
#include "jucer_PIPGenerator.h"
#include "../../Project/jucer_Module.h"
//==============================================================================
static void ensureSingleNewLineAfterIncludes (StringArray& lines)
@@ -105,7 +104,7 @@ PIPGenerator::PIPGenerator (const File& pip, const File& output, const File& juc
if (userModulesPath != File())
{
availableUserModules.reset (new AvailableModuleList());
availableUserModules.reset (new AvailableModulesList());
availableUserModules->scanPaths ({ userModulesPath });
}
}
@@ -357,6 +356,8 @@ Result PIPGenerator::setProjectSettings (ValueTree& jucerTree)
: "\"File->Global Paths...\"")
+ " menu item.");
}
jucerTree.setProperty (Ids::displaySplashScreen, true, nullptr);
}
setPropertyIfNotEmpty (Ids::defines, defines);


+ 3
- 6
extras/Projucer/Source/Utility/PIPs/jucer_PIPGenerator.h View File

@@ -19,6 +19,7 @@
#pragma once
#include "../Helpers/jucer_MiscUtilities.h"
#include "../../Project/Modules/jucer_AvailableModulesList.h"
//==============================================================================
class PIPGenerator
@@ -70,13 +71,9 @@ private:
//==============================================================================
File pipFile, outputDirectory, juceModulesPath, userModulesPath;
std::unique_ptr<AvailableModuleList> availableUserModules;
std::unique_ptr<AvailableModulesList> availableUserModules;
var metadata;
bool isTemp = false;
bool useLocalCopy = false;
bool isTemp = false, useLocalCopy = false;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (PIPGenerator)
};

+ 78
- 32
extras/Projucer/Source/Utility/UI/jucer_IconButton.h View File

@@ -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
{
auto alpha = 1.0f;
float alpha = 1.0f;
if (! isEnabled())
{
isMouseOverButton = false;
@@ -41,43 +74,56 @@ struct IconButton : public Button
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)
bounds.reduce (2, 2);
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();
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;
Colour backgroundColour { findColour (defaultButtonBackgroundColourId) };
bool usingNonDefaultBackgroundColour = false;
int iconInset = 2;
bool isIDEButton = false;
bool isUserButton = false;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (IconButton)
};

+ 0
- 23
extras/Projucer/Source/Utility/UI/jucer_Icons.cpp View File

@@ -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 };
}
/*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()
{
#define JUCE_LOAD_PATH_DATA(name) \


+ 10
- 6
extras/Projucer/Source/Utility/UI/jucer_Icons.h View File

@@ -22,18 +22,22 @@
//==============================================================================
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
{
if (path != nullptr)
if (! path.isEmpty())
{
g.setColour (colour);
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)
{
@@ -54,7 +58,7 @@ struct Icon
return Icon (path, newColour);
}
const Path* path;
Path path;
Colour colour;
};


+ 27
- 10
extras/Projucer/Source/Utility/UI/jucer_ProjucerLookAndFeel.cpp View File

@@ -19,6 +19,7 @@
#include "../../Application/jucer_Headers.h"
#include "jucer_ProjucerLookAndFeel.h"
#include "../../Application/jucer_Application.h"
#include "../../Project/UI/jucer_ProjectContentComponent.h"
//==============================================================================
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;
#ifndef BUILDING_JUCE_COMPILEENGINE
auto textColour = findColour (defaultTextColourId).withMultipliedAlpha (alpha);
auto iconColour = findColour (button.isFrontTab() ? activeTabIconColourId
: 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
#endif
{
auto textColour = findColour (defaultTextColourId).withMultipliedAlpha (alpha);
TextLayout textLayout;
LookAndFeel_V3::createTabTextLayout (button, (float) area.getWidth(), (float) area.getHeight(), textColour, textLayout);


+ 0
- 3
extras/Projucer/Source/Wizards/jucer_NewFileWizard.cpp View File

@@ -19,8 +19,6 @@
#include "../Application/jucer_Headers.h"
#include "jucer_NewFileWizard.h"
NewFileWizard::Type* createGUIComponentWizard();
//==============================================================================
namespace
{
@@ -232,7 +230,6 @@ NewFileWizard::NewFileWizard()
registerWizard (new NewCppAndHeaderFileWizard());
registerWizard (new NewComponentFileWizard());
registerWizard (new NewSingleFileComponentFileWizard());
registerWizard (createGUIComponentWizard());
}
NewFileWizard::~NewFileWizard()


+ 5
- 3
extras/Projucer/Source/Wizards/jucer_NewProjectWizard.h View File

@@ -122,11 +122,10 @@ struct NewProjectWizard
projectFile = targetFolder.getChildFile (File::createLegalFileName (appTitle))
.withFileExtension (Project::projectFileExtension);
std::unique_ptr<Project> project (new Project (projectFile));
auto project = std::make_unique<Project> (projectFile);
if (failedFiles.size() == 0)
{
project->setFile (projectFile);
project->setTitle (appTitle);
if (! initialiseProject (*project))
@@ -136,6 +135,9 @@ struct NewProjectWizard
project->getProjectValue (Ids::useAppConfig) = false;
project->getProjectValue (Ids::addUsingNamespaceToJuceHeader) = false;
if (! ProjucerApplication::getApp().getLicenseController().getCurrentState().isPaidOrGPL())
project->getProjectValue (Ids::displaySplashScreen) = true;
addExporters (*project, wc);
addDefaultModules (*project, useGlobalPath);
@@ -174,7 +176,7 @@ struct NewProjectWizard
{
auto defaultModules = getDefaultModules();
AvailableModuleList list;
AvailableModulesList list;
list.scanPaths ({ modulesFolder });
for (auto& mod : list.getAllModules())


Loading…
Cancel
Save