- clean working copy for leaving SVN
This commit is contained in:
@@ -0,0 +1,671 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
|
||||
Permission is granted to use this software under the terms of either:
|
||||
a) the GPL v2 (or any later version)
|
||||
b) the Affero GPL v3
|
||||
|
||||
Details of these licenses can be found at: www.gnu.org/licenses
|
||||
|
||||
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.juce.com for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#ifndef __JUCER_AUDIOPLUGINMODULE_JUCEHEADER__
|
||||
#define __JUCER_AUDIOPLUGINMODULE_JUCEHEADER__
|
||||
|
||||
|
||||
//==============================================================================
|
||||
namespace
|
||||
{
|
||||
Value shouldBuildVST (Project& project) { return project.getProjectValue ("buildVST"); }
|
||||
Value shouldBuildVST3 (Project& project) { return project.getProjectValue ("buildVST3"); }
|
||||
Value shouldBuildAU (Project& project) { return project.getProjectValue ("buildAU"); }
|
||||
Value shouldBuildRTAS (Project& project) { return project.getProjectValue ("buildRTAS"); }
|
||||
Value shouldBuildAAX (Project& project) { return project.getProjectValue ("buildAAX"); }
|
||||
|
||||
Value getPluginName (Project& project) { return project.getProjectValue ("pluginName"); }
|
||||
Value getPluginDesc (Project& project) { return project.getProjectValue ("pluginDesc"); }
|
||||
Value getPluginManufacturer (Project& project) { return project.getProjectValue ("pluginManufacturer"); }
|
||||
Value getPluginManufacturerCode (Project& project) { return project.getProjectValue ("pluginManufacturerCode"); }
|
||||
Value getPluginCode (Project& project) { return project.getProjectValue ("pluginCode"); }
|
||||
Value getPluginChannelConfigs (Project& project) { return project.getProjectValue ("pluginChannelConfigs"); }
|
||||
Value getPluginIsSynth (Project& project) { return project.getProjectValue ("pluginIsSynth"); }
|
||||
Value getPluginWantsMidiInput (Project& project) { return project.getProjectValue ("pluginWantsMidiIn"); }
|
||||
Value getPluginProducesMidiOut (Project& project) { return project.getProjectValue ("pluginProducesMidiOut"); }
|
||||
Value getPluginSilenceInProducesSilenceOut (Project& project) { return project.getProjectValue ("pluginSilenceInIsSilenceOut"); }
|
||||
Value getPluginEditorNeedsKeyFocus (Project& project) { return project.getProjectValue ("pluginEditorRequiresKeys"); }
|
||||
Value getPluginVSTCategory (Project& project) { return project.getProjectValue ("pluginVSTCategory"); }
|
||||
Value getPluginAUExportPrefix (Project& project) { return project.getProjectValue ("pluginAUExportPrefix"); }
|
||||
Value getPluginAUMainType (Project& project) { return project.getProjectValue ("pluginAUMainType"); }
|
||||
Value getPluginRTASCategory (Project& project) { return project.getProjectValue ("pluginRTASCategory"); }
|
||||
Value getPluginRTASBypassDisabled (Project& project) { return project.getProjectValue ("pluginRTASDisableBypass"); }
|
||||
Value getPluginRTASMultiMonoDisabled (Project& project) { return project.getProjectValue ("pluginRTASDisableMultiMono"); }
|
||||
Value getPluginAAXCategory (Project& project) { return project.getProjectValue ("pluginAAXCategory"); }
|
||||
Value getPluginAAXBypassDisabled (Project& project) { return project.getProjectValue ("pluginAAXDisableBypass"); }
|
||||
Value getPluginAAXMultiMonoDisabled (Project& project) { return project.getProjectValue ("pluginAAXDisableMultiMono"); }
|
||||
|
||||
String getPluginRTASCategoryCode (Project& project)
|
||||
{
|
||||
if (static_cast <bool> (getPluginIsSynth (project).getValue()))
|
||||
return "ePlugInCategory_SWGenerators";
|
||||
|
||||
String s (getPluginRTASCategory (project).toString());
|
||||
if (s.isEmpty())
|
||||
s = "ePlugInCategory_None";
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
String getAUMainTypeString (Project& project)
|
||||
{
|
||||
String s (getPluginAUMainType (project).toString());
|
||||
|
||||
if (s.isEmpty())
|
||||
{
|
||||
if (getPluginIsSynth (project).getValue()) s = "kAudioUnitType_MusicDevice";
|
||||
else if (getPluginWantsMidiInput (project).getValue()) s = "kAudioUnitType_MusicEffect";
|
||||
else s = "kAudioUnitType_Effect";
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
String getAUMainTypeCode (Project& project)
|
||||
{
|
||||
String s (getPluginAUMainType (project).toString());
|
||||
|
||||
if (s.isEmpty())
|
||||
{
|
||||
if (getPluginIsSynth (project).getValue()) s = "aumu";
|
||||
else if (getPluginWantsMidiInput (project).getValue()) s = "aumf";
|
||||
else s = "aufx";
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
String getPluginVSTCategoryString (Project& project)
|
||||
{
|
||||
String s (getPluginVSTCategory (project).toString().trim());
|
||||
|
||||
if (s.isEmpty())
|
||||
s = static_cast<bool> (getPluginIsSynth (project).getValue()) ? "kPlugCategSynth"
|
||||
: "kPlugCategEffect";
|
||||
return s;
|
||||
}
|
||||
|
||||
int countMaxPluginChannels (const String& configString, bool isInput)
|
||||
{
|
||||
StringArray configs;
|
||||
configs.addTokens (configString, ", {}", StringRef());
|
||||
configs.trim();
|
||||
configs.removeEmptyStrings();
|
||||
jassert ((configs.size() & 1) == 0); // looks like a syntax error in the configs?
|
||||
|
||||
int maxVal = 0;
|
||||
for (int i = (isInput ? 0 : 1); i < configs.size(); i += 2)
|
||||
maxVal = jmax (maxVal, configs[i].getIntValue());
|
||||
|
||||
return maxVal;
|
||||
}
|
||||
|
||||
String valueToBool (const Value& v)
|
||||
{
|
||||
return static_cast<bool> (v.getValue()) ? "1" : "0";
|
||||
}
|
||||
|
||||
String valueToStringLiteral (const var& v)
|
||||
{
|
||||
return CppTokeniserFunctions::addEscapeChars (v.toString()).quoted();
|
||||
}
|
||||
|
||||
String valueToCharLiteral (const var& v)
|
||||
{
|
||||
return CppTokeniserFunctions::addEscapeChars (v.toString().trim().substring (0, 4)).quoted ('\'');
|
||||
}
|
||||
|
||||
void writePluginCharacteristicsFile (ProjectSaver& projectSaver)
|
||||
{
|
||||
Project& project = projectSaver.project;
|
||||
|
||||
StringPairArray flags;
|
||||
//flags.set ("JUCE_MODAL_LOOPS_PERMITTED", "0");
|
||||
flags.set ("JucePlugin_Build_VST", valueToBool (shouldBuildVST (project)));
|
||||
flags.set ("JucePlugin_Build_VST3", valueToBool (shouldBuildVST3 (project)));
|
||||
flags.set ("JucePlugin_Build_AU", valueToBool (shouldBuildAU (project)));
|
||||
flags.set ("JucePlugin_Build_RTAS", valueToBool (shouldBuildRTAS (project)));
|
||||
flags.set ("JucePlugin_Build_AAX", valueToBool (shouldBuildAAX (project)));
|
||||
flags.set ("JucePlugin_Name", valueToStringLiteral (getPluginName (project)));
|
||||
flags.set ("JucePlugin_Desc", valueToStringLiteral (getPluginDesc (project)));
|
||||
flags.set ("JucePlugin_Manufacturer", valueToStringLiteral (getPluginManufacturer (project)));
|
||||
flags.set ("JucePlugin_ManufacturerWebsite", valueToStringLiteral (project.getCompanyWebsite()));
|
||||
flags.set ("JucePlugin_ManufacturerEmail", valueToStringLiteral (project.getCompanyEmail()));
|
||||
flags.set ("JucePlugin_ManufacturerCode", valueToCharLiteral (getPluginManufacturerCode (project)));
|
||||
flags.set ("JucePlugin_PluginCode", valueToCharLiteral (getPluginCode (project)));
|
||||
flags.set ("JucePlugin_MaxNumInputChannels", String (countMaxPluginChannels (getPluginChannelConfigs (project).toString(), true)));
|
||||
flags.set ("JucePlugin_MaxNumOutputChannels", String (countMaxPluginChannels (getPluginChannelConfigs (project).toString(), false)));
|
||||
flags.set ("JucePlugin_PreferredChannelConfigurations", getPluginChannelConfigs (project).toString());
|
||||
flags.set ("JucePlugin_IsSynth", valueToBool (getPluginIsSynth (project)));
|
||||
flags.set ("JucePlugin_WantsMidiInput", valueToBool (getPluginWantsMidiInput (project)));
|
||||
flags.set ("JucePlugin_ProducesMidiOutput", valueToBool (getPluginProducesMidiOut (project)));
|
||||
flags.set ("JucePlugin_SilenceInProducesSilenceOut", valueToBool (getPluginSilenceInProducesSilenceOut (project)));
|
||||
flags.set ("JucePlugin_EditorRequiresKeyboardFocus", valueToBool (getPluginEditorNeedsKeyFocus (project)));
|
||||
flags.set ("JucePlugin_Version", project.getVersionString());
|
||||
flags.set ("JucePlugin_VersionCode", project.getVersionAsHex());
|
||||
flags.set ("JucePlugin_VersionString", valueToStringLiteral (project.getVersionString()));
|
||||
flags.set ("JucePlugin_VSTUniqueID", "JucePlugin_PluginCode");
|
||||
flags.set ("JucePlugin_VSTCategory", getPluginVSTCategoryString (project));
|
||||
flags.set ("JucePlugin_AUMainType", getAUMainTypeString (project));
|
||||
flags.set ("JucePlugin_AUSubType", "JucePlugin_PluginCode");
|
||||
flags.set ("JucePlugin_AUExportPrefix", getPluginAUExportPrefix (project).toString());
|
||||
flags.set ("JucePlugin_AUExportPrefixQuoted", valueToStringLiteral (getPluginAUExportPrefix (project)));
|
||||
flags.set ("JucePlugin_AUManufacturerCode", "JucePlugin_ManufacturerCode");
|
||||
flags.set ("JucePlugin_CFBundleIdentifier", project.getBundleIdentifier().toString());
|
||||
flags.set ("JucePlugin_RTASCategory", getPluginRTASCategoryCode (project));
|
||||
flags.set ("JucePlugin_RTASManufacturerCode", "JucePlugin_ManufacturerCode");
|
||||
flags.set ("JucePlugin_RTASProductId", "JucePlugin_PluginCode");
|
||||
flags.set ("JucePlugin_RTASDisableBypass", valueToBool (getPluginRTASBypassDisabled (project)));
|
||||
flags.set ("JucePlugin_RTASDisableMultiMono", valueToBool (getPluginRTASMultiMonoDisabled (project)));
|
||||
flags.set ("JucePlugin_AAXIdentifier", project.getAAXIdentifier().toString());
|
||||
flags.set ("JucePlugin_AAXManufacturerCode", "JucePlugin_ManufacturerCode");
|
||||
flags.set ("JucePlugin_AAXProductId", "JucePlugin_PluginCode");
|
||||
flags.set ("JucePlugin_AAXCategory", getPluginAAXCategory (project).toString());
|
||||
flags.set ("JucePlugin_AAXDisableBypass", valueToBool (getPluginAAXBypassDisabled (project)));
|
||||
flags.set ("JucePlugin_AAXDisableMultiMono", valueToBool (getPluginAAXMultiMonoDisabled (project)));
|
||||
|
||||
MemoryOutputStream mem;
|
||||
|
||||
mem << "//==============================================================================" << newLine
|
||||
<< "// Audio plugin settings.." << newLine
|
||||
<< newLine;
|
||||
|
||||
for (int i = 0; i < flags.size(); ++i)
|
||||
{
|
||||
mem << "#ifndef " << flags.getAllKeys()[i] << newLine
|
||||
<< " #define " << flags.getAllKeys()[i].paddedRight (' ', 32) << " "
|
||||
<< flags.getAllValues()[i] << newLine
|
||||
<< "#endif" << newLine;
|
||||
}
|
||||
|
||||
projectSaver.setExtraAppConfigFileContent (mem.toString());
|
||||
}
|
||||
|
||||
static void fixMissingXcodePostBuildScript (ProjectExporter& exporter)
|
||||
{
|
||||
if (exporter.isXcode() && exporter.settings [Ids::postbuildCommand].toString().isEmpty())
|
||||
exporter.getSetting (Ids::postbuildCommand) = String::fromUTF8 (BinaryData::AudioPluginXCodeScript_txt,
|
||||
BinaryData::AudioPluginXCodeScript_txtSize);
|
||||
}
|
||||
|
||||
String createEscapedStringForVersion (ProjectExporter& exporter, const String& text)
|
||||
{
|
||||
// (VS10 automatically adds escape characters to the quotes for this definition)
|
||||
return exporter.getVisualStudioVersion() < 10 ? CppTokeniserFunctions::addEscapeChars (text.quoted())
|
||||
: CppTokeniserFunctions::addEscapeChars (text).quoted();
|
||||
}
|
||||
|
||||
String createRebasedPath (ProjectExporter& exporter, const RelativePath& path)
|
||||
{
|
||||
return createEscapedStringForVersion (exporter,
|
||||
exporter.rebaseFromProjectFolderToBuildTarget (path)
|
||||
.toWindowsStyle());
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
namespace VSTHelpers
|
||||
{
|
||||
static Value getVSTFolder (ProjectExporter& exporter, bool isVST3)
|
||||
{
|
||||
return exporter.getSetting (isVST3 ? Ids::vst3Folder
|
||||
: Ids::vstFolder);
|
||||
}
|
||||
|
||||
static void addVSTFolderToPath (ProjectExporter& exporter, bool isVST3)
|
||||
{
|
||||
const String vstFolder (getVSTFolder (exporter, isVST3).toString());
|
||||
|
||||
if (vstFolder.isNotEmpty())
|
||||
{
|
||||
RelativePath path (exporter.rebaseFromProjectFolderToBuildTarget (RelativePath (vstFolder, RelativePath::projectFolder)));
|
||||
|
||||
if (exporter.isVisualStudio())
|
||||
exporter.extraSearchPaths.add (path.toWindowsStyle());
|
||||
else if (exporter.isLinux() || exporter.isXcode())
|
||||
exporter.extraSearchPaths.insert (0, path.toUnixStyle());
|
||||
}
|
||||
}
|
||||
|
||||
static void createVSTPathEditor (ProjectExporter& exporter, PropertyListBuilder& props, bool isVST3)
|
||||
{
|
||||
const String vstFormat (isVST3 ? "VST3" : "VST");
|
||||
|
||||
props.add (new TextPropertyComponent (getVSTFolder (exporter, isVST3), vstFormat + " Folder", 1024, false),
|
||||
"If you're building a " + vstFormat + ", this must be the folder containing the " + vstFormat + " SDK. This should be an absolute path.");
|
||||
}
|
||||
|
||||
static void fixMissingVSTValues (ProjectExporter& exporter, bool isVST3)
|
||||
{
|
||||
if (getVSTFolder (exporter, isVST3).toString().isEmpty())
|
||||
getVSTFolder (exporter, isVST3) = exporter.isWindows() ? (isVST3 ? "c:\\SDKs\\VST3 SDK" : "c:\\SDKs\\vstsdk2.4")
|
||||
: (isVST3 ? "~/SDKs/VST3 SDK" : "~/SDKs/vstsdk2.4");
|
||||
|
||||
fixMissingXcodePostBuildScript (exporter);
|
||||
}
|
||||
|
||||
static inline void prepareExporter (ProjectExporter& exporter, ProjectSaver& projectSaver, bool isVST3)
|
||||
{
|
||||
fixMissingVSTValues (exporter, isVST3);
|
||||
writePluginCharacteristicsFile (projectSaver);
|
||||
|
||||
exporter.makefileTargetSuffix = ".so";
|
||||
|
||||
Project::Item group (Project::Item::createGroup (const_cast<ProjectExporter&> (exporter).getProject(),
|
||||
"Juce VST Wrapper", "__jucevstfiles"));
|
||||
|
||||
RelativePath juceWrapperFolder (exporter.getProject().getGeneratedCodeFolder(),
|
||||
exporter.getTargetFolder(), RelativePath::buildTargetFolder);
|
||||
|
||||
addVSTFolderToPath (exporter, isVST3);
|
||||
|
||||
if (exporter.isWindows())
|
||||
exporter.extraSearchPaths.add (juceWrapperFolder.toWindowsStyle());
|
||||
else if (exporter.isLinux())
|
||||
exporter.extraSearchPaths.add (juceWrapperFolder.toUnixStyle());
|
||||
|
||||
if (exporter.isVisualStudio())
|
||||
{
|
||||
if (! exporter.getExtraLinkerFlagsString().contains ("/FORCE:multiple"))
|
||||
exporter.getExtraLinkerFlags() = exporter.getExtraLinkerFlags().toString() + " /FORCE:multiple";
|
||||
|
||||
RelativePath modulePath (exporter.rebaseFromProjectFolderToBuildTarget (RelativePath (exporter.getPathForModuleString ("juce_audio_plugin_client"),
|
||||
RelativePath::projectFolder)
|
||||
.getChildFile ("juce_audio_plugin_client")
|
||||
.getChildFile ("VST3")));
|
||||
|
||||
for (ProjectExporter::ConfigIterator config (exporter); config.next();)
|
||||
{
|
||||
if (config->getValue (Ids::useRuntimeLibDLL).getValue().isVoid())
|
||||
config->getValue (Ids::useRuntimeLibDLL) = true;
|
||||
|
||||
if (isVST3)
|
||||
if (config->getValue (Ids::postbuildCommand).toString().isEmpty())
|
||||
config->getValue (Ids::postbuildCommand) = "copy /Y \"$(OutDir)\\$(TargetFileName)\" \"$(OutDir)\\$(TargetName).vst3\"";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static inline void createPropertyEditors (ProjectExporter& exporter, PropertyListBuilder& props, bool isVST3)
|
||||
{
|
||||
fixMissingVSTValues (exporter, isVST3);
|
||||
createVSTPathEditor (exporter, props, isVST3);
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
namespace RTASHelpers
|
||||
{
|
||||
static Value getRTASFolder (ProjectExporter& exporter) { return exporter.getSetting (Ids::rtasFolder); }
|
||||
static RelativePath getRTASFolderPath (ProjectExporter& exporter) { return RelativePath (exporter.getSettingString (Ids::rtasFolder),
|
||||
RelativePath::projectFolder); }
|
||||
|
||||
static bool isExporterSupported (ProjectExporter& exporter) { return exporter.isVisualStudio() || exporter.isXcode(); }
|
||||
|
||||
static void fixMissingRTASValues (ProjectExporter& exporter)
|
||||
{
|
||||
if (getRTASFolder (exporter).toString().isEmpty())
|
||||
{
|
||||
if (exporter.isVisualStudio())
|
||||
getRTASFolder (exporter) = "c:\\SDKs\\PT_80_SDK";
|
||||
else
|
||||
getRTASFolder (exporter) = "~/SDKs/PT_80_SDK";
|
||||
}
|
||||
|
||||
fixMissingXcodePostBuildScript (exporter);
|
||||
}
|
||||
|
||||
static void addExtraSearchPaths (ProjectExporter& exporter)
|
||||
{
|
||||
RelativePath rtasFolder (getRTASFolderPath (exporter));
|
||||
|
||||
if (exporter.isVisualStudio())
|
||||
{
|
||||
RelativePath juceWrapperFolder (exporter.getProject().getGeneratedCodeFolder(),
|
||||
exporter.getTargetFolder(), RelativePath::buildTargetFolder);
|
||||
|
||||
exporter.extraSearchPaths.add (juceWrapperFolder.toWindowsStyle());
|
||||
|
||||
static const char* p[] = { "AlturaPorts/TDMPlugins/PluginLibrary/EffectClasses",
|
||||
"AlturaPorts/TDMPlugins/PluginLibrary/ProcessClasses",
|
||||
"AlturaPorts/TDMPlugins/PluginLibrary/ProcessClasses/Interfaces",
|
||||
"AlturaPorts/TDMPlugins/PluginLibrary/Utilities",
|
||||
"AlturaPorts/TDMPlugins/PluginLibrary/RTASP_Adapt",
|
||||
"AlturaPorts/TDMPlugins/PluginLibrary/CoreClasses",
|
||||
"AlturaPorts/TDMPlugins/PluginLibrary/Controls",
|
||||
"AlturaPorts/TDMPlugins/PluginLibrary/Meters",
|
||||
"AlturaPorts/TDMPlugins/PluginLibrary/ViewClasses",
|
||||
"AlturaPorts/TDMPlugins/PluginLibrary/DSPClasses",
|
||||
"AlturaPorts/TDMPlugins/PluginLibrary/Interfaces",
|
||||
"AlturaPorts/TDMPlugins/common",
|
||||
"AlturaPorts/TDMPlugins/common/Platform",
|
||||
"AlturaPorts/TDMPlugins/common/Macros",
|
||||
"AlturaPorts/TDMPlugins/SignalProcessing/Public",
|
||||
"AlturaPorts/TDMPlugIns/DSPManager/Interfaces",
|
||||
"AlturaPorts/SADriver/Interfaces",
|
||||
"AlturaPorts/DigiPublic/Interfaces",
|
||||
"AlturaPorts/DigiPublic",
|
||||
"AlturaPorts/Fic/Interfaces/DAEClient",
|
||||
"AlturaPorts/NewFileLibs/Cmn",
|
||||
"AlturaPorts/NewFileLibs/DOA",
|
||||
"AlturaPorts/AlturaSource/PPC_H",
|
||||
"AlturaPorts/AlturaSource/AppSupport",
|
||||
"AvidCode/AVX2sdk/AVX/avx2/avx2sdk/inc",
|
||||
"xplat/AVX/avx2/avx2sdk/inc" };
|
||||
|
||||
for (int i = 0; i < numElementsInArray (p); ++i)
|
||||
exporter.addToExtraSearchPaths (rtasFolder.getChildFile (p[i]));
|
||||
}
|
||||
else if (exporter.isXcode())
|
||||
{
|
||||
exporter.extraSearchPaths.add ("$(DEVELOPER_DIR)/Headers/FlatCarbon");
|
||||
exporter.extraSearchPaths.add ("$(SDKROOT)/Developer/Headers/FlatCarbon");
|
||||
|
||||
static const char* p[] = { "AlturaPorts/TDMPlugIns/PlugInLibrary/Controls",
|
||||
"AlturaPorts/TDMPlugIns/PlugInLibrary/CoreClasses",
|
||||
"AlturaPorts/TDMPlugIns/PlugInLibrary/DSPClasses",
|
||||
"AlturaPorts/TDMPlugIns/PlugInLibrary/EffectClasses",
|
||||
"AlturaPorts/TDMPlugIns/PlugInLibrary/MacBuild",
|
||||
"AlturaPorts/TDMPlugIns/PlugInLibrary/Meters",
|
||||
"AlturaPorts/TDMPlugIns/PlugInLibrary/ProcessClasses",
|
||||
"AlturaPorts/TDMPlugIns/PlugInLibrary/ProcessClasses/Interfaces",
|
||||
"AlturaPorts/TDMPlugIns/PlugInLibrary/RTASP_Adapt",
|
||||
"AlturaPorts/TDMPlugIns/PlugInLibrary/Utilities",
|
||||
"AlturaPorts/TDMPlugIns/PlugInLibrary/ViewClasses",
|
||||
"AlturaPorts/TDMPlugIns/DSPManager/**",
|
||||
"AlturaPorts/TDMPlugIns/SupplementalPlugInLib/Encryption",
|
||||
"AlturaPorts/TDMPlugIns/SupplementalPlugInLib/GraphicsExtensions",
|
||||
"AlturaPorts/TDMPlugIns/common/**",
|
||||
"AlturaPorts/TDMPlugIns/common/PI_LibInterface",
|
||||
"AlturaPorts/TDMPlugIns/PACEProtection/**",
|
||||
"AlturaPorts/TDMPlugIns/SignalProcessing/**",
|
||||
"AlturaPorts/OMS/Headers",
|
||||
"AlturaPorts/Fic/Interfaces/**",
|
||||
"AlturaPorts/Fic/Source/SignalNets",
|
||||
"AlturaPorts/DSIPublicInterface/PublicHeaders",
|
||||
"DAEWin/Include",
|
||||
"AlturaPorts/DigiPublic/Interfaces",
|
||||
"AlturaPorts/DigiPublic",
|
||||
"AlturaPorts/NewFileLibs/DOA",
|
||||
"AlturaPorts/NewFileLibs/Cmn",
|
||||
"xplat/AVX/avx2/avx2sdk/inc",
|
||||
"xplat/AVX/avx2/avx2sdk/utils" };
|
||||
|
||||
for (int i = 0; i < numElementsInArray (p); ++i)
|
||||
exporter.addToExtraSearchPaths (rtasFolder.getChildFile (p[i]));
|
||||
}
|
||||
}
|
||||
|
||||
static inline void prepareExporter (ProjectExporter& exporter, ProjectSaver& projectSaver)
|
||||
{
|
||||
if (isExporterSupported (exporter))
|
||||
{
|
||||
fixMissingRTASValues (exporter);
|
||||
|
||||
const RelativePath rtasFolder (getRTASFolderPath (exporter));
|
||||
|
||||
if (exporter.isVisualStudio())
|
||||
{
|
||||
exporter.msvcTargetSuffix = ".dpm";
|
||||
|
||||
exporter.msvcExtraPreprocessorDefs.set ("JucePlugin_WinBag_path",
|
||||
createRebasedPath (exporter,
|
||||
rtasFolder.getChildFile ("WinBag")));
|
||||
|
||||
exporter.msvcDelayLoadedDLLs = "DAE.dll; DigiExt.dll; DSI.dll; PluginLib.dll; "
|
||||
"DSPManager.dll; DSPManager.dll; DSPManagerClientLib.dll; RTASClientLib.dll";
|
||||
|
||||
if (! exporter.getExtraLinkerFlagsString().contains ("/FORCE:multiple"))
|
||||
exporter.getExtraLinkerFlags() = exporter.getExtraLinkerFlags().toString() + " /FORCE:multiple";
|
||||
|
||||
RelativePath modulePath (exporter.rebaseFromProjectFolderToBuildTarget (RelativePath (exporter.getPathForModuleString ("juce_audio_plugin_client"),
|
||||
RelativePath::projectFolder)
|
||||
.getChildFile ("juce_audio_plugin_client")
|
||||
.getChildFile ("RTAS")));
|
||||
|
||||
for (ProjectExporter::ConfigIterator config (exporter); config.next();)
|
||||
{
|
||||
config->getValue (Ids::msvcModuleDefinitionFile) = modulePath.getChildFile ("juce_RTAS_WinExports.def").toWindowsStyle();
|
||||
|
||||
if (config->getValue (Ids::useRuntimeLibDLL).getValue().isVoid())
|
||||
config->getValue (Ids::useRuntimeLibDLL) = true;
|
||||
|
||||
if (config->getValue (Ids::postbuildCommand).toString().isEmpty())
|
||||
config->getValue (Ids::postbuildCommand)
|
||||
= "copy /Y "
|
||||
+ modulePath.getChildFile ("juce_RTAS_WinResources.rsr").toWindowsStyle().quoted()
|
||||
+ " \"$(TargetPath)\".rsr";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
exporter.xcodeCanUseDwarf = false;
|
||||
|
||||
exporter.xcodeExtraLibrariesDebug.add (rtasFolder.getChildFile ("MacBag/Libs/Debug/libPluginLibrary.a"));
|
||||
exporter.xcodeExtraLibrariesRelease.add (rtasFolder.getChildFile ("MacBag/Libs/Release/libPluginLibrary.a"));
|
||||
}
|
||||
|
||||
writePluginCharacteristicsFile (projectSaver);
|
||||
|
||||
addExtraSearchPaths (exporter);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void createPropertyEditors (ProjectExporter& exporter, PropertyListBuilder& props)
|
||||
{
|
||||
if (isExporterSupported (exporter))
|
||||
{
|
||||
fixMissingRTASValues (exporter);
|
||||
|
||||
props.add (new TextPropertyComponent (getRTASFolder (exporter), "RTAS Folder", 1024, false),
|
||||
"If you're building an RTAS, this must be the folder containing the RTAS SDK. This should be an absolute path.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
namespace AUHelpers
|
||||
{
|
||||
static inline void prepareExporter (ProjectExporter& exporter, ProjectSaver& projectSaver)
|
||||
{
|
||||
writePluginCharacteristicsFile (projectSaver);
|
||||
|
||||
if (exporter.isXcode())
|
||||
{
|
||||
exporter.extraSearchPaths.add ("$(DEVELOPER_DIR)/Extras/CoreAudio/PublicUtility");
|
||||
exporter.extraSearchPaths.add ("$(DEVELOPER_DIR)/Extras/CoreAudio/AudioUnits/AUPublic/Utility");
|
||||
exporter.extraSearchPaths.add ("$(DEVELOPER_DIR)/Extras/CoreAudio/AudioUnits/AUPublic/AUBase");
|
||||
|
||||
exporter.xcodeFrameworks.addTokens ("AudioUnit CoreAudioKit", false);
|
||||
exporter.xcodeExcludedFiles64Bit = "\"*Carbon*.cpp\"";
|
||||
|
||||
Project::Item subGroup (projectSaver.getGeneratedCodeGroup().addNewSubGroup ("Juce AU Wrapper", -1));
|
||||
subGroup.setID ("__juceappleaufiles");
|
||||
|
||||
{
|
||||
#define JUCE_AU_PUBLICUTILITY "${DEVELOPER_DIR}/Extras/CoreAudio/PublicUtility/"
|
||||
#define JUCE_AU_PUBLIC "${DEVELOPER_DIR}/Extras/CoreAudio/AudioUnits/AUPublic/"
|
||||
|
||||
static const char* appleAUFiles[] =
|
||||
{
|
||||
JUCE_AU_PUBLICUTILITY "CADebugMacros.h",
|
||||
JUCE_AU_PUBLICUTILITY "CAAUParameter.cpp",
|
||||
JUCE_AU_PUBLICUTILITY "CAAUParameter.h",
|
||||
JUCE_AU_PUBLICUTILITY "CAAudioChannelLayout.cpp",
|
||||
JUCE_AU_PUBLICUTILITY "CAAudioChannelLayout.h",
|
||||
JUCE_AU_PUBLICUTILITY "CAMutex.cpp",
|
||||
JUCE_AU_PUBLICUTILITY "CAMutex.h",
|
||||
JUCE_AU_PUBLICUTILITY "CAStreamBasicDescription.cpp",
|
||||
JUCE_AU_PUBLICUTILITY "CAStreamBasicDescription.h",
|
||||
JUCE_AU_PUBLICUTILITY "CAVectorUnitTypes.h",
|
||||
JUCE_AU_PUBLICUTILITY "CAVectorUnit.cpp",
|
||||
JUCE_AU_PUBLICUTILITY "CAVectorUnit.h",
|
||||
JUCE_AU_PUBLIC "AUViewBase/AUViewLocalizedStringKeys.h",
|
||||
JUCE_AU_PUBLIC "AUCarbonViewBase/AUCarbonViewDispatch.cpp",
|
||||
JUCE_AU_PUBLIC "AUCarbonViewBase/AUCarbonViewControl.cpp",
|
||||
JUCE_AU_PUBLIC "AUCarbonViewBase/AUCarbonViewControl.h",
|
||||
JUCE_AU_PUBLIC "AUCarbonViewBase/CarbonEventHandler.cpp",
|
||||
JUCE_AU_PUBLIC "AUCarbonViewBase/CarbonEventHandler.h",
|
||||
JUCE_AU_PUBLIC "AUCarbonViewBase/AUCarbonViewBase.cpp",
|
||||
JUCE_AU_PUBLIC "AUCarbonViewBase/AUCarbonViewBase.h",
|
||||
JUCE_AU_PUBLIC "AUBase/AUBase.cpp",
|
||||
JUCE_AU_PUBLIC "AUBase/AUBase.h",
|
||||
JUCE_AU_PUBLIC "AUBase/AUDispatch.cpp",
|
||||
JUCE_AU_PUBLIC "AUBase/AUDispatch.h",
|
||||
JUCE_AU_PUBLIC "AUBase/AUInputElement.cpp",
|
||||
JUCE_AU_PUBLIC "AUBase/AUInputElement.h",
|
||||
JUCE_AU_PUBLIC "AUBase/AUOutputElement.cpp",
|
||||
JUCE_AU_PUBLIC "AUBase/AUOutputElement.h",
|
||||
JUCE_AU_PUBLIC "AUBase/AUResources.r",
|
||||
JUCE_AU_PUBLIC "AUBase/AUScopeElement.cpp",
|
||||
JUCE_AU_PUBLIC "AUBase/AUScopeElement.h",
|
||||
JUCE_AU_PUBLIC "AUBase/ComponentBase.cpp",
|
||||
JUCE_AU_PUBLIC "AUBase/ComponentBase.h",
|
||||
JUCE_AU_PUBLIC "OtherBases/AUMIDIBase.cpp",
|
||||
JUCE_AU_PUBLIC "OtherBases/AUMIDIBase.h",
|
||||
JUCE_AU_PUBLIC "OtherBases/AUMIDIEffectBase.cpp",
|
||||
JUCE_AU_PUBLIC "OtherBases/AUMIDIEffectBase.h",
|
||||
JUCE_AU_PUBLIC "OtherBases/AUOutputBase.cpp",
|
||||
JUCE_AU_PUBLIC "OtherBases/AUOutputBase.h",
|
||||
JUCE_AU_PUBLIC "OtherBases/MusicDeviceBase.cpp",
|
||||
JUCE_AU_PUBLIC "OtherBases/MusicDeviceBase.h",
|
||||
JUCE_AU_PUBLIC "OtherBases/AUEffectBase.cpp",
|
||||
JUCE_AU_PUBLIC "OtherBases/AUEffectBase.h",
|
||||
JUCE_AU_PUBLIC "Utility/AUBuffer.cpp",
|
||||
JUCE_AU_PUBLIC "Utility/AUBuffer.h",
|
||||
JUCE_AU_PUBLIC "Utility/AUInputFormatConverter.h",
|
||||
JUCE_AU_PUBLIC "Utility/AUSilentTimeout.h",
|
||||
JUCE_AU_PUBLIC "Utility/AUTimestampGenerator.h",
|
||||
nullptr
|
||||
};
|
||||
|
||||
for (const char** f = appleAUFiles; *f != nullptr; ++f)
|
||||
{
|
||||
const RelativePath file (*f, RelativePath::projectFolder);
|
||||
subGroup.addRelativeFile (file, -1, file.hasFileExtension ("cpp;mm"));
|
||||
subGroup.getChild (subGroup.getNumChildren() - 1).getShouldInhibitWarningsValue() = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (exporter.isXcode())
|
||||
{
|
||||
XmlElement plistKey ("key");
|
||||
plistKey.addTextElement ("AudioComponents");
|
||||
|
||||
XmlElement plistEntry ("array");
|
||||
XmlElement* dict = plistEntry.createNewChildElement ("dict");
|
||||
|
||||
Project& project = exporter.getProject();
|
||||
|
||||
addPlistDictionaryKey (dict, "name", getPluginManufacturer (project).toString()
|
||||
+ ": " + getPluginName (project).toString());
|
||||
addPlistDictionaryKey (dict, "description", getPluginDesc (project).toString());
|
||||
addPlistDictionaryKey (dict, "factoryFunction", getPluginAUExportPrefix (project).toString() + "Factory");
|
||||
addPlistDictionaryKey (dict, "manufacturer", getPluginManufacturerCode (project).toString().trim().substring (0, 4));
|
||||
addPlistDictionaryKey (dict, "type", getAUMainTypeCode (project));
|
||||
addPlistDictionaryKey (dict, "subtype", getPluginCode (project).toString().trim().substring (0, 4));
|
||||
addPlistDictionaryKeyInt (dict, "version", project.getVersionAsHexInteger());
|
||||
|
||||
exporter.xcodeExtraPListEntries.add (plistKey);
|
||||
exporter.xcodeExtraPListEntries.add (plistEntry);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
namespace AAXHelpers
|
||||
{
|
||||
static Value getAAXFolder (ProjectExporter& exporter) { return exporter.getSetting (Ids::aaxFolder); }
|
||||
static RelativePath getAAXFolderPath (ProjectExporter& exporter) { return RelativePath (exporter.getSettingString (Ids::aaxFolder),
|
||||
RelativePath::projectFolder); }
|
||||
|
||||
static bool isExporterSupported (ProjectExporter& exporter) { return exporter.isVisualStudio() || exporter.isXcode(); }
|
||||
|
||||
static void fixMissingAAXValues (ProjectExporter& exporter)
|
||||
{
|
||||
if (getAAXFolder (exporter).toString().isEmpty())
|
||||
{
|
||||
if (exporter.isVisualStudio())
|
||||
getAAXFolder (exporter) = "c:\\SDKs\\AAX";
|
||||
else
|
||||
getAAXFolder (exporter) = "~/SDKs/AAX";
|
||||
}
|
||||
|
||||
fixMissingXcodePostBuildScript (exporter);
|
||||
}
|
||||
|
||||
static void addExtraSearchPaths (ProjectExporter& exporter)
|
||||
{
|
||||
const RelativePath aaxFolder (getAAXFolderPath (exporter));
|
||||
|
||||
exporter.addToExtraSearchPaths (aaxFolder);
|
||||
exporter.addToExtraSearchPaths (aaxFolder.getChildFile ("Interfaces"));
|
||||
exporter.addToExtraSearchPaths (aaxFolder.getChildFile ("Interfaces").getChildFile ("ACF"));
|
||||
}
|
||||
|
||||
static inline void prepareExporter (ProjectExporter& exporter, ProjectSaver& projectSaver)
|
||||
{
|
||||
if (isExporterSupported (exporter))
|
||||
{
|
||||
fixMissingAAXValues (exporter);
|
||||
|
||||
const RelativePath aaxLibsFolder (getAAXFolderPath (exporter).getChildFile ("Libs"));
|
||||
|
||||
if (exporter.isVisualStudio())
|
||||
{
|
||||
exporter.msvcTargetSuffix = ".aaxplugin";
|
||||
|
||||
for (ProjectExporter::ConfigIterator config (exporter); config.next();)
|
||||
if (config->getValue (Ids::useRuntimeLibDLL).getValue().isVoid())
|
||||
config->getValue (Ids::useRuntimeLibDLL) = true;
|
||||
|
||||
exporter.msvcExtraPreprocessorDefs.set ("JucePlugin_AAXLibs_path",
|
||||
createRebasedPath (exporter, aaxLibsFolder));
|
||||
}
|
||||
else
|
||||
{
|
||||
exporter.xcodeExtraLibrariesDebug.add (aaxLibsFolder.getChildFile ("Debug/libAAXLibrary.a"));
|
||||
exporter.xcodeExtraLibrariesRelease.add (aaxLibsFolder.getChildFile ("Release/libAAXLibrary.a"));
|
||||
}
|
||||
|
||||
writePluginCharacteristicsFile (projectSaver);
|
||||
|
||||
addExtraSearchPaths (exporter);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void createPropertyEditors (ProjectExporter& exporter, PropertyListBuilder& props)
|
||||
{
|
||||
if (isExporterSupported (exporter))
|
||||
{
|
||||
fixMissingAAXValues (exporter);
|
||||
|
||||
props.add (new TextPropertyComponent (getAAXFolder (exporter), "AAX SDK Folder", 1024, false),
|
||||
"If you're building an AAX, this must be the folder containing the AAX SDK. This should be an absolute path.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif // __JUCER_AUDIOPLUGINMODULE_JUCEHEADER__
|
||||
@@ -0,0 +1,296 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
|
||||
Permission is granted to use this software under the terms of either:
|
||||
a) the GPL v2 (or any later version)
|
||||
b) the Affero GPL v3
|
||||
|
||||
Details of these licenses can be found at: www.gnu.org/licenses
|
||||
|
||||
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.juce.com for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
class PropertyGroupComponent : public Component
|
||||
{
|
||||
public:
|
||||
PropertyGroupComponent() {}
|
||||
|
||||
void setProperties (const PropertyListBuilder& newProps)
|
||||
{
|
||||
properties.clear();
|
||||
properties.addArray (newProps.components);
|
||||
|
||||
for (int i = properties.size(); --i >= 0;)
|
||||
addAndMakeVisible (properties.getUnchecked(i));
|
||||
}
|
||||
|
||||
int updateSize (int x, int y, int width)
|
||||
{
|
||||
int height = 38;
|
||||
|
||||
for (int i = 0; i < properties.size(); ++i)
|
||||
{
|
||||
PropertyComponent* pp = properties.getUnchecked(i);
|
||||
pp->setBounds (10, height, width - 20, pp->getPreferredHeight());
|
||||
height += pp->getHeight();
|
||||
}
|
||||
|
||||
height += 16;
|
||||
setBounds (x, y, width, height);
|
||||
return height;
|
||||
}
|
||||
|
||||
void paint (Graphics& g) override
|
||||
{
|
||||
const Colour bkg (findColour (mainBackgroundColourId));
|
||||
|
||||
g.setColour (Colours::white.withAlpha (0.35f));
|
||||
g.fillRect (0, 30, getWidth(), getHeight() - 38);
|
||||
|
||||
g.setFont (Font (15.0f, Font::bold));
|
||||
g.setColour (bkg.contrasting (0.7f));
|
||||
g.drawFittedText (getName(), 12, 0, getWidth() - 16, 25, Justification::bottomLeft, 1);
|
||||
}
|
||||
|
||||
OwnedArray<PropertyComponent> properties;
|
||||
|
||||
private:
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (PropertyGroupComponent)
|
||||
};
|
||||
|
||||
|
||||
//==============================================================================
|
||||
class ConfigTreeItemBase : public JucerTreeViewBase,
|
||||
public ValueTree::Listener
|
||||
{
|
||||
public:
|
||||
ConfigTreeItemBase() {}
|
||||
|
||||
void showSettingsPage (Component* content)
|
||||
{
|
||||
content->setComponentID (getUniqueName());
|
||||
|
||||
ScopedPointer<Component> comp (content);
|
||||
|
||||
if (ProjectContentComponent* pcc = getProjectContentComponent())
|
||||
pcc->setEditorComponent (new PropertyPanelViewport (comp.release()), nullptr);
|
||||
}
|
||||
|
||||
void closeSettingsPage()
|
||||
{
|
||||
if (ProjectContentComponent* pcc = getProjectContentComponent())
|
||||
{
|
||||
if (PropertyPanelViewport* ppv = dynamic_cast<PropertyPanelViewport*> (pcc->getEditorComponent()))
|
||||
if (ppv->viewport.getViewedComponent()->getComponentID() == getUniqueName())
|
||||
pcc->hideEditor();
|
||||
}
|
||||
}
|
||||
|
||||
void deleteAllSelectedItems() override
|
||||
{
|
||||
TreeView* const tree = getOwnerView();
|
||||
jassert (tree->getNumSelectedItems() <= 1); // multi-select should be disabled
|
||||
|
||||
if (ConfigTreeItemBase* s = dynamic_cast<ConfigTreeItemBase*> (tree->getSelectedItem (0)))
|
||||
s->deleteItem();
|
||||
}
|
||||
|
||||
void itemOpennessChanged (bool isNowOpen) override
|
||||
{
|
||||
if (isNowOpen)
|
||||
refreshSubItems();
|
||||
}
|
||||
|
||||
void valueTreePropertyChanged (ValueTree&, const Identifier&) override {}
|
||||
void valueTreeChildAdded (ValueTree&, ValueTree&) override {}
|
||||
void valueTreeChildRemoved (ValueTree&, ValueTree&) override {}
|
||||
void valueTreeChildOrderChanged (ValueTree&) override {}
|
||||
void valueTreeParentChanged (ValueTree&) override {}
|
||||
|
||||
virtual bool isProjectSettings() const { return false; }
|
||||
virtual bool isModulesList() const { return false; }
|
||||
|
||||
static void updateSize (Component& comp, PropertyGroupComponent& group)
|
||||
{
|
||||
const int width = jmax (550, comp.getParentWidth() - 20);
|
||||
|
||||
int y = 0;
|
||||
y += group.updateSize (12, y, width - 12);
|
||||
|
||||
comp.setSize (width, y);
|
||||
}
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
class PropertyPanelViewport : public Component
|
||||
{
|
||||
public:
|
||||
PropertyPanelViewport (Component* content)
|
||||
{
|
||||
addAndMakeVisible (viewport);
|
||||
addAndMakeVisible (rolloverHelp);
|
||||
viewport.setViewedComponent (content, true);
|
||||
}
|
||||
|
||||
void paint (Graphics& g) override
|
||||
{
|
||||
IntrojucerLookAndFeel::fillWithBackgroundTexture (*this, g);
|
||||
}
|
||||
|
||||
void resized() override
|
||||
{
|
||||
Rectangle<int> r (getLocalBounds());
|
||||
rolloverHelp.setBounds (r.removeFromBottom (70).reduced (10, 0));
|
||||
viewport.setBounds (r);
|
||||
}
|
||||
|
||||
Viewport viewport;
|
||||
RolloverHelpComp rolloverHelp;
|
||||
|
||||
private:
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (PropertyPanelViewport)
|
||||
};
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
class RootItem : public ConfigTreeItemBase
|
||||
{
|
||||
public:
|
||||
RootItem (Project& p)
|
||||
: project (p), exportersTree (p.getExporters())
|
||||
{
|
||||
exportersTree.addListener (this);
|
||||
}
|
||||
|
||||
bool isProjectSettings() const override { return true; }
|
||||
String getRenamingName() const override { return getDisplayName(); }
|
||||
String getDisplayName() const override { return project.getTitle(); }
|
||||
void setName (const String&) override {}
|
||||
bool isMissing() override { return false; }
|
||||
Icon getIcon() const override { return project.getMainGroup().getIcon().withContrastingColourTo (getBackgroundColour()); }
|
||||
void showDocument() override { showSettingsPage (new SettingsComp (project)); }
|
||||
bool canBeSelected() const override { return true; }
|
||||
bool mightContainSubItems() override { return project.getNumExporters() > 0; }
|
||||
String getUniqueName() const override { return "config_root"; }
|
||||
|
||||
void addSubItems() override
|
||||
{
|
||||
addSubItem (new EnabledModulesItem (project));
|
||||
IntrojucerApp::getApp().addExtraConfigItems (project, *this);
|
||||
|
||||
int i = 0;
|
||||
for (Project::ExporterIterator exporter (project); exporter.next(); ++i)
|
||||
addSubItem (new ExporterItem (project, exporter.exporter.release(), i));
|
||||
}
|
||||
|
||||
void showPopupMenu() override
|
||||
{
|
||||
PopupMenu menu;
|
||||
|
||||
const StringArray exporters (ProjectExporter::getExporterNames());
|
||||
|
||||
for (int i = 0; i < exporters.size(); ++i)
|
||||
menu.addItem (i + 1, "Create a new " + exporters[i] + " target");
|
||||
|
||||
launchPopupMenu (menu);
|
||||
}
|
||||
|
||||
void handlePopupMenuResult (int resultCode) override
|
||||
{
|
||||
if (resultCode > 0)
|
||||
{
|
||||
String exporterName (ProjectExporter::getExporterNames() [resultCode - 1]);
|
||||
|
||||
if (exporterName.isNotEmpty())
|
||||
project.addNewExporter (exporterName);
|
||||
}
|
||||
}
|
||||
|
||||
bool isInterestedInDragSource (const DragAndDropTarget::SourceDetails& dragSourceDetails) override
|
||||
{
|
||||
return dragSourceDetails.description.toString().startsWith (getUniqueName());
|
||||
}
|
||||
|
||||
void itemDropped (const DragAndDropTarget::SourceDetails& dragSourceDetails, int insertIndex) override
|
||||
{
|
||||
int oldIndex = dragSourceDetails.description.toString().getTrailingIntValue();
|
||||
exportersTree.moveChild (oldIndex, jmax (0, insertIndex - 1), project.getUndoManagerFor (exportersTree));
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void valueTreeChildAdded (ValueTree& parentTree, ValueTree&) override { refreshIfNeeded (parentTree); }
|
||||
void valueTreeChildRemoved (ValueTree& parentTree, ValueTree&) override { refreshIfNeeded (parentTree); }
|
||||
void valueTreeChildOrderChanged (ValueTree& parentTree) override { refreshIfNeeded (parentTree); }
|
||||
|
||||
void refreshIfNeeded (ValueTree& changedTree)
|
||||
{
|
||||
if (changedTree == exportersTree)
|
||||
refreshSubItems();
|
||||
}
|
||||
|
||||
private:
|
||||
Project& project;
|
||||
ValueTree exportersTree;
|
||||
|
||||
//==============================================================================
|
||||
class SettingsComp : public Component,
|
||||
private ChangeListener
|
||||
{
|
||||
public:
|
||||
SettingsComp (Project& p) : project (p)
|
||||
{
|
||||
addAndMakeVisible (group);
|
||||
|
||||
updatePropertyList();
|
||||
project.addChangeListener (this);
|
||||
}
|
||||
|
||||
~SettingsComp()
|
||||
{
|
||||
project.removeChangeListener (this);
|
||||
}
|
||||
|
||||
void parentSizeChanged() override
|
||||
{
|
||||
updateSize (*this, group);
|
||||
}
|
||||
|
||||
void updatePropertyList()
|
||||
{
|
||||
PropertyListBuilder props;
|
||||
project.createPropertyEditors (props);
|
||||
group.setProperties (props);
|
||||
group.setName ("Project Settings");
|
||||
|
||||
lastProjectType = project.getProjectTypeValue().getValue();
|
||||
parentSizeChanged();
|
||||
}
|
||||
|
||||
void changeListenerCallback (ChangeBroadcaster*) override
|
||||
{
|
||||
if (lastProjectType != project.getProjectTypeValue().getValue())
|
||||
updatePropertyList();
|
||||
}
|
||||
|
||||
private:
|
||||
Project& project;
|
||||
var lastProjectType;
|
||||
PropertyGroupComponent group;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (SettingsComp)
|
||||
};
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (RootItem)
|
||||
};
|
||||
@@ -0,0 +1,251 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
|
||||
Permission is granted to use this software under the terms of either:
|
||||
a) the GPL v2 (or any later version)
|
||||
b) the Affero GPL v3
|
||||
|
||||
Details of these licenses can be found at: www.gnu.org/licenses
|
||||
|
||||
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.juce.com for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
class ExporterItem : public ConfigTreeItemBase
|
||||
{
|
||||
public:
|
||||
ExporterItem (Project& p, ProjectExporter* e, int index)
|
||||
: project (p), exporter (e), configListTree (exporter->getConfigurations()),
|
||||
exporterIndex (index)
|
||||
{
|
||||
configListTree.addListener (this);
|
||||
jassert (exporter != nullptr);
|
||||
}
|
||||
|
||||
bool canBeSelected() const override { return true; }
|
||||
bool mightContainSubItems() override { return exporter->getNumConfigurations() > 0; }
|
||||
String getUniqueName() const override { return "exporter_" + String (exporterIndex); }
|
||||
String getRenamingName() const override { return getDisplayName(); }
|
||||
String getDisplayName() const override { return exporter->getName(); }
|
||||
void setName (const String&) override {}
|
||||
bool isMissing() override { return false; }
|
||||
Icon getIcon() const override { return Icon (getIcons().exporter, getContrastingColour (0.5f)); }
|
||||
void showDocument() override { showSettingsPage (new SettingsComp (exporter)); }
|
||||
|
||||
void deleteItem() override
|
||||
{
|
||||
if (AlertWindow::showOkCancelBox (AlertWindow::WarningIcon, "Delete Exporter",
|
||||
"Are you sure you want to delete this export target?"))
|
||||
{
|
||||
closeSettingsPage();
|
||||
ValueTree parent (exporter->settings.getParent());
|
||||
parent.removeChild (exporter->settings, project.getUndoManagerFor (parent));
|
||||
}
|
||||
}
|
||||
|
||||
void addSubItems() override
|
||||
{
|
||||
for (ProjectExporter::ConfigIterator config (*exporter); config.next();)
|
||||
addSubItem (new ConfigItem (config.config, exporter->getName()));
|
||||
}
|
||||
|
||||
void showPopupMenu() override
|
||||
{
|
||||
PopupMenu menu;
|
||||
menu.addItem (1, "Add a new configuration");
|
||||
menu.addSeparator();
|
||||
menu.addItem (2, "Delete this exporter");
|
||||
|
||||
launchPopupMenu (menu);
|
||||
}
|
||||
|
||||
void handlePopupMenuResult (int resultCode) override
|
||||
{
|
||||
if (resultCode == 2)
|
||||
deleteAllSelectedItems();
|
||||
else if (resultCode == 1)
|
||||
exporter->addNewConfiguration (nullptr);
|
||||
}
|
||||
|
||||
var getDragSourceDescription() override
|
||||
{
|
||||
return getParentItem()->getUniqueName() + "/" + String (exporterIndex);
|
||||
}
|
||||
|
||||
bool isInterestedInDragSource (const DragAndDropTarget::SourceDetails& dragSourceDetails) override
|
||||
{
|
||||
return dragSourceDetails.description.toString().startsWith (getUniqueName());
|
||||
}
|
||||
|
||||
void itemDropped (const DragAndDropTarget::SourceDetails& dragSourceDetails, int insertIndex) override
|
||||
{
|
||||
const int oldIndex = indexOfConfig (dragSourceDetails.description.toString().fromLastOccurrenceOf ("||", false, false));
|
||||
|
||||
if (oldIndex >= 0)
|
||||
configListTree.moveChild (oldIndex, insertIndex, project.getUndoManagerFor (configListTree));
|
||||
}
|
||||
|
||||
int indexOfConfig (const String& configName)
|
||||
{
|
||||
int i = 0;
|
||||
for (ProjectExporter::ConfigIterator config (*exporter); config.next(); ++i)
|
||||
if (config->getName() == configName)
|
||||
return i;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void valueTreeChildAdded (ValueTree& parentTree, ValueTree&) override { refreshIfNeeded (parentTree); }
|
||||
void valueTreeChildRemoved (ValueTree& parentTree, ValueTree&) override { refreshIfNeeded (parentTree); }
|
||||
void valueTreeChildOrderChanged (ValueTree& parentTree) override { refreshIfNeeded (parentTree); }
|
||||
|
||||
void refreshIfNeeded (ValueTree& changedTree)
|
||||
{
|
||||
if (changedTree == configListTree)
|
||||
refreshSubItems();
|
||||
}
|
||||
|
||||
private:
|
||||
Project& project;
|
||||
ScopedPointer<ProjectExporter> exporter;
|
||||
ValueTree configListTree;
|
||||
int exporterIndex;
|
||||
|
||||
//==============================================================================
|
||||
class SettingsComp : public Component
|
||||
{
|
||||
public:
|
||||
SettingsComp (ProjectExporter* exp)
|
||||
{
|
||||
addAndMakeVisible (group);
|
||||
|
||||
PropertyListBuilder props;
|
||||
exp->createPropertyEditors (props);
|
||||
group.setProperties (props);
|
||||
group.setName ("Export target: " + exp->getName());
|
||||
parentSizeChanged();
|
||||
}
|
||||
|
||||
void parentSizeChanged() override { updateSize (*this, group); }
|
||||
|
||||
private:
|
||||
PropertyGroupComponent group;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (SettingsComp)
|
||||
};
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ExporterItem)
|
||||
};
|
||||
|
||||
|
||||
//==============================================================================
|
||||
class ConfigItem : public ConfigTreeItemBase
|
||||
{
|
||||
public:
|
||||
ConfigItem (const ProjectExporter::BuildConfiguration::Ptr& conf, const String& expName)
|
||||
: config (conf), exporterName (expName), configTree (config->config)
|
||||
{
|
||||
jassert (config != nullptr);
|
||||
configTree.addListener (this);
|
||||
}
|
||||
|
||||
bool isMissing() override { return false; }
|
||||
bool canBeSelected() const override { return true; }
|
||||
bool mightContainSubItems() override { return false; }
|
||||
String getUniqueName() const override { return "config_" + config->getName(); }
|
||||
String getRenamingName() const override { return getDisplayName(); }
|
||||
String getDisplayName() const override { return config->getName(); }
|
||||
void setName (const String&) override {}
|
||||
Icon getIcon() const override { return Icon (getIcons().config, getContrastingColour (Colours::green, 0.5f)); }
|
||||
|
||||
void showDocument() override { showSettingsPage (new SettingsComp (config, exporterName)); }
|
||||
void itemOpennessChanged (bool) override {}
|
||||
|
||||
void deleteItem() override
|
||||
{
|
||||
if (AlertWindow::showOkCancelBox (AlertWindow::WarningIcon, "Delete Configuration",
|
||||
"Are you sure you want to delete this configuration?"))
|
||||
{
|
||||
closeSettingsPage();
|
||||
config->removeFromExporter();
|
||||
}
|
||||
}
|
||||
|
||||
void showPopupMenu() override
|
||||
{
|
||||
PopupMenu menu;
|
||||
menu.addItem (1, "Create a copy of this configuration");
|
||||
menu.addSeparator();
|
||||
menu.addItem (2, "Delete this configuration");
|
||||
|
||||
launchPopupMenu (menu);
|
||||
}
|
||||
|
||||
void handlePopupMenuResult (int resultCode) override
|
||||
{
|
||||
if (resultCode == 2)
|
||||
{
|
||||
deleteAllSelectedItems();
|
||||
}
|
||||
else if (resultCode == 1)
|
||||
{
|
||||
for (Project::ExporterIterator exporter (config->project); exporter.next();)
|
||||
{
|
||||
if (config->config.isAChildOf (exporter->settings))
|
||||
{
|
||||
exporter->addNewConfiguration (config);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var getDragSourceDescription()
|
||||
{
|
||||
return getParentItem()->getUniqueName() + "||" + config->getName();
|
||||
}
|
||||
|
||||
void valueTreePropertyChanged (ValueTree&, const Identifier&) override { repaintItem(); }
|
||||
|
||||
private:
|
||||
ProjectExporter::BuildConfiguration::Ptr config;
|
||||
String exporterName;
|
||||
ValueTree configTree;
|
||||
|
||||
//==============================================================================
|
||||
class SettingsComp : public Component
|
||||
{
|
||||
public:
|
||||
SettingsComp (ProjectExporter::BuildConfiguration* conf, const String& expName)
|
||||
{
|
||||
addAndMakeVisible (group);
|
||||
|
||||
PropertyListBuilder props;
|
||||
conf->createPropertyEditors (props);
|
||||
group.setProperties (props);
|
||||
group.setName (expName + " / " + conf->getName());
|
||||
parentSizeChanged();
|
||||
}
|
||||
|
||||
void parentSizeChanged() override { updateSize (*this, group); }
|
||||
|
||||
private:
|
||||
PropertyGroupComponent group;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (SettingsComp)
|
||||
};
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ConfigItem)
|
||||
};
|
||||
@@ -0,0 +1,384 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
|
||||
Permission is granted to use this software under the terms of either:
|
||||
a) the GPL v2 (or any later version)
|
||||
b) the Affero GPL v3
|
||||
|
||||
Details of these licenses can be found at: www.gnu.org/licenses
|
||||
|
||||
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.juce.com for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
|
||||
class ModuleItem : public ConfigTreeItemBase
|
||||
{
|
||||
public:
|
||||
ModuleItem (Project& p, const String& modID)
|
||||
: project (p), moduleID (modID)
|
||||
{
|
||||
}
|
||||
|
||||
bool canBeSelected() const override { return true; }
|
||||
bool mightContainSubItems() override { return false; }
|
||||
String getUniqueName() const override { return "module_" + moduleID; }
|
||||
String getDisplayName() const override { return moduleID; }
|
||||
String getRenamingName() const override { return getDisplayName(); }
|
||||
void setName (const String&) override {}
|
||||
bool isMissing() override { return hasMissingDependencies(); }
|
||||
Icon getIcon() const override { return Icon (getIcons().jigsaw, getContrastingColour (Colours::red, 0.5f)); }
|
||||
void showDocument() override { showSettingsPage (new ModuleSettingsPanel (project, moduleID)); }
|
||||
void deleteItem() override { project.getModules().removeModule (moduleID); }
|
||||
|
||||
void showPopupMenu() override
|
||||
{
|
||||
PopupMenu menu;
|
||||
menu.addItem (1, "Remove this module");
|
||||
launchPopupMenu (menu);
|
||||
}
|
||||
|
||||
void handlePopupMenuResult (int resultCode) override
|
||||
{
|
||||
if (resultCode == 1)
|
||||
deleteItem();
|
||||
}
|
||||
|
||||
Project& project;
|
||||
String moduleID;
|
||||
|
||||
private:
|
||||
bool hasMissingDependencies() const
|
||||
{
|
||||
return project.getModules().getExtraDependenciesNeeded (moduleID).size() > 0;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
class ModuleSettingsPanel : public Component
|
||||
{
|
||||
public:
|
||||
ModuleSettingsPanel (Project& p, const String& modID)
|
||||
: project (p), moduleID (modID)
|
||||
{
|
||||
addAndMakeVisible (group);
|
||||
group.setName ("Module: " + moduleID);
|
||||
refresh();
|
||||
}
|
||||
|
||||
void refresh()
|
||||
{
|
||||
setEnabled (project.getModules().isModuleEnabled (moduleID));
|
||||
|
||||
PropertyListBuilder props;
|
||||
|
||||
props.add (new ModuleInfoComponent (project, moduleID));
|
||||
|
||||
if (project.getModules().getExtraDependenciesNeeded (moduleID).size() > 0)
|
||||
props.add (new MissingDependenciesComponent (project, moduleID));
|
||||
|
||||
for (Project::ExporterIterator exporter (project); exporter.next();)
|
||||
props.add (new TextPropertyComponent (exporter->getPathForModuleValue (moduleID),
|
||||
"Path for " + exporter->getName().quoted(), 1024, false),
|
||||
"A path to the folder that contains the " + moduleID + " module when compiling the "
|
||||
+ exporter->getName().quoted() + " target. "
|
||||
"This can be an absolute path, or relative to the jucer project folder, but it "
|
||||
"must be valid on the filesystem of the target machine that will be performing this build.");
|
||||
|
||||
props.add (new BooleanPropertyComponent (project.getModules().shouldCopyModuleFilesLocally (moduleID),
|
||||
"Create local copy", "Copy the module into the project folder"),
|
||||
"If this is enabled, then a local copy of the entire module will be made inside your project (in the auto-generated JuceLibraryFiles folder), "
|
||||
"so that your project will be self-contained, and won't need to contain any references to files in other folders. "
|
||||
"This also means that you can check the module into your source-control system to make sure it is always in sync with your own code.");
|
||||
|
||||
props.add (new BooleanPropertyComponent (project.getModules().shouldShowAllModuleFilesInProject (moduleID),
|
||||
"Add source to project", "Make module files browsable in projects"),
|
||||
"If this is enabled, then the entire source tree from this module will be shown inside your project, "
|
||||
"making it easy to browse/edit the module's classes. If disabled, then only the minimum number of files "
|
||||
"required to compile it will appear inside your project.");
|
||||
|
||||
StringArray possibleValues;
|
||||
possibleValues.add ("(Use Default)");
|
||||
possibleValues.add ("Enabled");
|
||||
possibleValues.add ("Disabled");
|
||||
|
||||
Array<var> mappings;
|
||||
mappings.add (Project::configFlagDefault);
|
||||
mappings.add (Project::configFlagEnabled);
|
||||
mappings.add (Project::configFlagDisabled);
|
||||
|
||||
ModuleDescription info (project.getModules().getModuleInfo (moduleID));
|
||||
|
||||
if (info.isValid())
|
||||
{
|
||||
OwnedArray <Project::ConfigFlag> configFlags;
|
||||
LibraryModule (info).getConfigFlags (project, configFlags);
|
||||
|
||||
for (int i = 0; i < configFlags.size(); ++i)
|
||||
{
|
||||
ChoicePropertyComponent* c = new ChoicePropertyComponent (configFlags[i]->value,
|
||||
configFlags[i]->symbol,
|
||||
possibleValues, mappings);
|
||||
c->setTooltip (configFlags[i]->description);
|
||||
props.add (c);
|
||||
}
|
||||
}
|
||||
|
||||
group.setProperties (props);
|
||||
parentSizeChanged();
|
||||
}
|
||||
|
||||
void parentSizeChanged() override { updateSize (*this, group); }
|
||||
|
||||
private:
|
||||
PropertyGroupComponent group;
|
||||
Project& project;
|
||||
String moduleID;
|
||||
|
||||
//==============================================================================
|
||||
class ModuleInfoComponent : public PropertyComponent
|
||||
{
|
||||
public:
|
||||
ModuleInfoComponent (Project& p, const String& modID)
|
||||
: PropertyComponent ("Module", 150), project (p), moduleID (modID)
|
||||
{
|
||||
}
|
||||
|
||||
void refresh() {}
|
||||
|
||||
void paint (Graphics& g)
|
||||
{
|
||||
g.setColour (Colours::white.withAlpha (0.4f));
|
||||
g.fillRect (0, 0, getWidth(), getHeight() - 1);
|
||||
|
||||
AttributedString s;
|
||||
s.setJustification (Justification::topLeft);
|
||||
|
||||
Font f (14.0f);
|
||||
|
||||
ModuleDescription info (project.getModules().getModuleInfo (moduleID));
|
||||
|
||||
if (info.isValid())
|
||||
{
|
||||
s.append (info.getName() + "\n\n", f.boldened());
|
||||
s.append ("Version: " + info.getVersion()
|
||||
+ "\nLicense: " + info.getLicense() + "\n", f.italicised());
|
||||
s.append ("\n" + info.getDescription(), f);
|
||||
}
|
||||
else
|
||||
{
|
||||
s.append ("Cannot find this module at the specified path!", f.boldened());
|
||||
s.setColour (Colours::darkred);
|
||||
}
|
||||
|
||||
s.draw (g, getLocalBounds().reduced (6, 5).toFloat());
|
||||
}
|
||||
|
||||
private:
|
||||
Project& project;
|
||||
String moduleID;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ModuleInfoComponent)
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
class MissingDependenciesComponent : public PropertyComponent,
|
||||
public ButtonListener
|
||||
{
|
||||
public:
|
||||
MissingDependenciesComponent (Project& p, const String& modID)
|
||||
: PropertyComponent ("Dependencies", 100),
|
||||
project (p), moduleID (modID),
|
||||
missingDependencies (project.getModules().getExtraDependenciesNeeded (modID)),
|
||||
fixButton ("Add Required Modules")
|
||||
{
|
||||
addAndMakeVisible (fixButton);
|
||||
fixButton.setColour (TextButton::buttonColourId, Colours::red);
|
||||
fixButton.setColour (TextButton::textColourOffId, Colours::white);
|
||||
fixButton.addListener (this);
|
||||
}
|
||||
|
||||
void refresh() {}
|
||||
|
||||
void paint (Graphics& g)
|
||||
{
|
||||
g.setColour (Colours::white.withAlpha (0.4f));
|
||||
g.fillRect (0, 0, getWidth(), getHeight() - 1);
|
||||
|
||||
String text ("This module has missing dependencies!\n\n"
|
||||
"To build correctly, it requires the following modules to be added:\n");
|
||||
text << missingDependencies.joinIntoString (", ");
|
||||
|
||||
AttributedString s;
|
||||
s.setJustification (Justification::topLeft);
|
||||
s.append (text, Font (13.0f), Colours::red.darker());
|
||||
s.draw (g, getLocalBounds().reduced (4, 16).toFloat());
|
||||
}
|
||||
|
||||
void buttonClicked (Button*)
|
||||
{
|
||||
bool anyFailed = false;
|
||||
|
||||
ModuleList list;
|
||||
list.scanAllKnownFolders (project);
|
||||
|
||||
for (int i = missingDependencies.size(); --i >= 0;)
|
||||
{
|
||||
if (const ModuleDescription* info = list.getModuleWithID (missingDependencies[i]))
|
||||
project.getModules().addModule (info->manifestFile, project.getModules().areMostModulesCopiedLocally());
|
||||
else
|
||||
anyFailed = true;
|
||||
}
|
||||
|
||||
if (ModuleSettingsPanel* p = findParentComponentOfClass<ModuleSettingsPanel>())
|
||||
p->refresh();
|
||||
|
||||
if (anyFailed)
|
||||
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.");
|
||||
}
|
||||
|
||||
void resized()
|
||||
{
|
||||
fixButton.setBounds (getWidth() - 168, getHeight() - 26, 160, 22);
|
||||
}
|
||||
|
||||
private:
|
||||
Project& project;
|
||||
String moduleID;
|
||||
StringArray missingDependencies;
|
||||
TextButton fixButton;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MissingDependenciesComponent)
|
||||
};
|
||||
};
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ModuleItem)
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
class EnabledModulesItem : public ConfigTreeItemBase
|
||||
{
|
||||
public:
|
||||
EnabledModulesItem (Project& p)
|
||||
: project (p),
|
||||
moduleListTree (p.getModules().state)
|
||||
{
|
||||
moduleListTree.addListener (this);
|
||||
}
|
||||
|
||||
bool isModulesList() const override { return true; }
|
||||
bool canBeSelected() const override { return true; }
|
||||
bool mightContainSubItems() override { return true; }
|
||||
String getUniqueName() const override { return "modules"; }
|
||||
String getRenamingName() const override { return getDisplayName(); }
|
||||
String getDisplayName() const override { return "Modules"; }
|
||||
void setName (const String&) override {}
|
||||
bool isMissing() override { return false; }
|
||||
Icon getIcon() const override { return Icon (getIcons().graph, getContrastingColour (Colours::red, 0.5f)); }
|
||||
|
||||
void showDocument()
|
||||
{
|
||||
if (ProjectContentComponent* pcc = getProjectContentComponent())
|
||||
pcc->setEditorComponent (new ModulesPanel (project), nullptr);
|
||||
}
|
||||
|
||||
static File getManifestFile (const File& draggedFile)
|
||||
{
|
||||
if (draggedFile.getFileName() == ModuleDescription::getManifestFileName())
|
||||
return draggedFile;
|
||||
|
||||
return draggedFile.getChildFile (ModuleDescription::getManifestFileName());
|
||||
}
|
||||
|
||||
bool isInterestedInFileDrag (const StringArray& files) override
|
||||
{
|
||||
for (int i = files.size(); --i >= 0;)
|
||||
if (ModuleDescription (getManifestFile (files[i])).isValid())
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void filesDropped (const StringArray& files, int /*insertIndex*/) override
|
||||
{
|
||||
Array<ModuleDescription> modules;
|
||||
|
||||
for (int i = files.size(); --i >= 0;)
|
||||
{
|
||||
ModuleDescription m (getManifestFile (files[i]));
|
||||
|
||||
if (m.isValid())
|
||||
modules.add (m);
|
||||
}
|
||||
|
||||
for (int i = 0; i < modules.size(); ++i)
|
||||
project.getModules().addModule (modules.getReference(i).manifestFile,
|
||||
project.getModules().areMostModulesCopiedLocally());
|
||||
}
|
||||
|
||||
void addSubItems() override
|
||||
{
|
||||
for (int i = 0; i < project.getModules().getNumModules(); ++i)
|
||||
addSubItem (new ModuleItem (project, project.getModules().getModuleID (i)));
|
||||
}
|
||||
|
||||
void showPopupMenu() override
|
||||
{
|
||||
PopupMenu menu, knownModules, copyModeMenu;
|
||||
|
||||
const StringArray modules (getAvailableModules());
|
||||
for (int i = 0; i < modules.size(); ++i)
|
||||
knownModules.addItem (1 + i, modules[i], ! project.getModules().isModuleEnabled (modules[i]));
|
||||
|
||||
menu.addSubMenu ("Add a module", knownModules);
|
||||
menu.addSeparator();
|
||||
menu.addItem (1001, "Add a module from a specified folder...");
|
||||
|
||||
launchPopupMenu (menu);
|
||||
}
|
||||
|
||||
void handlePopupMenuResult (int resultCode) override
|
||||
{
|
||||
if (resultCode == 1001)
|
||||
project.getModules().addModuleFromUserSelectedFile();
|
||||
else if (resultCode > 0)
|
||||
project.getModules().addModuleInteractive (getAvailableModules() [resultCode - 1]);
|
||||
}
|
||||
|
||||
StringArray getAvailableModules()
|
||||
{
|
||||
ModuleList list;
|
||||
list.scanAllKnownFolders (project);
|
||||
return list.getIDs();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void valueTreeChildAdded (ValueTree& parentTree, ValueTree&) override { refreshIfNeeded (parentTree); }
|
||||
void valueTreeChildRemoved (ValueTree& parentTree, ValueTree&) override { refreshIfNeeded (parentTree); }
|
||||
void valueTreeChildOrderChanged (ValueTree& parentTree) override { refreshIfNeeded (parentTree); }
|
||||
|
||||
void refreshIfNeeded (ValueTree& changedTree)
|
||||
{
|
||||
if (changedTree == moduleListTree)
|
||||
refreshSubItems();
|
||||
}
|
||||
|
||||
private:
|
||||
Project& project;
|
||||
ValueTree moduleListTree;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (EnabledModulesItem)
|
||||
};
|
||||
@@ -0,0 +1,167 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
|
||||
Permission is granted to use this software under the terms of either:
|
||||
a) the GPL v2 (or any later version)
|
||||
b) the Affero GPL v3
|
||||
|
||||
Details of these licenses can be found at: www.gnu.org/licenses
|
||||
|
||||
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.juce.com for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#ifndef __JUCER_GROUPINFORMATIONCOMPONENT_JUCEHEADER__
|
||||
#define __JUCER_GROUPINFORMATIONCOMPONENT_JUCEHEADER__
|
||||
|
||||
#include "../jucer_Headers.h"
|
||||
#include "../Project/jucer_Project.h"
|
||||
|
||||
|
||||
//==============================================================================
|
||||
class GroupInformationComponent : public Component,
|
||||
private ListBoxModel,
|
||||
private ValueTree::Listener
|
||||
{
|
||||
public:
|
||||
GroupInformationComponent (const Project::Item& group)
|
||||
: item (group)
|
||||
{
|
||||
list.setModel (this);
|
||||
list.setColour (ListBox::backgroundColourId, Colours::transparentBlack);
|
||||
addAndMakeVisible (list);
|
||||
list.updateContent();
|
||||
list.setRowHeight (20);
|
||||
item.state.addListener (this);
|
||||
lookAndFeelChanged();
|
||||
}
|
||||
|
||||
~GroupInformationComponent()
|
||||
{
|
||||
item.state.removeListener (this);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void paint (Graphics& g) override
|
||||
{
|
||||
IntrojucerLookAndFeel::fillWithBackgroundTexture (*this, g);
|
||||
}
|
||||
|
||||
void resized() override
|
||||
{
|
||||
list.setBounds (getLocalBounds().reduced (5, 4));
|
||||
}
|
||||
|
||||
int getNumRows() override
|
||||
{
|
||||
return item.getNumChildren();
|
||||
}
|
||||
|
||||
void paintListBoxItem (int /*rowNumber*/, Graphics& g, int width, int height, bool /*rowIsSelected*/) override
|
||||
{
|
||||
g.setColour (Colours::white.withAlpha (0.4f));
|
||||
g.fillRect (0, 0, width, height - 1);
|
||||
}
|
||||
|
||||
Component* refreshComponentForRow (int rowNumber, bool /*isRowSelected*/, Component* existingComponentToUpdate) override
|
||||
{
|
||||
ScopedPointer<Component> existing (existingComponentToUpdate);
|
||||
|
||||
if (rowNumber < getNumRows())
|
||||
{
|
||||
Project::Item child (item.getChild (rowNumber));
|
||||
|
||||
if (existingComponentToUpdate == nullptr
|
||||
|| dynamic_cast <FileOptionComponent*> (existing.get())->item != child)
|
||||
{
|
||||
existing = nullptr;
|
||||
existing = new FileOptionComponent (child);
|
||||
}
|
||||
}
|
||||
|
||||
return existing.release();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void valueTreePropertyChanged (ValueTree&, const Identifier&) override { itemChanged(); }
|
||||
void valueTreeChildAdded (ValueTree&, ValueTree&) override { itemChanged(); }
|
||||
void valueTreeChildRemoved (ValueTree&, ValueTree&) override { itemChanged(); }
|
||||
void valueTreeChildOrderChanged (ValueTree&) override { itemChanged(); }
|
||||
void valueTreeParentChanged (ValueTree&) override { itemChanged(); }
|
||||
|
||||
private:
|
||||
Project::Item item;
|
||||
ListBox list;
|
||||
|
||||
void itemChanged()
|
||||
{
|
||||
list.updateContent();
|
||||
repaint();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
class FileOptionComponent : public Component
|
||||
{
|
||||
public:
|
||||
FileOptionComponent (const Project::Item& fileItem)
|
||||
: item (fileItem),
|
||||
compileButton ("Compile"),
|
||||
resourceButton ("Add to Binary Resources")
|
||||
{
|
||||
if (item.isFile())
|
||||
{
|
||||
addAndMakeVisible (compileButton);
|
||||
compileButton.getToggleStateValue().referTo (item.getShouldCompileValue());
|
||||
|
||||
addAndMakeVisible (resourceButton);
|
||||
resourceButton.getToggleStateValue().referTo (item.getShouldAddToResourceValue());
|
||||
}
|
||||
}
|
||||
|
||||
void paint (Graphics& g) override
|
||||
{
|
||||
int x = getHeight() + 6;
|
||||
|
||||
item.getIcon().withContrastingColourTo (Colours::grey)
|
||||
.draw (g, Rectangle<float> (3.0f, 2.0f, x - 6.0f, getHeight() - 4.0f),
|
||||
item.isIconCrossedOut());
|
||||
|
||||
g.setColour (Colours::black);
|
||||
g.setFont (getHeight() * 0.6f);
|
||||
|
||||
const int x2 = compileButton.isVisible() ? compileButton.getX() - 4
|
||||
: getWidth() - 4;
|
||||
|
||||
g.drawText (item.getName(), x, 0, x2 - x, getHeight(), Justification::centredLeft, true);
|
||||
}
|
||||
|
||||
void resized() override
|
||||
{
|
||||
int w = 180;
|
||||
resourceButton.setBounds (getWidth() - w, 1, w, getHeight() - 2);
|
||||
w = 100;
|
||||
compileButton.setBounds (resourceButton.getX() - w, 1, w, getHeight() - 2);
|
||||
}
|
||||
|
||||
Project::Item item;
|
||||
|
||||
private:
|
||||
ToggleButton compileButton, resourceButton;
|
||||
};
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (GroupInformationComponent)
|
||||
};
|
||||
|
||||
|
||||
#endif // __JUCER_GROUPINFORMATIONCOMPONENT_JUCEHEADER__
|
||||
@@ -0,0 +1,907 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
|
||||
Permission is granted to use this software under the terms of either:
|
||||
a) the GPL v2 (or any later version)
|
||||
b) the Affero GPL v3
|
||||
|
||||
Details of these licenses can be found at: www.gnu.org/licenses
|
||||
|
||||
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.juce.com for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#include "jucer_Module.h"
|
||||
#include "jucer_ProjectType.h"
|
||||
#include "../Project Saving/jucer_ProjectExporter.h"
|
||||
#include "../Project Saving/jucer_ProjectSaver.h"
|
||||
#include "jucer_AudioPluginModule.h"
|
||||
|
||||
|
||||
ModuleDescription::ModuleDescription (const File& manifest)
|
||||
: moduleInfo (JSON::parse (manifest)), manifestFile (manifest)
|
||||
{
|
||||
if (moduleInfo.isVoid() && manifestFile.exists())
|
||||
{
|
||||
var json;
|
||||
Result r (JSON::parse (manifestFile.loadFileAsString(), json));
|
||||
|
||||
if (r.failed() && manifestFile.loadFileAsString().isNotEmpty())
|
||||
{
|
||||
DBG (r.getErrorMessage());
|
||||
jassertfalse; // broken JSON in a module manifest.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
ModuleList::ModuleList()
|
||||
{
|
||||
}
|
||||
|
||||
ModuleList::ModuleList (const ModuleList& other)
|
||||
{
|
||||
operator= (other);
|
||||
}
|
||||
|
||||
ModuleList& ModuleList::operator= (const ModuleList& other)
|
||||
{
|
||||
modules.clear();
|
||||
modules.addCopiesOf (other.modules);
|
||||
return *this;
|
||||
}
|
||||
|
||||
const ModuleDescription* ModuleList::getModuleWithID (const String& moduleID) const
|
||||
{
|
||||
for (int i = 0; i < modules.size(); ++i)
|
||||
{
|
||||
ModuleDescription* m = modules.getUnchecked(i);
|
||||
if (m->getID() == moduleID)
|
||||
return m;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
struct ModuleSorter
|
||||
{
|
||||
static int compareElements (const ModuleDescription* m1, const ModuleDescription* m2)
|
||||
{
|
||||
return m1->getID().compareIgnoreCase (m2->getID());
|
||||
}
|
||||
};
|
||||
|
||||
void ModuleList::sort()
|
||||
{
|
||||
ModuleSorter sorter;
|
||||
modules.sort (sorter);
|
||||
}
|
||||
|
||||
StringArray ModuleList::getIDs() const
|
||||
{
|
||||
StringArray results;
|
||||
|
||||
for (int i = 0; i < modules.size(); ++i)
|
||||
results.add (modules.getUnchecked(i)->getID());
|
||||
|
||||
results.sort (true);
|
||||
return results;
|
||||
}
|
||||
|
||||
Result ModuleList::addAllModulesInFolder (const File& path)
|
||||
{
|
||||
const File moduleDef (path.getChildFile (ModuleDescription::getManifestFileName()));
|
||||
|
||||
if (moduleDef.exists())
|
||||
{
|
||||
ModuleDescription m (moduleDef);
|
||||
|
||||
if (! m.isValid())
|
||||
return Result::fail ("Failed to load module manifest: " + moduleDef.getFullPathName());
|
||||
|
||||
modules.add (new ModuleDescription (m));
|
||||
}
|
||||
else
|
||||
{
|
||||
for (DirectoryIterator iter (path, false, "*", File::findDirectories); iter.next();)
|
||||
{
|
||||
Result r = addAllModulesInFolder (iter.getFile().getLinkedTarget());
|
||||
if (r.failed())
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
return Result::ok();
|
||||
}
|
||||
|
||||
static Array<File> getAllPossibleModulePaths (Project& project)
|
||||
{
|
||||
StringArray paths;
|
||||
|
||||
for (Project::ExporterIterator exporter (project); exporter.next();)
|
||||
{
|
||||
for (int i = 0; i < project.getModules().getNumModules(); ++i)
|
||||
{
|
||||
const String path (exporter->getPathForModuleString (project.getModules().getModuleID (i)));
|
||||
|
||||
if (path.isNotEmpty())
|
||||
paths.addIfNotAlreadyThere (path);
|
||||
}
|
||||
|
||||
String oldPath (exporter->getLegacyModulePath());
|
||||
|
||||
if (oldPath.isNotEmpty())
|
||||
paths.addIfNotAlreadyThere (oldPath);
|
||||
}
|
||||
|
||||
Array<File> files;
|
||||
|
||||
for (int i = 0; i < paths.size(); ++i)
|
||||
{
|
||||
const File f (project.resolveFilename (paths[i]));
|
||||
|
||||
if (f.isDirectory())
|
||||
{
|
||||
files.add (f);
|
||||
|
||||
if (f.getChildFile ("modules").isDirectory())
|
||||
files.addIfNotAlreadyThere (f.getChildFile ("modules"));
|
||||
}
|
||||
}
|
||||
|
||||
return files;
|
||||
}
|
||||
|
||||
Result ModuleList::scanAllKnownFolders (Project& project)
|
||||
{
|
||||
modules.clear();
|
||||
Result result (Result::ok());
|
||||
|
||||
const Array<File> modulePaths (getAllPossibleModulePaths (project));
|
||||
|
||||
for (int i = 0; i < modulePaths.size(); ++i)
|
||||
{
|
||||
result = addAllModulesInFolder (modulePaths.getReference(i));
|
||||
|
||||
if (result.failed())
|
||||
break;
|
||||
}
|
||||
|
||||
sort();
|
||||
return result;
|
||||
}
|
||||
|
||||
bool ModuleList::loadFromWebsite()
|
||||
{
|
||||
modules.clear();
|
||||
|
||||
URL baseURL ("http://www.juce.com/juce/modules");
|
||||
URL url (baseURL.getChildURL ("modulelist.php"));
|
||||
|
||||
const ScopedPointer<InputStream> in (url.createInputStream (false, nullptr, nullptr, String::empty, 4000));
|
||||
|
||||
if (in == nullptr)
|
||||
return false;
|
||||
|
||||
var infoList (JSON::parse (in->readEntireStreamAsString()));
|
||||
|
||||
if (! infoList.isArray())
|
||||
return false;
|
||||
|
||||
const Array<var>* moduleList = infoList.getArray();
|
||||
|
||||
for (int i = 0; i < moduleList->size(); ++i)
|
||||
{
|
||||
const var& m = moduleList->getReference(i);
|
||||
const String file (m [Ids::file].toString());
|
||||
|
||||
if (file.isNotEmpty())
|
||||
{
|
||||
ModuleDescription lm (m [Ids::info]);
|
||||
|
||||
if (lm.isValid())
|
||||
{
|
||||
lm.url = baseURL.getChildURL (file);
|
||||
modules.add (new ModuleDescription (lm));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sort();
|
||||
return true;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
LibraryModule::LibraryModule (const ModuleDescription& d)
|
||||
: moduleInfo (d)
|
||||
{
|
||||
}
|
||||
|
||||
bool LibraryModule::isAUPluginHost (const Project& project) const { return getID() == "juce_audio_processors" && project.isConfigFlagEnabled ("JUCE_PLUGINHOST_AU"); }
|
||||
bool LibraryModule::isVSTPluginHost (const Project& project) const { return getID() == "juce_audio_processors" && project.isConfigFlagEnabled ("JUCE_PLUGINHOST_VST"); }
|
||||
bool LibraryModule::isVST3PluginHost (const Project& project) const { return getID() == "juce_audio_processors" && project.isConfigFlagEnabled ("JUCE_PLUGINHOST_VST3"); }
|
||||
|
||||
File LibraryModule::getModuleHeaderFile (const File& folder) const
|
||||
{
|
||||
return folder.getChildFile (moduleInfo.getHeaderName());
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void LibraryModule::writeIncludes (ProjectSaver& projectSaver, OutputStream& out)
|
||||
{
|
||||
const File localModuleFolder (projectSaver.getLocalModuleFolder (getID()));
|
||||
const File localHeader (getModuleHeaderFile (localModuleFolder));
|
||||
|
||||
localModuleFolder.createDirectory();
|
||||
|
||||
if (projectSaver.project.getModules().shouldCopyModuleFilesLocally (getID()).getValue())
|
||||
{
|
||||
projectSaver.copyFolder (moduleInfo.getFolder(), localModuleFolder);
|
||||
}
|
||||
else
|
||||
{
|
||||
localModuleFolder.createDirectory();
|
||||
createLocalHeaderWrapper (projectSaver, getModuleHeaderFile (moduleInfo.getFolder()), localHeader);
|
||||
}
|
||||
|
||||
out << CodeHelpers::createIncludeStatement (localHeader, projectSaver.getGeneratedCodeFolder()
|
||||
.getChildFile ("AppConfig.h")) << newLine;
|
||||
}
|
||||
|
||||
static void writeGuardedInclude (OutputStream& out, StringArray paths, StringArray guards)
|
||||
{
|
||||
StringArray uniquePaths (paths);
|
||||
uniquePaths.removeDuplicates (false);
|
||||
|
||||
if (uniquePaths.size() == 1)
|
||||
{
|
||||
out << "#include " << paths[0] << newLine;
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = paths.size(); --i >= 0;)
|
||||
{
|
||||
for (int j = i; --j >= 0;)
|
||||
{
|
||||
if (paths[i] == paths[j] && guards[i] == guards[j])
|
||||
{
|
||||
paths.remove (i);
|
||||
guards.remove (i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < paths.size(); ++i)
|
||||
{
|
||||
out << (i == 0 ? "#if " : "#elif ") << guards[i] << newLine
|
||||
<< " #include " << paths[i] << newLine;
|
||||
}
|
||||
|
||||
out << "#else" << newLine
|
||||
<< " #error \"This file is designed to be used in an Introjucer-generated project!\"" << newLine
|
||||
<< "#endif" << newLine;
|
||||
}
|
||||
}
|
||||
|
||||
void LibraryModule::createLocalHeaderWrapper (ProjectSaver& projectSaver, const File& originalHeader, const File& localHeader) const
|
||||
{
|
||||
Project& project = projectSaver.project;
|
||||
|
||||
MemoryOutputStream out;
|
||||
|
||||
out << "// This is an auto-generated file to redirect any included" << newLine
|
||||
<< "// module headers to the correct external folder." << newLine
|
||||
<< newLine;
|
||||
|
||||
StringArray paths, guards;
|
||||
|
||||
for (Project::ExporterIterator exporter (project); exporter.next();)
|
||||
{
|
||||
const RelativePath headerFromProject (exporter->getModuleFolderRelativeToProject (getID(), projectSaver)
|
||||
.getChildFile (originalHeader.getFileName()));
|
||||
|
||||
const RelativePath fileFromHere (headerFromProject.rebased (project.getProjectFolder(),
|
||||
localHeader.getParentDirectory(), RelativePath::unknown));
|
||||
|
||||
paths.add (fileFromHere.toUnixStyle().quoted());
|
||||
guards.add ("defined (" + exporter->getExporterIdentifierMacro() + ")");
|
||||
}
|
||||
|
||||
writeGuardedInclude (out, paths, guards);
|
||||
out << newLine;
|
||||
|
||||
projectSaver.replaceFileIfDifferent (localHeader, out);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void LibraryModule::prepareExporter (ProjectExporter& exporter, ProjectSaver& projectSaver) const
|
||||
{
|
||||
Project& project = exporter.getProject();
|
||||
|
||||
exporter.addToExtraSearchPaths (exporter.getModuleFolderRelativeToProject (getID(), projectSaver).getParentDirectory());
|
||||
|
||||
const String extraDefs (moduleInfo.getPreprocessorDefs().trim());
|
||||
|
||||
if (extraDefs.isNotEmpty())
|
||||
exporter.getExporterPreprocessorDefs() = exporter.getExporterPreprocessorDefsString() + "\n" + extraDefs;
|
||||
|
||||
{
|
||||
Array<File> compiled;
|
||||
|
||||
const File localModuleFolder = project.getModules().shouldCopyModuleFilesLocally (getID()).getValue()
|
||||
? projectSaver.getLocalModuleFolder (getID())
|
||||
: moduleInfo.getFolder();
|
||||
|
||||
findAndAddCompiledCode (exporter, projectSaver, localModuleFolder, compiled);
|
||||
|
||||
if (project.getModules().shouldShowAllModuleFilesInProject (getID()).getValue())
|
||||
addBrowsableCode (exporter, projectSaver, compiled, localModuleFolder);
|
||||
}
|
||||
|
||||
if (isVSTPluginHost (project)) VSTHelpers::addVSTFolderToPath (exporter, false);
|
||||
if (isVST3PluginHost (project)) VSTHelpers::addVSTFolderToPath (exporter, true);
|
||||
|
||||
if (exporter.isXcode())
|
||||
{
|
||||
if (isAUPluginHost (project))
|
||||
exporter.xcodeFrameworks.addTokens ("AudioUnit CoreAudioKit", false);
|
||||
|
||||
const String frameworks (moduleInfo.moduleInfo [exporter.isOSX() ? "OSXFrameworks" : "iOSFrameworks"].toString());
|
||||
exporter.xcodeFrameworks.addTokens (frameworks, ", ", StringRef());
|
||||
}
|
||||
else if (exporter.isLinux())
|
||||
{
|
||||
const String libs (moduleInfo.moduleInfo ["LinuxLibs"].toString());
|
||||
exporter.linuxLibs.addTokens (libs, ", ", StringRef());
|
||||
exporter.linuxLibs.trim();
|
||||
exporter.linuxLibs.sort (false);
|
||||
exporter.linuxLibs.removeDuplicates (false);
|
||||
}
|
||||
else if (exporter.isCodeBlocks())
|
||||
{
|
||||
const String libs (moduleInfo.moduleInfo ["mingwLibs"].toString());
|
||||
exporter.mingwLibs.addTokens (libs, ", ", StringRef());
|
||||
exporter.mingwLibs.trim();
|
||||
exporter.mingwLibs.sort (false);
|
||||
exporter.mingwLibs.removeDuplicates (false);
|
||||
}
|
||||
|
||||
if (moduleInfo.isPluginClient())
|
||||
{
|
||||
if (shouldBuildVST (project).getValue()) VSTHelpers::prepareExporter (exporter, projectSaver, false);
|
||||
if (shouldBuildVST3 (project).getValue()) VSTHelpers::prepareExporter (exporter, projectSaver, true);
|
||||
if (shouldBuildAU (project).getValue()) AUHelpers::prepareExporter (exporter, projectSaver);
|
||||
if (shouldBuildAAX (project).getValue()) AAXHelpers::prepareExporter (exporter, projectSaver);
|
||||
if (shouldBuildRTAS (project).getValue()) RTASHelpers::prepareExporter (exporter, projectSaver);
|
||||
}
|
||||
}
|
||||
|
||||
void LibraryModule::createPropertyEditors (ProjectExporter& exporter, PropertyListBuilder& props) const
|
||||
{
|
||||
if (isVSTPluginHost (exporter.getProject())
|
||||
&& ! (moduleInfo.isPluginClient() && shouldBuildVST (exporter.getProject()).getValue()))
|
||||
VSTHelpers::createVSTPathEditor (exporter, props, false);
|
||||
|
||||
if (isVST3PluginHost (exporter.getProject())
|
||||
&& ! (moduleInfo.isPluginClient() && shouldBuildVST3 (exporter.getProject()).getValue()))
|
||||
VSTHelpers::createVSTPathEditor (exporter, props, true);
|
||||
|
||||
if (moduleInfo.isPluginClient())
|
||||
{
|
||||
if (shouldBuildVST (exporter.getProject()).getValue()) VSTHelpers::createPropertyEditors (exporter, props, false);
|
||||
if (shouldBuildVST3 (exporter.getProject()).getValue()) VSTHelpers::createPropertyEditors (exporter, props, true);
|
||||
if (shouldBuildRTAS (exporter.getProject()).getValue()) RTASHelpers::createPropertyEditors (exporter, props);
|
||||
if (shouldBuildAAX (exporter.getProject()).getValue()) AAXHelpers::createPropertyEditors (exporter, props);
|
||||
}
|
||||
}
|
||||
|
||||
void LibraryModule::getConfigFlags (Project& project, OwnedArray<Project::ConfigFlag>& flags) const
|
||||
{
|
||||
const File header (getModuleHeaderFile (moduleInfo.getFolder()));
|
||||
jassert (header.exists());
|
||||
|
||||
StringArray lines;
|
||||
header.readLines (lines);
|
||||
|
||||
for (int i = 0; i < lines.size(); ++i)
|
||||
{
|
||||
String line (lines[i].trim());
|
||||
|
||||
if (line.startsWith ("/**") && line.containsIgnoreCase ("Config:"))
|
||||
{
|
||||
ScopedPointer <Project::ConfigFlag> config (new Project::ConfigFlag());
|
||||
config->sourceModuleID = getID();
|
||||
config->symbol = line.fromFirstOccurrenceOf (":", false, false).trim();
|
||||
|
||||
if (config->symbol.length() > 2)
|
||||
{
|
||||
++i;
|
||||
|
||||
while (! (lines[i].contains ("*/") || lines[i].contains ("@see")))
|
||||
{
|
||||
if (lines[i].trim().isNotEmpty())
|
||||
config->description = config->description.trim() + " " + lines[i].trim();
|
||||
|
||||
++i;
|
||||
}
|
||||
|
||||
config->description = config->description.upToFirstOccurrenceOf ("*/", false, false);
|
||||
config->value.referTo (project.getConfigFlag (config->symbol));
|
||||
flags.add (config.release());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
static bool exporterTargetMatches (const String& test, String target)
|
||||
{
|
||||
StringArray validTargets;
|
||||
validTargets.addTokens (target, ",;", "");
|
||||
validTargets.trim();
|
||||
validTargets.removeEmptyStrings();
|
||||
|
||||
if (validTargets.size() == 0)
|
||||
return true;
|
||||
|
||||
for (int i = validTargets.size(); --i >= 0;)
|
||||
{
|
||||
const String& targetName = validTargets[i];
|
||||
|
||||
if (targetName == test
|
||||
|| (targetName.startsWithChar ('!') && test != targetName.substring (1).trimStart()))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
struct FileSorter
|
||||
{
|
||||
static int compareElements (const File& f1, const File& f2)
|
||||
{
|
||||
return f1.getFileName().compareNatural (f2.getFileName());
|
||||
}
|
||||
};
|
||||
|
||||
void LibraryModule::findWildcardMatches (const File& localModuleFolder, const String& wildcardPath, Array<File>& result) const
|
||||
{
|
||||
String path (wildcardPath.upToLastOccurrenceOf ("/", false, false));
|
||||
String wildCard (wildcardPath.fromLastOccurrenceOf ("/", false, false));
|
||||
|
||||
Array<File> tempList;
|
||||
FileSorter sorter;
|
||||
|
||||
DirectoryIterator iter (localModuleFolder.getChildFile (path), false, wildCard);
|
||||
bool isHiddenFile;
|
||||
|
||||
while (iter.next (nullptr, &isHiddenFile, nullptr, nullptr, nullptr, nullptr))
|
||||
if (! isHiddenFile)
|
||||
tempList.addSorted (sorter, iter.getFile());
|
||||
|
||||
result.addArray (tempList);
|
||||
}
|
||||
|
||||
static bool fileTargetMatches (ProjectExporter& exporter, const String& target)
|
||||
{
|
||||
if (exporter.isXcode()) return exporterTargetMatches ("xcode", target);
|
||||
if (exporter.isWindows()) return exporterTargetMatches ("msvc", target);
|
||||
if (exporter.isLinux()) return exporterTargetMatches ("linux", target);
|
||||
if (exporter.isAndroid()) return exporterTargetMatches ("android", target);
|
||||
if (exporter.isCodeBlocks()) return exporterTargetMatches ("mingw", target);
|
||||
return target.isEmpty();
|
||||
}
|
||||
|
||||
static bool fileShouldBeAdded (ProjectExporter& exporter, const var& properties)
|
||||
{
|
||||
if (! fileTargetMatches (exporter, properties["target"].toString()))
|
||||
return false;
|
||||
|
||||
if (properties["RTASOnly"] && ! shouldBuildRTAS (exporter.getProject()).getValue())
|
||||
return false;
|
||||
|
||||
if (properties["AudioUnitOnly"] && ! shouldBuildAU (exporter.getProject()).getValue())
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void LibraryModule::findAndAddCompiledCode (ProjectExporter& exporter, ProjectSaver& projectSaver,
|
||||
const File& localModuleFolder, Array<File>& result) const
|
||||
{
|
||||
const var compileArray (moduleInfo.moduleInfo ["compile"]); // careful to keep this alive while the array is in use!
|
||||
|
||||
if (const Array<var>* const files = compileArray.getArray())
|
||||
{
|
||||
for (int i = 0; i < files->size(); ++i)
|
||||
{
|
||||
const var& file = files->getReference(i);
|
||||
const String filename (file ["file"].toString());
|
||||
|
||||
if (filename.isNotEmpty() && fileShouldBeAdded (exporter, file))
|
||||
{
|
||||
const File compiledFile (localModuleFolder.getChildFile (filename));
|
||||
result.add (compiledFile);
|
||||
|
||||
Project::Item item (projectSaver.addFileToGeneratedGroup (compiledFile));
|
||||
|
||||
if (file ["warnings"].toString().equalsIgnoreCase ("disabled"))
|
||||
item.getShouldInhibitWarningsValue() = true;
|
||||
|
||||
if (file ["stdcall"])
|
||||
item.getShouldUseStdCallValue() = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void addFileWithGroups (Project::Item& group, const RelativePath& file, const String& path)
|
||||
{
|
||||
const int slash = path.indexOfChar (File::separator);
|
||||
|
||||
if (slash >= 0)
|
||||
{
|
||||
const String topLevelGroup (path.substring (0, slash));
|
||||
const String remainingPath (path.substring (slash + 1));
|
||||
|
||||
Project::Item newGroup (group.getOrCreateSubGroup (topLevelGroup));
|
||||
addFileWithGroups (newGroup, file, remainingPath);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (! group.containsChildForFile (file))
|
||||
group.addRelativeFile (file, -1, false);
|
||||
}
|
||||
}
|
||||
|
||||
void LibraryModule::findBrowseableFiles (const File& localModuleFolder, Array<File>& filesFound) const
|
||||
{
|
||||
const var filesArray (moduleInfo.moduleInfo ["browse"]);
|
||||
|
||||
if (const Array<var>* const files = filesArray.getArray())
|
||||
for (int i = 0; i < files->size(); ++i)
|
||||
findWildcardMatches (localModuleFolder, files->getReference(i), filesFound);
|
||||
}
|
||||
|
||||
void LibraryModule::addBrowsableCode (ProjectExporter& exporter, ProjectSaver& projectSaver,
|
||||
const Array<File>& compiled, const File& localModuleFolder) const
|
||||
{
|
||||
if (sourceFiles.size() == 0)
|
||||
findBrowseableFiles (localModuleFolder, sourceFiles);
|
||||
|
||||
Project::Item sourceGroup (Project::Item::createGroup (exporter.getProject(), getID(), "__mainsourcegroup" + getID()));
|
||||
|
||||
const RelativePath moduleFromProject (exporter.getModuleFolderRelativeToProject (getID(), projectSaver));
|
||||
|
||||
for (int i = 0; i < sourceFiles.size(); ++i)
|
||||
{
|
||||
const String pathWithinModule (FileHelpers::getRelativePathFrom (sourceFiles.getReference(i), localModuleFolder));
|
||||
|
||||
// (Note: in exporters like MSVC we have to avoid adding the same file twice, even if one of those instances
|
||||
// is flagged as being excluded from the build, because this overrides the other and it fails to compile)
|
||||
if (exporter.canCopeWithDuplicateFiles() || ! compiled.contains (sourceFiles.getReference(i)))
|
||||
addFileWithGroups (sourceGroup,
|
||||
moduleFromProject.getChildFile (pathWithinModule),
|
||||
pathWithinModule);
|
||||
}
|
||||
|
||||
sourceGroup.addFile (localModuleFolder.getChildFile (FileHelpers::getRelativePathFrom (moduleInfo.manifestFile,
|
||||
moduleInfo.getFolder())), -1, false);
|
||||
sourceGroup.addFile (getModuleHeaderFile (localModuleFolder), -1, false);
|
||||
|
||||
exporter.getModulesGroup().state.addChild (sourceGroup.state.createCopy(), -1, nullptr);
|
||||
}
|
||||
|
||||
|
||||
//==============================================================================
|
||||
EnabledModuleList::EnabledModuleList (Project& p, const ValueTree& s)
|
||||
: project (p), state (s)
|
||||
{
|
||||
}
|
||||
|
||||
ModuleDescription EnabledModuleList::getModuleInfo (const String& moduleID)
|
||||
{
|
||||
return ModuleDescription (getModuleInfoFile (moduleID));
|
||||
}
|
||||
|
||||
bool EnabledModuleList::isModuleEnabled (const String& moduleID) const
|
||||
{
|
||||
for (int i = 0; i < state.getNumChildren(); ++i)
|
||||
if (state.getChild(i) [Ids::ID] == moduleID)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool EnabledModuleList::isAudioPluginModuleMissing() const
|
||||
{
|
||||
return project.getProjectType().isAudioPlugin()
|
||||
&& ! isModuleEnabled ("juce_audio_plugin_client");
|
||||
}
|
||||
|
||||
Value EnabledModuleList::shouldShowAllModuleFilesInProject (const String& moduleID)
|
||||
{
|
||||
return state.getChildWithProperty (Ids::ID, moduleID)
|
||||
.getPropertyAsValue (Ids::showAllCode, getUndoManager());
|
||||
}
|
||||
|
||||
File EnabledModuleList::findLocalModuleInfoFile (const String& moduleID, bool useExportersForOtherOSes)
|
||||
{
|
||||
for (Project::ExporterIterator exporter (project); exporter.next();)
|
||||
{
|
||||
if (useExportersForOtherOSes || exporter->mayCompileOnCurrentOS())
|
||||
{
|
||||
const String path (exporter->getPathForModuleString (moduleID));
|
||||
|
||||
if (path.isNotEmpty())
|
||||
{
|
||||
const File moduleFolder (project.resolveFilename (path));
|
||||
|
||||
if (moduleFolder.exists())
|
||||
{
|
||||
File f (moduleFolder.getChildFile (ModuleDescription::getManifestFileName()));
|
||||
|
||||
if (f.exists())
|
||||
return f;
|
||||
|
||||
f = moduleFolder.getChildFile (moduleID)
|
||||
.getChildFile (ModuleDescription::getManifestFileName());
|
||||
|
||||
if (f.exists())
|
||||
return f;
|
||||
|
||||
f = moduleFolder.getChildFile ("modules")
|
||||
.getChildFile (moduleID)
|
||||
.getChildFile (ModuleDescription::getManifestFileName());
|
||||
|
||||
if (f.exists())
|
||||
return f;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return File::nonexistent;
|
||||
}
|
||||
|
||||
File EnabledModuleList::getModuleInfoFile (const String& moduleID)
|
||||
{
|
||||
const File f (findLocalModuleInfoFile (moduleID, false));
|
||||
|
||||
if (f != File::nonexistent)
|
||||
return f;
|
||||
|
||||
return findLocalModuleInfoFile (moduleID, true);
|
||||
}
|
||||
|
||||
File EnabledModuleList::getModuleFolder (const String& moduleID)
|
||||
{
|
||||
const File infoFile (getModuleInfoFile (moduleID));
|
||||
|
||||
return infoFile.exists() ? infoFile.getParentDirectory()
|
||||
: File::nonexistent;
|
||||
}
|
||||
|
||||
struct ModuleTreeSorter
|
||||
{
|
||||
static int compareElements (const ValueTree& m1, const ValueTree& m2)
|
||||
{
|
||||
return m1[Ids::ID].toString().compareIgnoreCase (m2[Ids::ID]);
|
||||
}
|
||||
};
|
||||
|
||||
void EnabledModuleList::sortAlphabetically()
|
||||
{
|
||||
ModuleTreeSorter sorter;
|
||||
state.sort (sorter, getUndoManager(), false);
|
||||
}
|
||||
|
||||
Value EnabledModuleList::shouldCopyModuleFilesLocally (const String& moduleID) const
|
||||
{
|
||||
return state.getChildWithProperty (Ids::ID, moduleID)
|
||||
.getPropertyAsValue (Ids::useLocalCopy, getUndoManager());
|
||||
}
|
||||
|
||||
void EnabledModuleList::addModule (const File& moduleManifestFile, bool copyLocally)
|
||||
{
|
||||
ModuleDescription info (moduleManifestFile);
|
||||
|
||||
if (info.isValid())
|
||||
{
|
||||
const String moduleID (info.getID());
|
||||
|
||||
if (! isModuleEnabled (moduleID))
|
||||
{
|
||||
ValueTree module (Ids::MODULES);
|
||||
module.setProperty (Ids::ID, moduleID, nullptr);
|
||||
|
||||
state.addChild (module, -1, getUndoManager());
|
||||
sortAlphabetically();
|
||||
|
||||
shouldShowAllModuleFilesInProject (moduleID) = true;
|
||||
shouldCopyModuleFilesLocally (moduleID) = copyLocally;
|
||||
|
||||
RelativePath path (moduleManifestFile.getParentDirectory().getParentDirectory(),
|
||||
project.getProjectFolder(), RelativePath::projectFolder);
|
||||
|
||||
for (Project::ExporterIterator exporter (project); exporter.next();)
|
||||
exporter->getPathForModuleValue (moduleID) = path.toUnixStyle();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void EnabledModuleList::removeModule (String moduleID) // must be pass-by-value, and not a const ref!
|
||||
{
|
||||
for (int i = state.getNumChildren(); --i >= 0;)
|
||||
if (state.getChild(i) [Ids::ID] == moduleID)
|
||||
state.removeChild (i, getUndoManager());
|
||||
|
||||
for (Project::ExporterIterator exporter (project); exporter.next();)
|
||||
exporter->removePathForModule (moduleID);
|
||||
}
|
||||
|
||||
void EnabledModuleList::createRequiredModules (OwnedArray<LibraryModule>& modules)
|
||||
{
|
||||
for (int i = 0; i < getNumModules(); ++i)
|
||||
{
|
||||
ModuleDescription info (getModuleInfo (getModuleID (i)));
|
||||
|
||||
if (info.isValid())
|
||||
modules.add (new LibraryModule (info));
|
||||
}
|
||||
}
|
||||
|
||||
StringArray EnabledModuleList::getAllModules() const
|
||||
{
|
||||
StringArray moduleIDs;
|
||||
|
||||
for (int i = 0; i < getNumModules(); ++i)
|
||||
moduleIDs.add (getModuleID(i));
|
||||
|
||||
return moduleIDs;
|
||||
}
|
||||
|
||||
static void getDependencies (Project& project, const String& moduleID, StringArray& dependencies)
|
||||
{
|
||||
ModuleDescription info (project.getModules().getModuleInfo (moduleID));
|
||||
|
||||
if (info.isValid())
|
||||
{
|
||||
const var depsArray (info.moduleInfo ["dependencies"]);
|
||||
|
||||
if (const Array<var>* const deps = depsArray.getArray())
|
||||
{
|
||||
for (int i = 0; i < deps->size(); ++i)
|
||||
{
|
||||
const var& d = deps->getReference(i);
|
||||
|
||||
String uid (d [Ids::ID].toString());
|
||||
String version (d [Ids::version].toString());
|
||||
|
||||
if (! dependencies.contains (uid, true))
|
||||
{
|
||||
dependencies.add (uid);
|
||||
getDependencies (project, uid, dependencies);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
StringArray EnabledModuleList::getExtraDependenciesNeeded (const String& moduleID) const
|
||||
{
|
||||
StringArray dependencies, extraDepsNeeded;
|
||||
getDependencies (project, moduleID, dependencies);
|
||||
|
||||
for (int i = 0; i < dependencies.size(); ++i)
|
||||
if ((! isModuleEnabled (dependencies[i])) && dependencies[i] != moduleID)
|
||||
extraDepsNeeded.add (dependencies[i]);
|
||||
|
||||
return extraDepsNeeded;
|
||||
}
|
||||
|
||||
bool EnabledModuleList::areMostModulesCopiedLocally() const
|
||||
{
|
||||
int numYes = 0, numNo = 0;
|
||||
|
||||
for (int i = getNumModules(); --i >= 0;)
|
||||
{
|
||||
if (shouldCopyModuleFilesLocally (getModuleID (i)).getValue())
|
||||
++numYes;
|
||||
else
|
||||
++numNo;
|
||||
}
|
||||
|
||||
return numYes > numNo;
|
||||
}
|
||||
|
||||
void EnabledModuleList::setLocalCopyModeForAllModules (bool copyLocally)
|
||||
{
|
||||
for (int i = getNumModules(); --i >= 0;)
|
||||
shouldCopyModuleFilesLocally (project.getModules().getModuleID (i)) = copyLocally;
|
||||
}
|
||||
|
||||
File EnabledModuleList::findDefaultModulesFolder (Project& project)
|
||||
{
|
||||
ModuleList available;
|
||||
available.scanAllKnownFolders (project);
|
||||
|
||||
for (int i = available.modules.size(); --i >= 0;)
|
||||
{
|
||||
File f (available.modules.getUnchecked(i)->getFolder());
|
||||
|
||||
if (f.isDirectory())
|
||||
return f.getParentDirectory();
|
||||
}
|
||||
|
||||
return File::getCurrentWorkingDirectory();
|
||||
}
|
||||
|
||||
void EnabledModuleList::addModuleFromUserSelectedFile()
|
||||
{
|
||||
static File lastLocation (findDefaultModulesFolder (project));
|
||||
|
||||
FileChooser fc ("Select a module to add...", lastLocation, String::empty, false);
|
||||
|
||||
if (fc.browseForDirectory())
|
||||
{
|
||||
lastLocation = fc.getResult();
|
||||
addModuleOfferingToCopy (lastLocation);
|
||||
}
|
||||
}
|
||||
|
||||
void EnabledModuleList::addModuleInteractive (const String& moduleID)
|
||||
{
|
||||
ModuleList list;
|
||||
list.scanAllKnownFolders (project);
|
||||
|
||||
if (const ModuleDescription* info = list.getModuleWithID (moduleID))
|
||||
addModule (info->manifestFile, areMostModulesCopiedLocally());
|
||||
else
|
||||
addModuleFromUserSelectedFile();
|
||||
}
|
||||
|
||||
void EnabledModuleList::addModuleOfferingToCopy (const File& f)
|
||||
{
|
||||
ModuleDescription m (f);
|
||||
|
||||
if (! m.isValid())
|
||||
m = ModuleDescription (f.getChildFile (ModuleDescription::getManifestFileName()));
|
||||
|
||||
if (! m.isValid())
|
||||
{
|
||||
AlertWindow::showMessageBoxAsync (AlertWindow::InfoIcon,
|
||||
"Add Module", "This wasn't a valid module folder!");
|
||||
return;
|
||||
}
|
||||
|
||||
if (isModuleEnabled (m.getID()))
|
||||
{
|
||||
AlertWindow::showMessageBoxAsync (AlertWindow::InfoIcon,
|
||||
"Add Module", "The project already contains this module!");
|
||||
return;
|
||||
}
|
||||
|
||||
addModule (m.manifestFile, areMostModulesCopiedLocally());
|
||||
}
|
||||
|
||||
bool isJuceFolder (const File& f)
|
||||
{
|
||||
return isJuceModulesFolder (f.getChildFile ("modules"));
|
||||
}
|
||||
|
||||
bool isJuceModulesFolder (const File& f)
|
||||
{
|
||||
return f.isDirectory() && f.getChildFile ("juce_core").isDirectory();
|
||||
}
|
||||
@@ -0,0 +1,169 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
|
||||
Permission is granted to use this software under the terms of either:
|
||||
a) the GPL v2 (or any later version)
|
||||
b) the Affero GPL v3
|
||||
|
||||
Details of these licenses can be found at: www.gnu.org/licenses
|
||||
|
||||
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.juce.com for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#ifndef __JUCER_MODULE_JUCEHEADER__
|
||||
#define __JUCER_MODULE_JUCEHEADER__
|
||||
|
||||
#include "../jucer_Headers.h"
|
||||
#include "jucer_Project.h"
|
||||
class ProjectExporter;
|
||||
class ProjectSaver;
|
||||
|
||||
//==============================================================================
|
||||
File findDefaultModulesFolder (bool mustContainJuceCoreModule = true);
|
||||
bool isJuceModulesFolder (const File&);
|
||||
bool isJuceFolder (const File&);
|
||||
|
||||
//==============================================================================
|
||||
struct ModuleDescription
|
||||
{
|
||||
ModuleDescription() {}
|
||||
ModuleDescription (const File& manifest);
|
||||
ModuleDescription (const var& info) : moduleInfo (info) {}
|
||||
|
||||
bool isValid() const { return getID().isNotEmpty(); }
|
||||
|
||||
String getID() const { return moduleInfo [Ids::ID].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 getHeaderName() const { return moduleInfo [Ids::include].toString(); }
|
||||
String getPreprocessorDefs() const { return moduleInfo [Ids::defines].toString(); }
|
||||
|
||||
File getFolder() const { jassert (manifestFile != File::nonexistent); return manifestFile.getParentDirectory(); }
|
||||
|
||||
bool isPluginClient() const { return getID() == "juce_audio_plugin_client"; }
|
||||
|
||||
static const char* getManifestFileName() { return "juce_module_info"; }
|
||||
|
||||
var moduleInfo;
|
||||
File manifestFile;
|
||||
URL url;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
struct ModuleList
|
||||
{
|
||||
ModuleList();
|
||||
ModuleList (const ModuleList&);
|
||||
ModuleList& operator= (const ModuleList&);
|
||||
|
||||
const ModuleDescription* getModuleWithID (const String& moduleID) const;
|
||||
StringArray getIDs() const;
|
||||
void sort();
|
||||
|
||||
Result addAllModulesInFolder (const File&);
|
||||
Result scanAllKnownFolders (Project&);
|
||||
bool loadFromWebsite();
|
||||
|
||||
OwnedArray<ModuleDescription> modules;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
class LibraryModule
|
||||
{
|
||||
public:
|
||||
LibraryModule (const ModuleDescription&);
|
||||
|
||||
bool isValid() const { return moduleInfo.isValid(); }
|
||||
String getID() const { return moduleInfo.getID(); }
|
||||
String getVersion() const { return moduleInfo.getVersion(); }
|
||||
String getName() const { return moduleInfo.getName(); }
|
||||
String getDescription() const { return moduleInfo.getDescription(); }
|
||||
String getLicense() const { return moduleInfo.getLicense(); }
|
||||
|
||||
File getFolder() const { return moduleInfo.getFolder(); }
|
||||
|
||||
void writeIncludes (ProjectSaver&, OutputStream&);
|
||||
void prepareExporter (ProjectExporter&, ProjectSaver&) const;
|
||||
void createPropertyEditors (ProjectExporter&, PropertyListBuilder&) const;
|
||||
void getConfigFlags (Project&, OwnedArray<Project::ConfigFlag>& flags) const;
|
||||
void findBrowseableFiles (const File& localModuleFolder, Array<File>& files) const;
|
||||
|
||||
ModuleDescription moduleInfo;
|
||||
|
||||
private:
|
||||
mutable Array<File> sourceFiles;
|
||||
|
||||
File getModuleHeaderFile (const File& folder) const;
|
||||
|
||||
void findWildcardMatches (const File& localModuleFolder, const String& wildcardPath, Array<File>& result) const;
|
||||
void findAndAddCompiledCode (ProjectExporter&, ProjectSaver&, const File& localModuleFolder, Array<File>& result) const;
|
||||
void addBrowsableCode (ProjectExporter&, ProjectSaver&, const Array<File>& compiled, const File& localModuleFolder) const;
|
||||
void createLocalHeaderWrapper (ProjectSaver&, const File& originalHeader, const File& localHeader) const;
|
||||
|
||||
bool isAUPluginHost (const Project&) const;
|
||||
bool isVSTPluginHost (const Project&) const;
|
||||
bool isVST3PluginHost (const Project&) const;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
class EnabledModuleList
|
||||
{
|
||||
public:
|
||||
EnabledModuleList (Project&, const ValueTree&);
|
||||
|
||||
bool isModuleEnabled (const String& moduleID) const;
|
||||
Value shouldShowAllModuleFilesInProject (const String& moduleID);
|
||||
Value shouldCopyModuleFilesLocally (const String& moduleID) const;
|
||||
void removeModule (String moduleID);
|
||||
bool isAudioPluginModuleMissing() const;
|
||||
|
||||
ModuleDescription getModuleInfo (const String& moduleID);
|
||||
|
||||
File getModuleInfoFile (const String& moduleID);
|
||||
File getModuleFolder (const String& moduleID);
|
||||
|
||||
void addModule (const File& moduleManifestFile, bool copyLocally);
|
||||
void addModuleInteractive (const String& moduleID);
|
||||
void addModuleFromUserSelectedFile();
|
||||
void addModuleOfferingToCopy (const File&);
|
||||
|
||||
StringArray getAllModules() const;
|
||||
StringArray getExtraDependenciesNeeded (const String& moduleID) const;
|
||||
void createRequiredModules (OwnedArray<LibraryModule>& modules);
|
||||
|
||||
int getNumModules() const { return state.getNumChildren(); }
|
||||
String getModuleID (int index) const { return state.getChild (index) [Ids::ID].toString(); }
|
||||
|
||||
bool areMostModulesCopiedLocally() const;
|
||||
void setLocalCopyModeForAllModules (bool copyLocally);
|
||||
void sortAlphabetically();
|
||||
|
||||
static File findDefaultModulesFolder (Project&);
|
||||
|
||||
Project& project;
|
||||
ValueTree state;
|
||||
|
||||
private:
|
||||
UndoManager* getUndoManager() const { return project.getUndoManagerFor (state); }
|
||||
|
||||
File findLocalModuleInfoFile (const String& moduleID, bool useExportersForOtherOSes);
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (EnabledModuleList)
|
||||
};
|
||||
|
||||
|
||||
#endif // __JUCER_MODULE_JUCEHEADER__
|
||||
@@ -0,0 +1,541 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
|
||||
Permission is granted to use this software under the terms of either:
|
||||
a) the GPL v2 (or any later version)
|
||||
b) the Affero GPL v3
|
||||
|
||||
Details of these licenses can be found at: www.gnu.org/licenses
|
||||
|
||||
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.juce.com for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
|
||||
class ModulesPanel : public Component,
|
||||
private TableListBoxModel,
|
||||
private ValueTree::Listener,
|
||||
private Button::Listener
|
||||
{
|
||||
public:
|
||||
ModulesPanel (Project& p)
|
||||
: project (p),
|
||||
modulesValueTree (p.getModules().state),
|
||||
addWebModuleButton ("Download and add a module..."),
|
||||
updateModuleButton ("Install updates to modules..."),
|
||||
setCopyModeButton ("Set copy-mode for all modules..."),
|
||||
copyPathButton ("Set paths for all modules...")
|
||||
{
|
||||
table.getHeader().addColumn ("Module", nameCol, 180, 100, 400, TableHeaderComponent::notSortable);
|
||||
table.getHeader().addColumn ("Installed Version", versionCol, 100, 100, 100, TableHeaderComponent::notSortable);
|
||||
table.getHeader().addColumn ("Available Version", updateCol, 100, 100, 100, TableHeaderComponent::notSortable);
|
||||
table.getHeader().addColumn ("Make Local Copy", copyCol, 100, 100, 100, TableHeaderComponent::notSortable);
|
||||
table.getHeader().addColumn ("Paths", pathCol, 250, 100, 600, TableHeaderComponent::notSortable);
|
||||
|
||||
table.setModel (this);
|
||||
table.setColour (TableListBox::backgroundColourId, Colours::transparentBlack);
|
||||
addAndMakeVisible (table);
|
||||
table.updateContent();
|
||||
table.setRowHeight (20);
|
||||
|
||||
addAndMakeVisible (addWebModuleButton);
|
||||
addAndMakeVisible (updateModuleButton);
|
||||
addAndMakeVisible (setCopyModeButton);
|
||||
addAndMakeVisible (copyPathButton);
|
||||
addWebModuleButton.addListener (this);
|
||||
updateModuleButton.addListener (this);
|
||||
updateModuleButton.setEnabled (false);
|
||||
setCopyModeButton.addListener (this);
|
||||
setCopyModeButton.setTriggeredOnMouseDown (true);
|
||||
copyPathButton.addListener (this);
|
||||
copyPathButton.setTriggeredOnMouseDown (true);
|
||||
|
||||
modulesValueTree.addListener (this);
|
||||
lookAndFeelChanged();
|
||||
}
|
||||
|
||||
void paint (Graphics& g) override
|
||||
{
|
||||
if (webUpdateThread == nullptr)
|
||||
webUpdateThread = new WebsiteUpdateFetchThread (*this);
|
||||
|
||||
IntrojucerLookAndFeel::fillWithBackgroundTexture (*this, g);
|
||||
}
|
||||
|
||||
void resized() override
|
||||
{
|
||||
Rectangle<int> r (getLocalBounds().reduced (5, 4));
|
||||
|
||||
table.setBounds (r.removeFromTop (table.getRowPosition (getNumRows() - 1, true).getBottom() + 20));
|
||||
|
||||
Rectangle<int> buttonRow (r.removeFromTop (32).removeFromBottom (28));
|
||||
addWebModuleButton.setBounds (buttonRow.removeFromLeft (jmin (260, r.getWidth() / 3)));
|
||||
buttonRow.removeFromLeft (8);
|
||||
updateModuleButton.setBounds (buttonRow.removeFromLeft (jmin (260, r.getWidth() / 3)));
|
||||
buttonRow.removeFromLeft (8);
|
||||
|
||||
buttonRow = r.removeFromTop (34).removeFromBottom (28);
|
||||
setCopyModeButton.setBounds (buttonRow.removeFromLeft (jmin (260, r.getWidth() / 3)));
|
||||
buttonRow.removeFromLeft (8);
|
||||
copyPathButton.setBounds (buttonRow.removeFromLeft (jmin (260, r.getWidth() / 3)));
|
||||
}
|
||||
|
||||
int getNumRows() override
|
||||
{
|
||||
return project.getModules().getNumModules();
|
||||
}
|
||||
|
||||
void paintRowBackground (Graphics& g, int /*rowNumber*/, int width, int height, bool rowIsSelected) override
|
||||
{
|
||||
g.setColour (rowIsSelected ? Colours::lightblue.withAlpha (0.4f)
|
||||
: Colours::white.withAlpha (0.4f));
|
||||
g.fillRect (0, 0, width, height - 1);
|
||||
}
|
||||
|
||||
void paintCell (Graphics& g, int rowNumber, int columnId, int width, int height, bool /*rowIsSelected*/) override
|
||||
{
|
||||
String text;
|
||||
const String moduleID (project.getModules().getModuleID (rowNumber));
|
||||
|
||||
if (columnId == nameCol)
|
||||
{
|
||||
text = moduleID;
|
||||
}
|
||||
else if (columnId == versionCol)
|
||||
{
|
||||
text = project.getModules().getModuleInfo (moduleID).getVersion();
|
||||
|
||||
if (text.isEmpty())
|
||||
text = "?";
|
||||
}
|
||||
else if (columnId == updateCol)
|
||||
{
|
||||
if (listFromWebsite != nullptr)
|
||||
{
|
||||
if (const ModuleDescription* m = listFromWebsite->getModuleWithID (moduleID))
|
||||
{
|
||||
if (m->getVersion() != project.getModules().getModuleInfo (moduleID).getVersion())
|
||||
text = m->getVersion() + " available";
|
||||
else
|
||||
text = "Up-to-date";
|
||||
}
|
||||
else
|
||||
text = "?";
|
||||
}
|
||||
else
|
||||
{
|
||||
text = "-";
|
||||
}
|
||||
}
|
||||
else if (columnId == copyCol)
|
||||
{
|
||||
text = project.getModules().shouldCopyModuleFilesLocally (moduleID).getValue()
|
||||
? "Yes" : "No";
|
||||
}
|
||||
else if (columnId == pathCol)
|
||||
{
|
||||
StringArray paths;
|
||||
|
||||
for (Project::ExporterIterator exporter (project); exporter.next();)
|
||||
paths.addIfNotAlreadyThere (exporter->getPathForModuleString (moduleID).trim());
|
||||
|
||||
text = paths.joinIntoString (", ");
|
||||
}
|
||||
|
||||
g.setColour (Colours::black);
|
||||
g.setFont (height * 0.65f);
|
||||
g.drawText (text, Rectangle<int> (width, height).reduced (4, 0), Justification::centredLeft, true);
|
||||
}
|
||||
|
||||
void cellDoubleClicked (int rowNumber, int, const MouseEvent&) override
|
||||
{
|
||||
const String moduleID (project.getModules().getModuleID (rowNumber));
|
||||
|
||||
if (moduleID.isNotEmpty())
|
||||
if (ProjectContentComponent* pcc = findParentComponentOfClass<ProjectContentComponent>())
|
||||
pcc->showModule (moduleID);
|
||||
}
|
||||
|
||||
void deleteKeyPressed (int row) override
|
||||
{
|
||||
project.getModules().removeModule (project.getModules().getModuleID (row));
|
||||
}
|
||||
|
||||
void webUpdateFinished (const ModuleList& newList)
|
||||
{
|
||||
listFromWebsite = new ModuleList (newList);
|
||||
|
||||
table.updateContent();
|
||||
table.repaint();
|
||||
|
||||
updateModuleButton.setEnabled (getUpdatableModules().size() != 0);
|
||||
}
|
||||
|
||||
void buttonClicked (Button* b)
|
||||
{
|
||||
if (b == &addWebModuleButton) showAddModuleMenu();
|
||||
else if (b == &updateModuleButton) showUpdateModulesMenu();
|
||||
else if (b == &setCopyModeButton) showCopyModeMenu();
|
||||
else if (b == ©PathButton) showSetPathsMenu();
|
||||
}
|
||||
|
||||
private:
|
||||
enum
|
||||
{
|
||||
nameCol = 1,
|
||||
versionCol,
|
||||
updateCol,
|
||||
copyCol,
|
||||
pathCol
|
||||
};
|
||||
|
||||
Project& project;
|
||||
ValueTree modulesValueTree;
|
||||
TableListBox table;
|
||||
TextButton addWebModuleButton, updateModuleButton, setCopyModeButton, copyPathButton;
|
||||
ScopedPointer<ModuleList> listFromWebsite;
|
||||
|
||||
void valueTreePropertyChanged (ValueTree&, const Identifier&) override { itemChanged(); }
|
||||
void valueTreeChildAdded (ValueTree&, ValueTree&) override { itemChanged(); }
|
||||
void valueTreeChildRemoved (ValueTree&, ValueTree&) override { itemChanged(); }
|
||||
void valueTreeChildOrderChanged (ValueTree&) override { itemChanged(); }
|
||||
void valueTreeParentChanged (ValueTree&) override { itemChanged(); }
|
||||
|
||||
void itemChanged()
|
||||
{
|
||||
table.updateContent();
|
||||
resized();
|
||||
repaint();
|
||||
}
|
||||
|
||||
StringArray getUpdatableModules() const
|
||||
{
|
||||
StringArray result;
|
||||
|
||||
if (listFromWebsite != nullptr)
|
||||
{
|
||||
for (int i = 0; i < listFromWebsite->modules.size(); ++i)
|
||||
{
|
||||
const ModuleDescription* m = listFromWebsite->modules.getUnchecked(i);
|
||||
const String v1 (m->getVersion());
|
||||
const String v2 (project.getModules().getModuleInfo (m->getID()).getVersion());
|
||||
|
||||
if (v1 != v2 && v1.isNotEmpty() && v2.isNotEmpty())
|
||||
result.add (m->getID());
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
StringArray getAddableModules() const
|
||||
{
|
||||
StringArray result;
|
||||
|
||||
if (listFromWebsite != nullptr)
|
||||
{
|
||||
for (int i = 0; i < listFromWebsite->modules.size(); ++i)
|
||||
{
|
||||
const ModuleDescription* m = listFromWebsite->modules.getUnchecked(i);
|
||||
|
||||
if (! project.getModules().isModuleEnabled (m->getID()))
|
||||
result.add (m->getID());
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void showUpdateModulesMenu()
|
||||
{
|
||||
StringArray mods (getUpdatableModules());
|
||||
|
||||
PopupMenu m;
|
||||
m.addItem (1000, "Update all modules");
|
||||
m.addSeparator();
|
||||
|
||||
for (int i = 0; i < mods.size(); ++i)
|
||||
m.addItem (1 + i, "Update " + mods[i]);
|
||||
|
||||
int res = m.showAt (&updateModuleButton);
|
||||
|
||||
if (res > 0 && listFromWebsite != nullptr)
|
||||
{
|
||||
if (res != 1000)
|
||||
mods = StringArray (mods[res - 1]);
|
||||
|
||||
Array<ModuleDescription> modsToUpdate;
|
||||
|
||||
for (int i = 0; i < mods.size(); ++i)
|
||||
{
|
||||
if (const ModuleDescription* md = listFromWebsite->getModuleWithID (mods[i]))
|
||||
{
|
||||
ModuleDescription modToUpdate (*md);
|
||||
modToUpdate.manifestFile = project.getModules().getModuleInfo (modToUpdate.getID()).manifestFile;
|
||||
modsToUpdate.add (modToUpdate);
|
||||
}
|
||||
}
|
||||
|
||||
DownloadAndInstallThread::updateModulesFromWeb (project, modsToUpdate);
|
||||
}
|
||||
}
|
||||
|
||||
void showAddModuleMenu()
|
||||
{
|
||||
if (listFromWebsite == nullptr)
|
||||
{
|
||||
AlertWindow::showMessageBoxAsync (AlertWindow::WarningIcon,
|
||||
"Couldn't contact the website!",
|
||||
"Failed to get the latest module list from juce.com - "
|
||||
"maybe network or server problems - try again soon!");
|
||||
return;
|
||||
}
|
||||
|
||||
StringArray mods (getAddableModules());
|
||||
|
||||
if (mods.size() == 0)
|
||||
{
|
||||
AlertWindow::showMessageBoxAsync (AlertWindow::WarningIcon,
|
||||
"No modules to add!",
|
||||
"Couldn't find any new modules that aren't already in your project!");
|
||||
return;
|
||||
}
|
||||
|
||||
PopupMenu m;
|
||||
|
||||
for (int i = 0; i < mods.size(); ++i)
|
||||
m.addItem (i + 1, "Install " + mods[i]);
|
||||
|
||||
int res = m.showAt (&addWebModuleButton);
|
||||
|
||||
if (res > 0 && listFromWebsite != nullptr)
|
||||
if (const ModuleDescription* md = listFromWebsite->getModuleWithID (mods[res - 1]))
|
||||
DownloadAndInstallThread::addModuleFromWebsite (project, *md);
|
||||
}
|
||||
|
||||
void showCopyModeMenu()
|
||||
{
|
||||
PopupMenu m;
|
||||
m.addItem (1, "Set all modules to copy locally");
|
||||
m.addItem (2, "Set all modules to not copy locally");
|
||||
|
||||
int res = m.showAt (&setCopyModeButton);
|
||||
|
||||
if (res != 0)
|
||||
project.getModules().setLocalCopyModeForAllModules (res == 1);
|
||||
}
|
||||
|
||||
void showSetPathsMenu()
|
||||
{
|
||||
EnabledModuleList& moduleList = project.getModules();
|
||||
|
||||
const String moduleToCopy (moduleList.getModuleID (table.getSelectedRow()));
|
||||
|
||||
if (moduleToCopy.isNotEmpty())
|
||||
{
|
||||
PopupMenu m;
|
||||
m.addItem (1, "Copy the paths from the module '" + moduleToCopy + "' to all other modules");
|
||||
|
||||
int res = m.showAt (©PathButton);
|
||||
|
||||
if (res != 0)
|
||||
{
|
||||
for (Project::ExporterIterator exporter (project); exporter.next();)
|
||||
{
|
||||
for (int i = 0; i < moduleList.getNumModules(); ++i)
|
||||
{
|
||||
String modID = moduleList.getModuleID (i);
|
||||
|
||||
if (modID != moduleToCopy)
|
||||
exporter->getPathForModuleValue (modID) = exporter->getPathForModuleValue (moduleToCopy).getValue();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
table.repaint();
|
||||
}
|
||||
else
|
||||
{
|
||||
PopupMenu m;
|
||||
m.addItem (1, "Copy the paths from the selected module to all other modules", false);
|
||||
|
||||
m.showAt (©PathButton);
|
||||
}
|
||||
}
|
||||
|
||||
struct WebsiteUpdateFetchThread : private Thread,
|
||||
private AsyncUpdater
|
||||
{
|
||||
WebsiteUpdateFetchThread (ModulesPanel& p) : Thread ("Web Updater"), panel (p)
|
||||
{
|
||||
startThread (3);
|
||||
}
|
||||
|
||||
~WebsiteUpdateFetchThread()
|
||||
{
|
||||
stopThread (15000);
|
||||
}
|
||||
|
||||
void run() override
|
||||
{
|
||||
static Time lastDownloadTime;
|
||||
static ModuleList lastList;
|
||||
|
||||
if (Time::getCurrentTime() < lastDownloadTime + RelativeTime::minutes (2.0))
|
||||
{
|
||||
list = lastList;
|
||||
triggerAsyncUpdate();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (list.loadFromWebsite() && ! threadShouldExit())
|
||||
{
|
||||
lastList = list;
|
||||
lastDownloadTime = Time::getCurrentTime();
|
||||
triggerAsyncUpdate();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void handleAsyncUpdate() override
|
||||
{
|
||||
panel.webUpdateFinished (list);
|
||||
}
|
||||
|
||||
private:
|
||||
ModuleList list;
|
||||
ModulesPanel& panel;
|
||||
};
|
||||
|
||||
ScopedPointer<WebsiteUpdateFetchThread> webUpdateThread;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ModulesPanel)
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
class DownloadAndInstallThread : public ThreadWithProgressWindow
|
||||
{
|
||||
public:
|
||||
DownloadAndInstallThread (const Array<ModuleDescription>& modulesToInstall)
|
||||
: ThreadWithProgressWindow ("Installing New Modules", true, true),
|
||||
result (Result::ok()),
|
||||
modules (modulesToInstall)
|
||||
{
|
||||
}
|
||||
|
||||
static void updateModulesFromWeb (Project& project, const Array<ModuleDescription>& mods)
|
||||
{
|
||||
DownloadAndInstallThread d (mods);
|
||||
|
||||
if (d.runThread())
|
||||
{
|
||||
if (d.result.failed())
|
||||
{
|
||||
AlertWindow::showMessageBoxAsync (AlertWindow::WarningIcon,
|
||||
"Module Install Failed",
|
||||
d.result.getErrorMessage());
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < d.modules.size(); ++i)
|
||||
project.getModules().addModule (d.modules.getReference(i).manifestFile,
|
||||
project.getModules().areMostModulesCopiedLocally());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void addModuleFromWebsite (Project& project, const ModuleDescription& module)
|
||||
{
|
||||
Array<ModuleDescription> mods;
|
||||
mods.add (module);
|
||||
|
||||
static File lastLocation (EnabledModuleList::findDefaultModulesFolder (project));
|
||||
|
||||
FileChooser fc ("Select the parent folder for the new module...", lastLocation, String::empty, false);
|
||||
|
||||
if (fc.browseForDirectory())
|
||||
{
|
||||
lastLocation = fc.getResult();
|
||||
|
||||
if (lastLocation.getChildFile (ModuleDescription::getManifestFileName()).exists())
|
||||
{
|
||||
AlertWindow::showMessageBoxAsync (AlertWindow::WarningIcon,
|
||||
"Adding Module",
|
||||
"You chose a folder that appears to be a module.\n\n"
|
||||
"You need to select the *parent* folder inside which the new modules will be created.");
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < mods.size(); ++i)
|
||||
mods.getReference(i).manifestFile = lastLocation.getChildFile (mods.getReference(i).getID())
|
||||
.getChildFile (ModuleDescription::getManifestFileName());
|
||||
|
||||
updateModulesFromWeb (project, mods);
|
||||
}
|
||||
}
|
||||
|
||||
void run() override
|
||||
{
|
||||
for (int i = 0; i < modules.size(); ++i)
|
||||
{
|
||||
const ModuleDescription& m = modules.getReference(i);
|
||||
|
||||
setProgress (i / (double) modules.size());
|
||||
|
||||
MemoryBlock downloaded;
|
||||
result = download (m, downloaded);
|
||||
|
||||
if (result.failed() || threadShouldExit())
|
||||
break;
|
||||
|
||||
result = unzip (m, downloaded);
|
||||
|
||||
if (result.failed() || threadShouldExit())
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Result download (const ModuleDescription& m, MemoryBlock& dest)
|
||||
{
|
||||
setStatusMessage ("Downloading " + m.getID() + "...");
|
||||
|
||||
const ScopedPointer<InputStream> in (m.url.createInputStream (false, nullptr, nullptr, String::empty, 10000));
|
||||
|
||||
if (in != nullptr && in->readIntoMemoryBlock (dest))
|
||||
return Result::ok();
|
||||
|
||||
return Result::fail ("Failed to download from: " + m.url.toString (false));
|
||||
}
|
||||
|
||||
Result unzip (const ModuleDescription& m, const MemoryBlock& data)
|
||||
{
|
||||
setStatusMessage ("Installing " + m.getID() + "...");
|
||||
|
||||
MemoryInputStream input (data, false);
|
||||
ZipFile zip (input);
|
||||
|
||||
if (zip.getNumEntries() == 0)
|
||||
return Result::fail ("The downloaded file wasn't a valid module file!");
|
||||
|
||||
if (! m.getFolder().deleteRecursively())
|
||||
return Result::fail ("Couldn't delete the existing folder:\n" + m.getFolder().getFullPathName());
|
||||
|
||||
return zip.uncompressTo (m.getFolder().getParentDirectory(), true);
|
||||
}
|
||||
|
||||
Result result;
|
||||
Array<ModuleDescription> modules;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DownloadAndInstallThread)
|
||||
};
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,290 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
|
||||
Permission is granted to use this software under the terms of either:
|
||||
a) the GPL v2 (or any later version)
|
||||
b) the Affero GPL v3
|
||||
|
||||
Details of these licenses can be found at: www.gnu.org/licenses
|
||||
|
||||
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.juce.com for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#ifndef __JUCER_PROJECT_JUCEHEADER__
|
||||
#define __JUCER_PROJECT_JUCEHEADER__
|
||||
|
||||
#include "../jucer_Headers.h"
|
||||
class ProjectExporter;
|
||||
class ProjectType;
|
||||
class LibraryModule;
|
||||
class EnabledModuleList;
|
||||
|
||||
//==============================================================================
|
||||
class Project : public FileBasedDocument,
|
||||
public ValueTree::Listener
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
Project (const File& file);
|
||||
~Project();
|
||||
|
||||
//==============================================================================
|
||||
// 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);
|
||||
File getLastDocumentOpened() override;
|
||||
void setLastDocumentOpened (const File& file) override;
|
||||
|
||||
void setTitle (const String& newTitle);
|
||||
|
||||
//==============================================================================
|
||||
File getProjectFolder() const { return getFile().getParentDirectory(); }
|
||||
ValueTree getProjectRoot() const { return projectRoot; }
|
||||
String getTitle() const;
|
||||
Value getProjectNameValue() { return getMainGroup().getNameValue(); }
|
||||
String getProjectFilenameRoot() { return File::createLegalFileName (getDocumentTitle()); }
|
||||
String getProjectUID() const { return projectRoot [Ids::ID]; }
|
||||
|
||||
//==============================================================================
|
||||
template <class FileType>
|
||||
bool shouldBeAddedToBinaryResourcesByDefault (const FileType& file)
|
||||
{
|
||||
return ! file.hasFileExtension (sourceOrHeaderFileExtensions);
|
||||
}
|
||||
|
||||
File resolveFilename (String filename) const;
|
||||
String getRelativePathForFile (const File& file) const;
|
||||
|
||||
//==============================================================================
|
||||
// Creates editors for the project settings
|
||||
void createPropertyEditors (PropertyListBuilder&);
|
||||
|
||||
//==============================================================================
|
||||
// project types
|
||||
const ProjectType& getProjectType() const;
|
||||
Value getProjectTypeValue() { return getProjectValue (Ids::projectType); }
|
||||
String getProjectTypeString() const { return projectRoot [Ids::projectType]; }
|
||||
|
||||
Value getVersionValue() { return getProjectValue (Ids::version); }
|
||||
String getVersionString() const { return projectRoot [Ids::version]; }
|
||||
String getVersionAsHex() const;
|
||||
int getVersionAsHexInteger() const;
|
||||
|
||||
Value getBundleIdentifier() { return getProjectValue (Ids::bundleIdentifier); }
|
||||
String getDefaultBundleIdentifier() { return "com.yourcompany." + CodeHelpers::makeValidIdentifier (getTitle(), false, true, false); }
|
||||
|
||||
Value getAAXIdentifier() { return getProjectValue (Ids::aaxIdentifier); }
|
||||
String getDefaultAAXIdentifier() { return getDefaultBundleIdentifier(); }
|
||||
|
||||
Value getCompanyName() { return getProjectValue (Ids::companyName); }
|
||||
Value getCompanyWebsite() { return getProjectValue (Ids::companyWebsite); }
|
||||
Value getCompanyEmail() { return getProjectValue (Ids::companyEmail); }
|
||||
|
||||
//==============================================================================
|
||||
Value getProjectValue (const Identifier& name) { return projectRoot.getPropertyAsValue (name, getUndoManagerFor (projectRoot)); }
|
||||
|
||||
Value getProjectPreprocessorDefs() { return getProjectValue (Ids::defines); }
|
||||
StringPairArray getPreprocessorDefs() const;
|
||||
|
||||
Value getProjectUserNotes() { return getProjectValue (Ids::userNotes); }
|
||||
|
||||
//==============================================================================
|
||||
File getGeneratedCodeFolder() const { return getFile().getSiblingFile ("JuceLibraryCode"); }
|
||||
File getAppIncludeFile() const { return getGeneratedCodeFolder().getChildFile (getJuceSourceHFilename()); }
|
||||
|
||||
File getBinaryDataCppFile (int index) const;
|
||||
File getBinaryDataHeaderFile() const { return getBinaryDataCppFile (0).withFileExtension (".h"); }
|
||||
Value getMaxBinaryFileSize() { return getProjectValue (Ids::maxBinaryFileSize); }
|
||||
Value shouldIncludeBinaryInAppConfig() { return getProjectValue (Ids::includeBinaryInAppConfig); }
|
||||
|
||||
//==============================================================================
|
||||
String getAmalgamatedHeaderFileName() const { return "juce_amalgamated.h"; }
|
||||
String getAmalgamatedMMFileName() const { return "juce_amalgamated.mm"; }
|
||||
String getAmalgamatedCppFileName() const { return "juce_amalgamated.cpp"; }
|
||||
|
||||
String getAppConfigFilename() const { return "AppConfig.h"; }
|
||||
String getJuceSourceFilenameRoot() const { return "JuceLibraryCode"; }
|
||||
int getNumSeparateAmalgamatedFiles() const { return 4; }
|
||||
String getJuceSourceHFilename() const { return "JuceHeader.h"; }
|
||||
|
||||
//==============================================================================
|
||||
class Item
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
Item (Project& project, const ValueTree& itemNode);
|
||||
Item (const Item& other);
|
||||
|
||||
static Item createGroup (Project& project, const String& name, const String& uid);
|
||||
void initialiseMissingProperties();
|
||||
|
||||
//==============================================================================
|
||||
bool isValid() const { return state.isValid(); }
|
||||
bool operator== (const Item& other) const { return state == other.state && &project == &other.project; }
|
||||
bool operator!= (const Item& other) const { return ! operator== (other); }
|
||||
|
||||
//==============================================================================
|
||||
bool isFile() const;
|
||||
bool isGroup() const;
|
||||
bool isMainGroup() const;
|
||||
bool isImageFile() const;
|
||||
|
||||
String getID() const;
|
||||
void setID (const String& newID);
|
||||
Item findItemWithID (const String& targetId) const; // (recursive search)
|
||||
|
||||
String getImageFileID() const;
|
||||
Drawable* loadAsImageFile() const;
|
||||
|
||||
//==============================================================================
|
||||
Value getNameValue();
|
||||
String getName() const;
|
||||
File getFile() const;
|
||||
String getFilePath() const;
|
||||
void setFile (const File& file);
|
||||
void setFile (const RelativePath& file);
|
||||
File determineGroupFolder() const;
|
||||
bool renameFile (const File& newFile);
|
||||
|
||||
bool shouldBeAddedToTargetProject() const;
|
||||
bool shouldBeCompiled() const;
|
||||
Value getShouldCompileValue();
|
||||
bool shouldBeAddedToBinaryResources() const;
|
||||
Value getShouldAddToResourceValue();
|
||||
Value getShouldInhibitWarningsValue();
|
||||
bool shouldInhibitWarnings() const;
|
||||
Value getShouldUseStdCallValue();
|
||||
bool shouldUseStdCall() const;
|
||||
|
||||
//==============================================================================
|
||||
bool canContain (const Item& child) const;
|
||||
int getNumChildren() const { return state.getNumChildren(); }
|
||||
Item getChild (int index) const { return Item (project, state.getChild (index)); }
|
||||
|
||||
Item addNewSubGroup (const String& name, int insertIndex);
|
||||
Item getOrCreateSubGroup (const String& name);
|
||||
void addChild (const Item& newChild, int insertIndex);
|
||||
bool addFile (const File& file, int insertIndex, bool shouldCompile);
|
||||
void addFileUnchecked (const File& file, int insertIndex, bool shouldCompile);
|
||||
bool addRelativeFile (const RelativePath& file, int insertIndex, bool shouldCompile);
|
||||
void removeItemFromProject();
|
||||
void sortAlphabetically (bool keepGroupsAtStart);
|
||||
Item findItemForFile (const File& file) const;
|
||||
bool containsChildForFile (const RelativePath& file) const;
|
||||
|
||||
Item getParent() const;
|
||||
Item createCopy();
|
||||
|
||||
UndoManager* getUndoManager() const { return project.getUndoManagerFor (state); }
|
||||
|
||||
Icon getIcon() const;
|
||||
bool isIconCrossedOut() const;
|
||||
|
||||
Project& project;
|
||||
ValueTree state;
|
||||
|
||||
private:
|
||||
Item& operator= (const Item&);
|
||||
};
|
||||
|
||||
Item getMainGroup();
|
||||
|
||||
void findAllImageItems (OwnedArray<Item>& items);
|
||||
|
||||
//==============================================================================
|
||||
ValueTree getExporters();
|
||||
int getNumExporters();
|
||||
ProjectExporter* createExporter (int index);
|
||||
void addNewExporter (const String& exporterName);
|
||||
void createExporterForCurrentPlatform();
|
||||
|
||||
struct ExporterIterator
|
||||
{
|
||||
ExporterIterator (Project& project);
|
||||
~ExporterIterator();
|
||||
|
||||
bool next();
|
||||
|
||||
ProjectExporter& operator*() const { return *exporter; }
|
||||
ProjectExporter* operator->() const { return exporter; }
|
||||
|
||||
ScopedPointer<ProjectExporter> exporter;
|
||||
int index;
|
||||
|
||||
private:
|
||||
Project& project;
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ExporterIterator)
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
struct ConfigFlag
|
||||
{
|
||||
String symbol, description, sourceModuleID;
|
||||
Value value; // 1 = true, 2 = false, anything else = use default
|
||||
};
|
||||
|
||||
static const char* const configFlagDefault;
|
||||
static const char* const configFlagEnabled;
|
||||
static const char* const configFlagDisabled;
|
||||
Value getConfigFlag (const String& name);
|
||||
bool isConfigFlagEnabled (const String& name) const;
|
||||
|
||||
//==============================================================================
|
||||
EnabledModuleList& getModules();
|
||||
|
||||
//==============================================================================
|
||||
String getFileTemplate (const String& templateName);
|
||||
|
||||
//==============================================================================
|
||||
PropertiesFile& getStoredProperties() const;
|
||||
|
||||
//==============================================================================
|
||||
void valueTreePropertyChanged (ValueTree&, const Identifier&) override;
|
||||
void valueTreeChildAdded (ValueTree&, ValueTree&) override;
|
||||
void valueTreeChildRemoved (ValueTree&, ValueTree&) override;
|
||||
void valueTreeChildOrderChanged (ValueTree&) override;
|
||||
void valueTreeParentChanged (ValueTree&) override;
|
||||
|
||||
//==============================================================================
|
||||
UndoManager* getUndoManagerFor (const ValueTree&) const { return nullptr; }
|
||||
|
||||
//==============================================================================
|
||||
static const char* projectFileExtension;
|
||||
|
||||
private:
|
||||
friend class Item;
|
||||
ValueTree projectRoot;
|
||||
ScopedPointer<EnabledModuleList> enabledModulesList;
|
||||
|
||||
void updateProjectSettings();
|
||||
void sanitiseConfigFlags();
|
||||
void setMissingDefaultValues();
|
||||
ValueTree getConfigurations() const;
|
||||
ValueTree getConfigNode();
|
||||
|
||||
void updateOldStyleConfigList();
|
||||
void moveOldPropertyFromProjectToAllExporters (Identifier name);
|
||||
void removeDefunctExporters();
|
||||
void updateOldModulePaths();
|
||||
void warnAboutOldIntrojucerVersion();
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Project)
|
||||
};
|
||||
|
||||
|
||||
#endif // __JUCER_PROJECT_JUCEHEADER__
|
||||
@@ -0,0 +1,937 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
|
||||
Permission is granted to use this software under the terms of either:
|
||||
a) the GPL v2 (or any later version)
|
||||
b) the Affero GPL v3
|
||||
|
||||
Details of these licenses can be found at: www.gnu.org/licenses
|
||||
|
||||
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.juce.com for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#include "jucer_ProjectContentComponent.h"
|
||||
#include "jucer_Module.h"
|
||||
#include "../Application/jucer_MainWindow.h"
|
||||
#include "../Application/jucer_Application.h"
|
||||
#include "../Code Editor/jucer_SourceCodeEditor.h"
|
||||
#include "../Project Saving/jucer_ProjectExporter.h"
|
||||
#include "../Utility/jucer_TranslationTool.h"
|
||||
#include "../Utility/jucer_JucerTreeViewBase.h"
|
||||
#include "../Wizards/jucer_NewFileWizard.h"
|
||||
#include "jucer_GroupInformationComponent.h"
|
||||
|
||||
//==============================================================================
|
||||
class FileTreePanel : public TreePanelBase
|
||||
{
|
||||
public:
|
||||
FileTreePanel (Project& p)
|
||||
: TreePanelBase (&p, "fileTreeState")
|
||||
{
|
||||
tree.setMultiSelectEnabled (true);
|
||||
setRoot (new GroupItem (p.getMainGroup()));
|
||||
}
|
||||
|
||||
void updateMissingFileStatuses()
|
||||
{
|
||||
if (ProjectTreeItemBase* p = dynamic_cast<ProjectTreeItemBase*> (rootItem.get()))
|
||||
p->checkFileStatus();
|
||||
}
|
||||
|
||||
#include "jucer_ProjectTree_Base.h"
|
||||
#include "jucer_ProjectTree_Group.h"
|
||||
#include "jucer_ProjectTree_File.h"
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
class ConfigTreePanel : public TreePanelBase
|
||||
{
|
||||
public:
|
||||
ConfigTreePanel (Project& p)
|
||||
: TreePanelBase (&p, "settingsTreeState")
|
||||
{
|
||||
tree.setMultiSelectEnabled (false);
|
||||
setRoot (new RootItem (p));
|
||||
|
||||
if (tree.getNumSelectedItems() == 0)
|
||||
tree.getRootItem()->setSelected (true, true);
|
||||
|
||||
#if JUCE_MAC || JUCE_WINDOWS
|
||||
ApplicationCommandManager& commandManager = IntrojucerApp::getCommandManager();
|
||||
|
||||
addAndMakeVisible (openProjectButton);
|
||||
openProjectButton.setCommandToTrigger (&commandManager, CommandIDs::openInIDE, true);
|
||||
openProjectButton.setButtonText (commandManager.getNameOfCommand (CommandIDs::openInIDE));
|
||||
openProjectButton.setColour (TextButton::buttonColourId, Colours::white.withAlpha (0.5f));
|
||||
|
||||
addAndMakeVisible (saveAndOpenButton);
|
||||
saveAndOpenButton.setCommandToTrigger (&commandManager, CommandIDs::saveAndOpenInIDE, true);
|
||||
saveAndOpenButton.setButtonText (commandManager.getNameOfCommand (CommandIDs::saveAndOpenInIDE));
|
||||
saveAndOpenButton.setColour (TextButton::buttonColourId, Colours::white.withAlpha (0.5f));
|
||||
#endif
|
||||
}
|
||||
|
||||
void resized()
|
||||
{
|
||||
Rectangle<int> r (getAvailableBounds());
|
||||
r.removeFromBottom (6);
|
||||
|
||||
if (saveAndOpenButton.isVisible())
|
||||
saveAndOpenButton.setBounds (r.removeFromBottom (30).reduced (16, 4));
|
||||
|
||||
if (openProjectButton.isVisible())
|
||||
openProjectButton.setBounds (r.removeFromBottom (30).reduced (16, 4));
|
||||
|
||||
tree.setBounds (r);
|
||||
}
|
||||
|
||||
void showProjectSettings()
|
||||
{
|
||||
if (ConfigTreeItemBase* root = dynamic_cast<ConfigTreeItemBase*> (rootItem.get()))
|
||||
if (root->isProjectSettings())
|
||||
root->setSelected (true, true);
|
||||
}
|
||||
|
||||
void showModules()
|
||||
{
|
||||
if (ConfigTreeItemBase* mods = getModulesItem())
|
||||
mods->setSelected (true, true);
|
||||
}
|
||||
|
||||
void showModule (const String& moduleID)
|
||||
{
|
||||
if (ConfigTreeItemBase* mods = getModulesItem())
|
||||
{
|
||||
mods->setOpen (true);
|
||||
|
||||
for (int i = mods->getNumSubItems(); --i >= 0;)
|
||||
if (ModuleItem* m = dynamic_cast<ModuleItem*> (mods->getSubItem (i)))
|
||||
if (m->moduleID == moduleID)
|
||||
m->setSelected (true, true);
|
||||
}
|
||||
}
|
||||
|
||||
TextButton openProjectButton, saveAndOpenButton;
|
||||
|
||||
private:
|
||||
#include "jucer_ConfigTree_Base.h"
|
||||
#include "jucer_ConfigTree_Modules.h"
|
||||
#include "jucer_ConfigTree_Exporter.h"
|
||||
#include "jucer_ModulesPanel.h"
|
||||
|
||||
ConfigTreeItemBase* getModulesItem()
|
||||
{
|
||||
if (ConfigTreeItemBase* root = dynamic_cast<ConfigTreeItemBase*> (rootItem.get()))
|
||||
if (root->isProjectSettings())
|
||||
if (ConfigTreeItemBase* mods = dynamic_cast<ConfigTreeItemBase*> (root->getSubItem (0)))
|
||||
if (mods->isModulesList())
|
||||
return mods;
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
struct LogoComponent : public Component
|
||||
{
|
||||
void paint (Graphics& g)
|
||||
{
|
||||
g.setColour (findColour (mainBackgroundColourId).contrasting (0.3f));
|
||||
|
||||
Rectangle<int> r (getLocalBounds());
|
||||
|
||||
g.setFont (15.0f);
|
||||
g.drawFittedText (getVersionInfo(), r.removeFromBottom (30), Justification::centred, 2);
|
||||
|
||||
const Path& logo = getIcons().mainJuceLogo;
|
||||
g.fillPath (logo, RectanglePlacement (RectanglePlacement::centred)
|
||||
.getTransformToFit (logo.getBounds(), r.toFloat()));
|
||||
}
|
||||
|
||||
static String getVersionInfo()
|
||||
{
|
||||
const Time buildDate (Time::getCompilationDate());
|
||||
|
||||
String s;
|
||||
|
||||
s << SystemStats::getJUCEVersion() << newLine
|
||||
<< "Introjucer built: " << buildDate.getDayOfMonth()
|
||||
<< " " << Time::getMonthName (buildDate.getMonth(), true)
|
||||
<< " " << buildDate.getYear();
|
||||
|
||||
return s;
|
||||
}
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
ProjectContentComponent::ProjectContentComponent()
|
||||
: project (nullptr),
|
||||
currentDocument (nullptr),
|
||||
treeViewTabs (TabbedButtonBar::TabsAtTop)
|
||||
{
|
||||
setOpaque (true);
|
||||
setWantsKeyboardFocus (true);
|
||||
|
||||
addAndMakeVisible (logo = new LogoComponent());
|
||||
|
||||
treeSizeConstrainer.setMinimumWidth (200);
|
||||
treeSizeConstrainer.setMaximumWidth (500);
|
||||
|
||||
treeViewTabs.setOutline (0);
|
||||
treeViewTabs.getTabbedButtonBar().setMinimumTabScaleFactor (0.3);
|
||||
|
||||
IntrojucerApp::getApp().openDocumentManager.addListener (this);
|
||||
}
|
||||
|
||||
ProjectContentComponent::~ProjectContentComponent()
|
||||
{
|
||||
IntrojucerApp::getApp().openDocumentManager.removeListener (this);
|
||||
|
||||
logo = nullptr;
|
||||
setProject (nullptr);
|
||||
contentView = nullptr;
|
||||
removeChildComponent (&bubbleMessage);
|
||||
jassert (getNumChildComponents() <= 1);
|
||||
}
|
||||
|
||||
void ProjectContentComponent::paint (Graphics& g)
|
||||
{
|
||||
IntrojucerLookAndFeel::fillWithBackgroundTexture (*this, g);
|
||||
}
|
||||
|
||||
void ProjectContentComponent::paintOverChildren (Graphics& g)
|
||||
{
|
||||
if (resizerBar != nullptr)
|
||||
{
|
||||
const int shadowSize = 15;
|
||||
const int x = resizerBar->getX();
|
||||
|
||||
ColourGradient cg (Colours::black.withAlpha (0.25f), (float) x, 0,
|
||||
Colours::transparentBlack, (float) (x - shadowSize), 0, false);
|
||||
cg.addColour (0.4, Colours::black.withAlpha (0.07f));
|
||||
cg.addColour (0.6, Colours::black.withAlpha (0.02f));
|
||||
|
||||
g.setGradientFill (cg);
|
||||
g.fillRect (x - shadowSize, 0, shadowSize, getHeight());
|
||||
}
|
||||
}
|
||||
|
||||
void ProjectContentComponent::resized()
|
||||
{
|
||||
Rectangle<int> r (getLocalBounds());
|
||||
|
||||
if (treeViewTabs.isVisible())
|
||||
treeViewTabs.setBounds (r.removeFromLeft (treeViewTabs.getWidth()));
|
||||
|
||||
if (resizerBar != nullptr)
|
||||
resizerBar->setBounds (r.withWidth (4));
|
||||
|
||||
if (contentView != nullptr)
|
||||
contentView->setBounds (r);
|
||||
|
||||
if (logo != nullptr)
|
||||
logo->setBounds (r.reduced (r.getWidth() / 4, r.getHeight() / 4));
|
||||
}
|
||||
|
||||
void ProjectContentComponent::lookAndFeelChanged()
|
||||
{
|
||||
repaint();
|
||||
}
|
||||
|
||||
void ProjectContentComponent::childBoundsChanged (Component* child)
|
||||
{
|
||||
if (child == &treeViewTabs)
|
||||
resized();
|
||||
}
|
||||
|
||||
void ProjectContentComponent::setProject (Project* newProject)
|
||||
{
|
||||
if (project != newProject)
|
||||
{
|
||||
if (project != nullptr)
|
||||
project->removeChangeListener (this);
|
||||
|
||||
contentView = nullptr;
|
||||
resizerBar = nullptr;
|
||||
|
||||
deleteProjectTabs();
|
||||
project = newProject;
|
||||
rebuildProjectTabs();
|
||||
}
|
||||
}
|
||||
|
||||
void ProjectContentComponent::rebuildProjectTabs()
|
||||
{
|
||||
deleteProjectTabs();
|
||||
|
||||
if (project != nullptr)
|
||||
{
|
||||
addAndMakeVisible (treeViewTabs);
|
||||
|
||||
createProjectTabs();
|
||||
|
||||
PropertiesFile& settings = project->getStoredProperties();
|
||||
|
||||
const String lastTabName (settings.getValue ("lastTab"));
|
||||
int lastTabIndex = treeViewTabs.getTabNames().indexOf (lastTabName);
|
||||
|
||||
if (lastTabIndex < 0 || lastTabIndex > treeViewTabs.getNumTabs())
|
||||
lastTabIndex = 1;
|
||||
|
||||
treeViewTabs.setCurrentTabIndex (lastTabIndex);
|
||||
|
||||
int lastTreeWidth = settings.getValue ("projectPanelWidth").getIntValue();
|
||||
if (lastTreeWidth < 150)
|
||||
lastTreeWidth = 240;
|
||||
|
||||
treeViewTabs.setBounds (0, 0, lastTreeWidth, getHeight());
|
||||
|
||||
addAndMakeVisible (resizerBar = new ResizableEdgeComponent (&treeViewTabs, &treeSizeConstrainer,
|
||||
ResizableEdgeComponent::rightEdge));
|
||||
resizerBar->setAlwaysOnTop (true);
|
||||
|
||||
project->addChangeListener (this);
|
||||
|
||||
updateMissingFileStatuses();
|
||||
}
|
||||
else
|
||||
{
|
||||
treeViewTabs.setVisible (false);
|
||||
}
|
||||
|
||||
resized();
|
||||
}
|
||||
|
||||
void ProjectContentComponent::createProjectTabs()
|
||||
{
|
||||
jassert (project != nullptr);
|
||||
const Colour tabColour (Colours::transparentBlack);
|
||||
|
||||
treeViewTabs.addTab ("Files", tabColour, new FileTreePanel (*project), true);
|
||||
treeViewTabs.addTab ("Config", tabColour, new ConfigTreePanel (*project), true);
|
||||
}
|
||||
|
||||
void ProjectContentComponent::deleteProjectTabs()
|
||||
{
|
||||
if (project != nullptr && treeViewTabs.isShowing())
|
||||
{
|
||||
PropertiesFile& settings = project->getStoredProperties();
|
||||
|
||||
if (treeViewTabs.getWidth() > 0)
|
||||
settings.setValue ("projectPanelWidth", treeViewTabs.getWidth());
|
||||
|
||||
if (treeViewTabs.getNumTabs() > 0)
|
||||
settings.setValue ("lastTab", treeViewTabs.getCurrentTabName());
|
||||
}
|
||||
|
||||
treeViewTabs.clearTabs();
|
||||
}
|
||||
|
||||
void ProjectContentComponent::saveTreeViewState()
|
||||
{
|
||||
for (int i = treeViewTabs.getNumTabs(); --i >= 0;)
|
||||
if (TreePanelBase* t = dynamic_cast<TreePanelBase*> (treeViewTabs.getTabContentComponent (i)))
|
||||
t->saveOpenness();
|
||||
}
|
||||
|
||||
void ProjectContentComponent::saveOpenDocumentList()
|
||||
{
|
||||
if (project != nullptr)
|
||||
{
|
||||
ScopedPointer<XmlElement> xml (recentDocumentList.createXML());
|
||||
|
||||
if (xml != nullptr)
|
||||
project->getStoredProperties().setValue ("lastDocs", xml);
|
||||
}
|
||||
}
|
||||
|
||||
void ProjectContentComponent::reloadLastOpenDocuments()
|
||||
{
|
||||
if (project != nullptr)
|
||||
{
|
||||
ScopedPointer<XmlElement> xml (project->getStoredProperties().getXmlValue ("lastDocs"));
|
||||
|
||||
if (xml != nullptr)
|
||||
{
|
||||
recentDocumentList.restoreFromXML (*project, *xml);
|
||||
showDocument (recentDocumentList.getCurrentDocument(), true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool ProjectContentComponent::documentAboutToClose (OpenDocumentManager::Document* document)
|
||||
{
|
||||
hideDocument (document);
|
||||
return true;
|
||||
}
|
||||
|
||||
void ProjectContentComponent::changeListenerCallback (ChangeBroadcaster*)
|
||||
{
|
||||
updateMissingFileStatuses();
|
||||
}
|
||||
|
||||
void ProjectContentComponent::updateMissingFileStatuses()
|
||||
{
|
||||
if (FileTreePanel* tree = dynamic_cast<FileTreePanel*> (treeViewTabs.getTabContentComponent (0)))
|
||||
tree->updateMissingFileStatuses();
|
||||
}
|
||||
|
||||
bool ProjectContentComponent::showEditorForFile (const File& f, bool grabFocus)
|
||||
{
|
||||
return getCurrentFile() == f
|
||||
|| showDocument (IntrojucerApp::getApp().openDocumentManager.openFile (project, f), grabFocus);
|
||||
}
|
||||
|
||||
File ProjectContentComponent::getCurrentFile() const
|
||||
{
|
||||
return currentDocument != nullptr ? currentDocument->getFile()
|
||||
: File::nonexistent;
|
||||
}
|
||||
|
||||
bool ProjectContentComponent::showDocument (OpenDocumentManager::Document* doc, bool grabFocus)
|
||||
{
|
||||
if (doc == nullptr)
|
||||
return false;
|
||||
|
||||
if (doc->hasFileBeenModifiedExternally())
|
||||
doc->reloadFromFile();
|
||||
|
||||
if (doc == getCurrentDocument() && contentView != nullptr)
|
||||
{
|
||||
if (grabFocus)
|
||||
contentView->grabKeyboardFocus();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
recentDocumentList.newDocumentOpened (doc);
|
||||
|
||||
bool opened = setEditorComponent (doc->createEditor(), doc);
|
||||
|
||||
if (opened && grabFocus)
|
||||
contentView->grabKeyboardFocus();
|
||||
|
||||
return opened;
|
||||
}
|
||||
|
||||
void ProjectContentComponent::hideEditor()
|
||||
{
|
||||
currentDocument = nullptr;
|
||||
contentView = nullptr;
|
||||
updateMainWindowTitle();
|
||||
IntrojucerApp::getCommandManager().commandStatusChanged();
|
||||
resized();
|
||||
}
|
||||
|
||||
void ProjectContentComponent::hideDocument (OpenDocumentManager::Document* doc)
|
||||
{
|
||||
if (doc == currentDocument)
|
||||
{
|
||||
if (OpenDocumentManager::Document* replacement = recentDocumentList.getClosestPreviousDocOtherThan (doc))
|
||||
showDocument (replacement, true);
|
||||
else
|
||||
hideEditor();
|
||||
}
|
||||
}
|
||||
|
||||
bool ProjectContentComponent::setEditorComponent (Component* editor,
|
||||
OpenDocumentManager::Document* doc)
|
||||
{
|
||||
if (editor != nullptr)
|
||||
{
|
||||
contentView = nullptr;
|
||||
contentView = editor;
|
||||
currentDocument = doc;
|
||||
addAndMakeVisible (editor);
|
||||
resized();
|
||||
|
||||
updateMainWindowTitle();
|
||||
IntrojucerApp::getCommandManager().commandStatusChanged();
|
||||
return true;
|
||||
}
|
||||
|
||||
updateMainWindowTitle();
|
||||
return false;
|
||||
}
|
||||
|
||||
void ProjectContentComponent::closeDocument()
|
||||
{
|
||||
if (currentDocument != nullptr)
|
||||
IntrojucerApp::getApp().openDocumentManager.closeDocument (currentDocument, true);
|
||||
else if (contentView != nullptr)
|
||||
if (! goToPreviousFile())
|
||||
hideEditor();
|
||||
}
|
||||
|
||||
static void showSaveWarning (OpenDocumentManager::Document* currentDocument)
|
||||
{
|
||||
AlertWindow::showMessageBox (AlertWindow::WarningIcon,
|
||||
TRANS("Save failed!"),
|
||||
TRANS("Couldn't save the file:")
|
||||
+ "\n" + currentDocument->getFile().getFullPathName());
|
||||
}
|
||||
|
||||
void ProjectContentComponent::saveDocument()
|
||||
{
|
||||
if (currentDocument != nullptr)
|
||||
{
|
||||
if (! currentDocument->save())
|
||||
showSaveWarning (currentDocument);
|
||||
}
|
||||
else
|
||||
saveProject();
|
||||
|
||||
updateMainWindowTitle();
|
||||
}
|
||||
|
||||
void ProjectContentComponent::saveAs()
|
||||
{
|
||||
if (currentDocument != nullptr && ! currentDocument->saveAs())
|
||||
showSaveWarning (currentDocument);
|
||||
}
|
||||
|
||||
bool ProjectContentComponent::goToPreviousFile()
|
||||
{
|
||||
OpenDocumentManager::Document* doc = recentDocumentList.getCurrentDocument();
|
||||
|
||||
if (doc == nullptr || doc == getCurrentDocument())
|
||||
doc = recentDocumentList.getPrevious();
|
||||
|
||||
return showDocument (doc, true);
|
||||
}
|
||||
|
||||
bool ProjectContentComponent::goToNextFile()
|
||||
{
|
||||
return showDocument (recentDocumentList.getNext(), true);
|
||||
}
|
||||
|
||||
bool ProjectContentComponent::canGoToCounterpart() const
|
||||
{
|
||||
return currentDocument != nullptr
|
||||
&& currentDocument->getCounterpartFile().exists();
|
||||
}
|
||||
|
||||
bool ProjectContentComponent::goToCounterpart()
|
||||
{
|
||||
if (currentDocument != nullptr)
|
||||
{
|
||||
const File file (currentDocument->getCounterpartFile());
|
||||
|
||||
if (file.exists())
|
||||
return showEditorForFile (file, true);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ProjectContentComponent::saveProject()
|
||||
{
|
||||
return project != nullptr
|
||||
&& project->save (true, true) == FileBasedDocument::savedOk;
|
||||
}
|
||||
|
||||
void ProjectContentComponent::closeProject()
|
||||
{
|
||||
if (MainWindow* const mw = findParentComponentOfClass<MainWindow>())
|
||||
mw->closeCurrentProject();
|
||||
}
|
||||
|
||||
void ProjectContentComponent::showFilesTab()
|
||||
{
|
||||
treeViewTabs.setCurrentTabIndex (0);
|
||||
}
|
||||
|
||||
void ProjectContentComponent::showConfigTab()
|
||||
{
|
||||
treeViewTabs.setCurrentTabIndex (1);
|
||||
}
|
||||
|
||||
void ProjectContentComponent::showProjectSettings()
|
||||
{
|
||||
showConfigTab();
|
||||
|
||||
if (ConfigTreePanel* const tree = dynamic_cast<ConfigTreePanel*> (treeViewTabs.getCurrentContentComponent()))
|
||||
tree->showProjectSettings();
|
||||
}
|
||||
|
||||
void ProjectContentComponent::showModules()
|
||||
{
|
||||
showConfigTab();
|
||||
|
||||
if (ConfigTreePanel* const tree = dynamic_cast<ConfigTreePanel*> (treeViewTabs.getCurrentContentComponent()))
|
||||
tree->showModules();
|
||||
}
|
||||
|
||||
void ProjectContentComponent::showModule (const String& moduleID)
|
||||
{
|
||||
showConfigTab();
|
||||
|
||||
if (ConfigTreePanel* const tree = dynamic_cast<ConfigTreePanel*> (treeViewTabs.getCurrentContentComponent()))
|
||||
tree->showModule (moduleID);
|
||||
}
|
||||
|
||||
StringArray ProjectContentComponent::getExportersWhichCanLaunch() const
|
||||
{
|
||||
StringArray s;
|
||||
|
||||
if (project != nullptr)
|
||||
for (Project::ExporterIterator exporter (*project); exporter.next();)
|
||||
if (exporter->canLaunchProject())
|
||||
s.add (exporter->getName());
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
void ProjectContentComponent::openInIDE (int exporterIndex)
|
||||
{
|
||||
int i = 0;
|
||||
|
||||
if (project != nullptr)
|
||||
for (Project::ExporterIterator exporter (*project); exporter.next();)
|
||||
if (exporter->canLaunchProject())
|
||||
if (i++ == exporterIndex && exporter->launchProject())
|
||||
break;
|
||||
}
|
||||
|
||||
static void openIDEMenuCallback (int result, ProjectContentComponent* comp)
|
||||
{
|
||||
if (comp != nullptr && result > 0)
|
||||
comp->openInIDE (result - 1);
|
||||
}
|
||||
|
||||
void ProjectContentComponent::openInIDE()
|
||||
{
|
||||
if (project != nullptr)
|
||||
{
|
||||
StringArray possibleExporters = getExportersWhichCanLaunch();
|
||||
|
||||
if (possibleExporters.size() > 1)
|
||||
{
|
||||
PopupMenu menu;
|
||||
|
||||
for (int i = 0; i < possibleExporters.size(); ++i)
|
||||
menu.addItem (i + 1, possibleExporters[i]);
|
||||
|
||||
menu.showMenuAsync (PopupMenu::Options(),
|
||||
ModalCallbackFunction::forComponent (openIDEMenuCallback, this));
|
||||
}
|
||||
else
|
||||
{
|
||||
openInIDE (0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ProjectContentComponent::deleteSelectedTreeItems()
|
||||
{
|
||||
if (TreePanelBase* const tree = dynamic_cast<TreePanelBase*> (treeViewTabs.getCurrentContentComponent()))
|
||||
tree->deleteSelectedItems();
|
||||
}
|
||||
|
||||
void ProjectContentComponent::updateMainWindowTitle()
|
||||
{
|
||||
if (MainWindow* mw = findParentComponentOfClass<MainWindow>())
|
||||
{
|
||||
String title;
|
||||
File file;
|
||||
bool edited = false;
|
||||
|
||||
if (currentDocument != nullptr)
|
||||
{
|
||||
title = currentDocument->getName();
|
||||
edited = currentDocument->needsSaving();
|
||||
file = currentDocument->getFile();
|
||||
}
|
||||
|
||||
if (ComponentPeer* peer = mw->getPeer())
|
||||
{
|
||||
if (! peer->setDocumentEditedStatus (edited))
|
||||
if (edited)
|
||||
title << "*";
|
||||
|
||||
peer->setRepresentedFile (file);
|
||||
}
|
||||
|
||||
mw->updateTitle (title);
|
||||
}
|
||||
}
|
||||
|
||||
void ProjectContentComponent::showBubbleMessage (const Rectangle<int>& pos, const String& text)
|
||||
{
|
||||
addChildComponent (bubbleMessage);
|
||||
bubbleMessage.setColour (BubbleComponent::backgroundColourId, Colours::white.withAlpha (0.7f));
|
||||
bubbleMessage.setColour (BubbleComponent::outlineColourId, Colours::black.withAlpha (0.8f));
|
||||
bubbleMessage.setAlwaysOnTop (true);
|
||||
|
||||
bubbleMessage.showAt (pos, AttributedString (text), 3000, true, false);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void ProjectContentComponent::showTranslationTool()
|
||||
{
|
||||
if (translationTool != nullptr)
|
||||
{
|
||||
translationTool->toFront (true);
|
||||
}
|
||||
else if (project != nullptr)
|
||||
{
|
||||
new FloatingToolWindow ("Translation File Builder",
|
||||
"transToolWindowPos",
|
||||
new TranslationToolComponent(),
|
||||
translationTool,
|
||||
600, 700,
|
||||
600, 400, 10000, 10000);
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
ApplicationCommandTarget* ProjectContentComponent::getNextCommandTarget()
|
||||
{
|
||||
return findFirstTargetParentComponent();
|
||||
}
|
||||
|
||||
void ProjectContentComponent::getAllCommands (Array <CommandID>& commands)
|
||||
{
|
||||
const CommandID ids[] = { CommandIDs::saveDocument,
|
||||
CommandIDs::saveDocumentAs,
|
||||
CommandIDs::closeDocument,
|
||||
CommandIDs::saveProject,
|
||||
CommandIDs::closeProject,
|
||||
CommandIDs::openInIDE,
|
||||
CommandIDs::saveAndOpenInIDE,
|
||||
CommandIDs::showFilePanel,
|
||||
CommandIDs::showConfigPanel,
|
||||
CommandIDs::showProjectSettings,
|
||||
CommandIDs::showProjectModules,
|
||||
CommandIDs::goToPreviousDoc,
|
||||
CommandIDs::goToNextDoc,
|
||||
CommandIDs::goToCounterpart,
|
||||
CommandIDs::deleteSelectedItem,
|
||||
CommandIDs::showTranslationTool };
|
||||
|
||||
commands.addArray (ids, numElementsInArray (ids));
|
||||
}
|
||||
|
||||
void ProjectContentComponent::getCommandInfo (const CommandID commandID, ApplicationCommandInfo& result)
|
||||
{
|
||||
String documentName;
|
||||
if (currentDocument != nullptr)
|
||||
documentName = " '" + currentDocument->getName().substring (0, 32) + "'";
|
||||
|
||||
#if JUCE_MAC
|
||||
const ModifierKeys cmdCtrl (ModifierKeys::ctrlModifier | ModifierKeys::commandModifier);
|
||||
#else
|
||||
const ModifierKeys cmdCtrl (ModifierKeys::ctrlModifier | ModifierKeys::altModifier);
|
||||
#endif
|
||||
|
||||
switch (commandID)
|
||||
{
|
||||
case CommandIDs::saveProject:
|
||||
result.setInfo ("Save Project",
|
||||
"Saves the current project",
|
||||
CommandCategories::general, 0);
|
||||
result.setActive (project != nullptr);
|
||||
break;
|
||||
|
||||
case CommandIDs::closeProject:
|
||||
result.setInfo ("Close Project",
|
||||
"Closes the current project",
|
||||
CommandCategories::general, 0);
|
||||
result.setActive (project != nullptr);
|
||||
break;
|
||||
|
||||
case CommandIDs::saveDocument:
|
||||
result.setInfo ("Save" + documentName,
|
||||
"Saves the current document",
|
||||
CommandCategories::general, 0);
|
||||
result.setActive (currentDocument != nullptr || project != nullptr);
|
||||
result.defaultKeypresses.add (KeyPress ('s', ModifierKeys::commandModifier, 0));
|
||||
break;
|
||||
|
||||
case CommandIDs::saveDocumentAs:
|
||||
result.setInfo ("Save As...",
|
||||
"Saves the current document to a new location",
|
||||
CommandCategories::general, 0);
|
||||
result.setActive (currentDocument != nullptr || project != nullptr);
|
||||
result.defaultKeypresses.add (KeyPress ('s', ModifierKeys::commandModifier | ModifierKeys::shiftModifier, 0));
|
||||
break;
|
||||
|
||||
case CommandIDs::closeDocument:
|
||||
result.setInfo ("Close" + documentName,
|
||||
"Closes the current document",
|
||||
CommandCategories::general, 0);
|
||||
result.setActive (contentView != nullptr);
|
||||
result.defaultKeypresses.add (KeyPress ('w', cmdCtrl, 0));
|
||||
break;
|
||||
|
||||
case CommandIDs::goToPreviousDoc:
|
||||
result.setInfo ("Previous Document", "Go to previous document", CommandCategories::general, 0);
|
||||
result.setActive (recentDocumentList.canGoToPrevious());
|
||||
result.defaultKeypresses.add (KeyPress (KeyPress::leftKey, cmdCtrl, 0));
|
||||
break;
|
||||
|
||||
case CommandIDs::goToNextDoc:
|
||||
result.setInfo ("Next Document", "Go to next document", CommandCategories::general, 0);
|
||||
result.setActive (recentDocumentList.canGoToNext());
|
||||
result.defaultKeypresses.add (KeyPress (KeyPress::rightKey, cmdCtrl, 0));
|
||||
break;
|
||||
|
||||
case CommandIDs::goToCounterpart:
|
||||
result.setInfo ("Open corresponding header or cpp file", "Open counterpart file", CommandCategories::general, 0);
|
||||
result.setActive (canGoToCounterpart());
|
||||
result.defaultKeypresses.add (KeyPress (KeyPress::upKey, cmdCtrl, 0));
|
||||
break;
|
||||
|
||||
case CommandIDs::openInIDE:
|
||||
#if JUCE_MAC
|
||||
result.setInfo ("Open in Xcode...",
|
||||
#elif JUCE_WINDOWS
|
||||
result.setInfo ("Open in Visual Studio...",
|
||||
#else
|
||||
result.setInfo ("Open as a Makefile...",
|
||||
#endif
|
||||
"Launches the project in an external IDE",
|
||||
CommandCategories::general, 0);
|
||||
result.setActive (ProjectExporter::canProjectBeLaunched (project));
|
||||
break;
|
||||
|
||||
case CommandIDs::saveAndOpenInIDE:
|
||||
#if JUCE_MAC
|
||||
result.setInfo ("Save Project and Open in Xcode...",
|
||||
#elif JUCE_WINDOWS
|
||||
result.setInfo ("Save Project and Open in Visual Studio...",
|
||||
#else
|
||||
result.setInfo ("Save Project and Open as a Makefile...",
|
||||
#endif
|
||||
"Saves the project and launches it in an external IDE",
|
||||
CommandCategories::general, 0);
|
||||
result.setActive (ProjectExporter::canProjectBeLaunched (project));
|
||||
result.defaultKeypresses.add (KeyPress ('l', ModifierKeys::commandModifier | ModifierKeys::shiftModifier, 0));
|
||||
break;
|
||||
|
||||
case CommandIDs::showFilePanel:
|
||||
result.setInfo ("Show File Panel",
|
||||
"Shows the tree of files for this project",
|
||||
CommandCategories::general, 0);
|
||||
result.setActive (project != nullptr);
|
||||
result.defaultKeypresses.add (KeyPress ('p', ModifierKeys::commandModifier, 0));
|
||||
break;
|
||||
|
||||
case CommandIDs::showConfigPanel:
|
||||
result.setInfo ("Show Config Panel",
|
||||
"Shows the build options for the project",
|
||||
CommandCategories::general, 0);
|
||||
result.setActive (project != nullptr);
|
||||
result.defaultKeypresses.add (KeyPress ('i', ModifierKeys::commandModifier, 0));
|
||||
break;
|
||||
|
||||
case CommandIDs::showProjectSettings:
|
||||
result.setInfo ("Show Project Settings",
|
||||
"Shows the main project options page",
|
||||
CommandCategories::general, 0);
|
||||
result.setActive (project != nullptr);
|
||||
result.defaultKeypresses.add (KeyPress ('i', ModifierKeys::commandModifier | ModifierKeys::shiftModifier, 0));
|
||||
break;
|
||||
|
||||
case CommandIDs::showProjectModules:
|
||||
result.setInfo ("Show Project Modules",
|
||||
"Shows the project's list of modules",
|
||||
CommandCategories::general, 0);
|
||||
result.setActive (project != nullptr);
|
||||
result.defaultKeypresses.add (KeyPress ('m', ModifierKeys::commandModifier | ModifierKeys::shiftModifier, 0));
|
||||
break;
|
||||
|
||||
case CommandIDs::deleteSelectedItem:
|
||||
result.setInfo ("Delete Selected File", String::empty, CommandCategories::general, 0);
|
||||
result.defaultKeypresses.add (KeyPress (KeyPress::deleteKey, 0, 0));
|
||||
result.defaultKeypresses.add (KeyPress (KeyPress::backspaceKey, 0, 0));
|
||||
result.setActive (dynamic_cast<TreePanelBase*> (treeViewTabs.getCurrentContentComponent()) != nullptr);
|
||||
break;
|
||||
|
||||
case CommandIDs::showTranslationTool:
|
||||
result.setInfo ("Translation File Builder", "Shows the translation file helper tool", CommandCategories::general, 0);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool ProjectContentComponent::perform (const InvocationInfo& info)
|
||||
{
|
||||
switch (info.commandID)
|
||||
{
|
||||
case CommandIDs::saveProject:
|
||||
case CommandIDs::closeProject:
|
||||
case CommandIDs::saveDocument:
|
||||
case CommandIDs::saveDocumentAs:
|
||||
case CommandIDs::closeDocument:
|
||||
case CommandIDs::goToPreviousDoc:
|
||||
case CommandIDs::goToNextDoc:
|
||||
case CommandIDs::goToCounterpart:
|
||||
case CommandIDs::saveAndOpenInIDE:
|
||||
if (reinvokeCommandAfterCancellingModalComps (info))
|
||||
{
|
||||
grabKeyboardFocus(); // to force any open labels to close their text editors
|
||||
return true;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
switch (info.commandID)
|
||||
{
|
||||
case CommandIDs::saveProject: saveProject(); break;
|
||||
case CommandIDs::closeProject: closeProject(); break;
|
||||
case CommandIDs::saveDocument: saveDocument(); break;
|
||||
case CommandIDs::saveDocumentAs: saveAs(); break;
|
||||
|
||||
case CommandIDs::closeDocument: closeDocument(); break;
|
||||
case CommandIDs::goToPreviousDoc: goToPreviousFile(); break;
|
||||
case CommandIDs::goToNextDoc: goToNextFile(); break;
|
||||
case CommandIDs::goToCounterpart: goToCounterpart(); break;
|
||||
|
||||
case CommandIDs::showFilePanel: showFilesTab(); break;
|
||||
case CommandIDs::showConfigPanel: showConfigTab(); break;
|
||||
case CommandIDs::showProjectSettings: showProjectSettings(); break;
|
||||
case CommandIDs::showProjectModules: showModules(); break;
|
||||
|
||||
case CommandIDs::openInIDE: openInIDE(); break;
|
||||
|
||||
case CommandIDs::deleteSelectedItem: deleteSelectedTreeItems(); break;
|
||||
|
||||
case CommandIDs::saveAndOpenInIDE:
|
||||
if (saveProject())
|
||||
openInIDE();
|
||||
|
||||
break;
|
||||
|
||||
case CommandIDs::showTranslationTool: showTranslationTool(); break;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ProjectContentComponent::getSelectedProjectItemsBeingDragged (const DragAndDropTarget::SourceDetails& dragSourceDetails,
|
||||
OwnedArray<Project::Item>& selectedNodes)
|
||||
{
|
||||
FileTreePanel::ProjectTreeItemBase::getSelectedProjectItemsBeingDragged (dragSourceDetails, selectedNodes);
|
||||
}
|
||||
@@ -0,0 +1,132 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
|
||||
Permission is granted to use this software under the terms of either:
|
||||
a) the GPL v2 (or any later version)
|
||||
b) the Affero GPL v3
|
||||
|
||||
Details of these licenses can be found at: www.gnu.org/licenses
|
||||
|
||||
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.juce.com for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#ifndef __JUCER_PROJECTCONTENTCOMPONENT_JUCEHEADER__
|
||||
#define __JUCER_PROJECTCONTENTCOMPONENT_JUCEHEADER__
|
||||
|
||||
#include "jucer_Project.h"
|
||||
#include "../Application/jucer_OpenDocumentManager.h"
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
*/
|
||||
class ProjectContentComponent : public Component,
|
||||
public ApplicationCommandTarget,
|
||||
private ChangeListener,
|
||||
private OpenDocumentManager::DocumentCloseListener
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
ProjectContentComponent();
|
||||
~ProjectContentComponent();
|
||||
|
||||
Project* getProject() const noexcept { return project; }
|
||||
virtual void setProject (Project* project);
|
||||
|
||||
void saveTreeViewState();
|
||||
void saveOpenDocumentList();
|
||||
void reloadLastOpenDocuments();
|
||||
|
||||
bool showEditorForFile (const File& f, bool grabFocus);
|
||||
File getCurrentFile() const;
|
||||
|
||||
bool showDocument (OpenDocumentManager::Document* doc, bool grabFocus);
|
||||
void hideDocument (OpenDocumentManager::Document* doc);
|
||||
OpenDocumentManager::Document* getCurrentDocument() const { return currentDocument; }
|
||||
void closeDocument();
|
||||
void saveDocument();
|
||||
void saveAs();
|
||||
|
||||
void hideEditor();
|
||||
bool setEditorComponent (Component* editor, OpenDocumentManager::Document* doc);
|
||||
Component* getEditorComponent() const { return contentView; }
|
||||
|
||||
bool goToPreviousFile();
|
||||
bool goToNextFile();
|
||||
bool canGoToCounterpart() const;
|
||||
bool goToCounterpart();
|
||||
|
||||
bool saveProject();
|
||||
void closeProject();
|
||||
void openInIDE();
|
||||
void openInIDE (int exporterIndex);
|
||||
|
||||
void showFilesTab();
|
||||
void showConfigTab();
|
||||
void showProjectSettings();
|
||||
void showModules();
|
||||
void showModule (const String& moduleID);
|
||||
|
||||
void deleteSelectedTreeItems();
|
||||
|
||||
void updateMainWindowTitle();
|
||||
|
||||
void updateMissingFileStatuses();
|
||||
virtual void createProjectTabs();
|
||||
virtual void deleteProjectTabs();
|
||||
void rebuildProjectTabs();
|
||||
|
||||
void showBubbleMessage (const Rectangle<int>&, const String&);
|
||||
|
||||
StringArray getExportersWhichCanLaunch() const;
|
||||
|
||||
static void getSelectedProjectItemsBeingDragged (const DragAndDropTarget::SourceDetails& dragSourceDetails,
|
||||
OwnedArray<Project::Item>& selectedNodes);
|
||||
|
||||
//==============================================================================
|
||||
ApplicationCommandTarget* getNextCommandTarget() override;
|
||||
void getAllCommands (Array <CommandID>& commands) override;
|
||||
void getCommandInfo (CommandID commandID, ApplicationCommandInfo& result) override;
|
||||
bool perform (const InvocationInfo& info) override;
|
||||
|
||||
void paint (Graphics&) override;
|
||||
void paintOverChildren (Graphics&) override;
|
||||
void resized() override;
|
||||
void childBoundsChanged (Component*) override;
|
||||
void lookAndFeelChanged() override;
|
||||
|
||||
protected:
|
||||
Project* project;
|
||||
OpenDocumentManager::Document* currentDocument;
|
||||
RecentDocumentList recentDocumentList;
|
||||
ScopedPointer<Component> logo;
|
||||
ScopedPointer<Component> translationTool;
|
||||
|
||||
TabbedComponent treeViewTabs;
|
||||
ScopedPointer<ResizableEdgeComponent> resizerBar;
|
||||
ScopedPointer<Component> contentView;
|
||||
|
||||
ComponentBoundsConstrainer treeSizeConstrainer;
|
||||
BubbleMessageComponent bubbleMessage;
|
||||
|
||||
bool documentAboutToClose (OpenDocumentManager::Document*) override;
|
||||
void changeListenerCallback (ChangeBroadcaster*) override;
|
||||
void showTranslationTool();
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ProjectContentComponent)
|
||||
};
|
||||
|
||||
|
||||
#endif // __JUCER_PROJECTCONTENTCOMPONENT_JUCEHEADER__
|
||||
@@ -0,0 +1,423 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
|
||||
Permission is granted to use this software under the terms of either:
|
||||
a) the GPL v2 (or any later version)
|
||||
b) the Affero GPL v3
|
||||
|
||||
Details of these licenses can be found at: www.gnu.org/licenses
|
||||
|
||||
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.juce.com for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
class ProjectTreeItemBase : public JucerTreeViewBase,
|
||||
public ValueTree::Listener
|
||||
{
|
||||
public:
|
||||
ProjectTreeItemBase (const Project::Item& projectItem)
|
||||
: item (projectItem), isFileMissing (false)
|
||||
{
|
||||
item.state.addListener (this);
|
||||
}
|
||||
|
||||
~ProjectTreeItemBase()
|
||||
{
|
||||
item.state.removeListener (this);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
virtual bool isRoot() const { return false; }
|
||||
|
||||
virtual bool acceptsFileDrop (const StringArray& files) const = 0;
|
||||
virtual bool acceptsDragItems (const OwnedArray<Project::Item>& selectedNodes) = 0;
|
||||
|
||||
//==============================================================================
|
||||
virtual String getDisplayName() const { return item.getName(); }
|
||||
virtual String getRenamingName() const { return getDisplayName(); }
|
||||
|
||||
virtual void setName (const String& newName)
|
||||
{
|
||||
if (item.isMainGroup())
|
||||
item.project.setTitle (newName);
|
||||
else
|
||||
item.getNameValue() = newName;
|
||||
}
|
||||
|
||||
virtual bool isMissing() { return isFileMissing; }
|
||||
virtual File getFile() const { return item.getFile(); }
|
||||
|
||||
virtual void deleteItem() { item.removeItemFromProject(); }
|
||||
|
||||
virtual void deleteAllSelectedItems()
|
||||
{
|
||||
TreeView* tree = getOwnerView();
|
||||
const int numSelected = tree->getNumSelectedItems();
|
||||
OwnedArray <File> filesToTrash;
|
||||
OwnedArray <Project::Item> itemsToRemove;
|
||||
|
||||
for (int i = 0; i < numSelected; ++i)
|
||||
{
|
||||
if (const ProjectTreeItemBase* const p = dynamic_cast<ProjectTreeItemBase*> (tree->getSelectedItem (i)))
|
||||
{
|
||||
itemsToRemove.add (new Project::Item (p->item));
|
||||
|
||||
if (p->getFile().existsAsFile())
|
||||
filesToTrash.add (new File (p->getFile()));
|
||||
}
|
||||
}
|
||||
|
||||
if (filesToTrash.size() > 0)
|
||||
{
|
||||
String fileList;
|
||||
const int maxFilesToList = 10;
|
||||
for (int i = jmin (maxFilesToList, filesToTrash.size()); --i >= 0;)
|
||||
fileList << filesToTrash.getUnchecked(i)->getFullPathName() << "\n";
|
||||
|
||||
if (filesToTrash.size() > maxFilesToList)
|
||||
fileList << "\n...plus " << (filesToTrash.size() - maxFilesToList) << " more files...";
|
||||
|
||||
int r = AlertWindow::showYesNoCancelBox (AlertWindow::NoIcon, "Delete Project Items",
|
||||
"As well as removing the selected item(s) from the project, do you also want to move their files to the trash:\n\n"
|
||||
+ fileList,
|
||||
"Just remove references",
|
||||
"Also move files to Trash",
|
||||
"Cancel",
|
||||
tree->getTopLevelComponent());
|
||||
|
||||
if (r == 0)
|
||||
return;
|
||||
|
||||
if (r != 2)
|
||||
filesToTrash.clear();
|
||||
}
|
||||
|
||||
if (ProjectTreeItemBase* treeRootItem = dynamic_cast<ProjectTreeItemBase*> (tree->getRootItem()))
|
||||
{
|
||||
OpenDocumentManager& om = IntrojucerApp::getApp().openDocumentManager;
|
||||
|
||||
for (int i = filesToTrash.size(); --i >= 0;)
|
||||
{
|
||||
const File f (*filesToTrash.getUnchecked(i));
|
||||
|
||||
om.closeFile (f, false);
|
||||
|
||||
if (! f.moveToTrash())
|
||||
{
|
||||
// xxx
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = itemsToRemove.size(); --i >= 0;)
|
||||
{
|
||||
if (ProjectTreeItemBase* itemToRemove = treeRootItem->findTreeViewItem (*itemsToRemove.getUnchecked(i)))
|
||||
{
|
||||
om.closeFile (itemToRemove->getFile(), false);
|
||||
itemToRemove->deleteItem();
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
jassertfalse;
|
||||
}
|
||||
}
|
||||
|
||||
virtual void revealInFinder() const
|
||||
{
|
||||
getFile().revealToUser();
|
||||
}
|
||||
|
||||
virtual void browseToAddExistingFiles()
|
||||
{
|
||||
const File location (item.isGroup() ? item.determineGroupFolder() : getFile());
|
||||
FileChooser fc ("Add Files to Jucer Project", location, String::empty, false);
|
||||
|
||||
if (fc.browseForMultipleFilesOrDirectories())
|
||||
{
|
||||
StringArray files;
|
||||
|
||||
for (int i = 0; i < fc.getResults().size(); ++i)
|
||||
files.add (fc.getResults().getReference(i).getFullPathName());
|
||||
|
||||
addFiles (files, 0);
|
||||
}
|
||||
}
|
||||
|
||||
virtual void checkFileStatus() // (recursive)
|
||||
{
|
||||
const File file (getFile());
|
||||
const bool nowMissing = file != File::nonexistent && ! file.exists();
|
||||
|
||||
if (nowMissing != isFileMissing)
|
||||
{
|
||||
isFileMissing = nowMissing;
|
||||
repaintItem();
|
||||
}
|
||||
}
|
||||
|
||||
virtual void addFiles (const StringArray& files, int insertIndex)
|
||||
{
|
||||
if (ProjectTreeItemBase* p = getParentProjectItem())
|
||||
p->addFiles (files, insertIndex);
|
||||
}
|
||||
|
||||
virtual void moveSelectedItemsTo (OwnedArray <Project::Item>&, int /*insertIndex*/)
|
||||
{
|
||||
jassertfalse;
|
||||
}
|
||||
|
||||
virtual void showMultiSelectionPopupMenu()
|
||||
{
|
||||
PopupMenu m;
|
||||
m.addItem (1, "Delete");
|
||||
|
||||
m.showMenuAsync (PopupMenu::Options(),
|
||||
ModalCallbackFunction::create (treeViewMultiSelectItemChosen, this));
|
||||
}
|
||||
|
||||
static void treeViewMultiSelectItemChosen (int resultCode, ProjectTreeItemBase* item)
|
||||
{
|
||||
switch (resultCode)
|
||||
{
|
||||
case 1: item->deleteAllSelectedItems(); break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
|
||||
virtual ProjectTreeItemBase* findTreeViewItem (const Project::Item& itemToFind)
|
||||
{
|
||||
if (item == itemToFind)
|
||||
return this;
|
||||
|
||||
const bool wasOpen = isOpen();
|
||||
setOpen (true);
|
||||
|
||||
for (int i = getNumSubItems(); --i >= 0;)
|
||||
{
|
||||
if (ProjectTreeItemBase* pg = dynamic_cast<ProjectTreeItemBase*> (getSubItem(i)))
|
||||
if (ProjectTreeItemBase* found = pg->findTreeViewItem (itemToFind))
|
||||
return found;
|
||||
}
|
||||
|
||||
setOpen (wasOpen);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void valueTreePropertyChanged (ValueTree& tree, const Identifier&) override
|
||||
{
|
||||
if (tree == item.state)
|
||||
repaintItem();
|
||||
}
|
||||
|
||||
void valueTreeChildAdded (ValueTree& parentTree, ValueTree&) override { treeChildrenChanged (parentTree); }
|
||||
void valueTreeChildRemoved (ValueTree& parentTree, ValueTree&) override { treeChildrenChanged (parentTree); }
|
||||
void valueTreeChildOrderChanged (ValueTree& parentTree) override { treeChildrenChanged (parentTree); }
|
||||
void valueTreeParentChanged (ValueTree&) override {}
|
||||
|
||||
//==============================================================================
|
||||
bool mightContainSubItems() override { return item.getNumChildren() > 0; }
|
||||
String getUniqueName() const override { jassert (item.getID().isNotEmpty()); return item.getID(); }
|
||||
bool canBeSelected() const override { return true; }
|
||||
String getTooltip() override { return String::empty; }
|
||||
File getDraggableFile() const override { return getFile(); }
|
||||
|
||||
var getDragSourceDescription() override
|
||||
{
|
||||
cancelDelayedSelectionTimer();
|
||||
return projectItemDragType;
|
||||
}
|
||||
|
||||
void addSubItems() override
|
||||
{
|
||||
for (int i = 0; i < item.getNumChildren(); ++i)
|
||||
if (ProjectTreeItemBase* p = createSubItem (item.getChild(i)))
|
||||
addSubItem (p);
|
||||
}
|
||||
|
||||
void itemOpennessChanged (bool isNowOpen) override
|
||||
{
|
||||
if (isNowOpen)
|
||||
refreshSubItems();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
bool isInterestedInFileDrag (const StringArray& files) override
|
||||
{
|
||||
return acceptsFileDrop (files);
|
||||
}
|
||||
|
||||
void filesDropped (const StringArray& files, int insertIndex) override
|
||||
{
|
||||
if (files.size() == 1 && File (files[0]).hasFileExtension (Project::projectFileExtension))
|
||||
IntrojucerApp::getApp().openFile (files[0]);
|
||||
else
|
||||
addFiles (files, insertIndex);
|
||||
}
|
||||
|
||||
bool isInterestedInDragSource (const DragAndDropTarget::SourceDetails& dragSourceDetails) override
|
||||
{
|
||||
OwnedArray<Project::Item> selectedNodes;
|
||||
getSelectedProjectItemsBeingDragged (dragSourceDetails, selectedNodes);
|
||||
|
||||
return selectedNodes.size() > 0 && acceptsDragItems (selectedNodes);
|
||||
}
|
||||
|
||||
void itemDropped (const DragAndDropTarget::SourceDetails& dragSourceDetails, int insertIndex) override
|
||||
{
|
||||
OwnedArray<Project::Item> selectedNodes;
|
||||
getSelectedProjectItemsBeingDragged (dragSourceDetails, selectedNodes);
|
||||
|
||||
if (selectedNodes.size() > 0)
|
||||
{
|
||||
TreeView* tree = getOwnerView();
|
||||
ScopedPointer<XmlElement> oldOpenness (tree->getOpennessState (false));
|
||||
|
||||
moveSelectedItemsTo (selectedNodes, insertIndex);
|
||||
|
||||
if (oldOpenness != nullptr)
|
||||
tree->restoreOpennessState (*oldOpenness, false);
|
||||
}
|
||||
}
|
||||
|
||||
int getMillisecsAllowedForDragGesture() override
|
||||
{
|
||||
// for images, give the user longer to start dragging before assuming they're
|
||||
// clicking to select it for previewing..
|
||||
return item.isImageFile() ? 250 : JucerTreeViewBase::getMillisecsAllowedForDragGesture();
|
||||
}
|
||||
|
||||
static void getSelectedProjectItemsBeingDragged (const DragAndDropTarget::SourceDetails& dragSourceDetails,
|
||||
OwnedArray<Project::Item>& selectedNodes)
|
||||
{
|
||||
if (dragSourceDetails.description == projectItemDragType)
|
||||
{
|
||||
TreeView* tree = dynamic_cast<TreeView*> (dragSourceDetails.sourceComponent.get());
|
||||
|
||||
if (tree == nullptr)
|
||||
tree = dragSourceDetails.sourceComponent->findParentComponentOfClass<TreeView>();
|
||||
|
||||
if (tree != nullptr)
|
||||
{
|
||||
const int numSelected = tree->getNumSelectedItems();
|
||||
|
||||
for (int i = 0; i < numSelected; ++i)
|
||||
if (const ProjectTreeItemBase* const p = dynamic_cast<ProjectTreeItemBase*> (tree->getSelectedItem (i)))
|
||||
selectedNodes.add (new Project::Item (p->item));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ProjectTreeItemBase* getParentProjectItem() const
|
||||
{
|
||||
return dynamic_cast<ProjectTreeItemBase*> (getParentItem());
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
Project::Item item;
|
||||
|
||||
protected:
|
||||
bool isFileMissing;
|
||||
|
||||
virtual ProjectTreeItemBase* createSubItem (const Project::Item& node) = 0;
|
||||
|
||||
Icon getIcon() const override { return item.getIcon().withContrastingColourTo (getBackgroundColour()); }
|
||||
bool isIconCrossedOut() const override { return item.isIconCrossedOut(); }
|
||||
|
||||
void treeChildrenChanged (const ValueTree& parentTree)
|
||||
{
|
||||
if (parentTree == item.state)
|
||||
{
|
||||
refreshSubItems();
|
||||
treeHasChanged();
|
||||
setOpen (true);
|
||||
}
|
||||
}
|
||||
|
||||
void triggerAsyncRename (const Project::Item& itemToRename)
|
||||
{
|
||||
class RenameMessage : public CallbackMessage
|
||||
{
|
||||
public:
|
||||
RenameMessage (TreeView* const t, const Project::Item& i)
|
||||
: tree (t), itemToRename (i) {}
|
||||
|
||||
void messageCallback() override
|
||||
{
|
||||
if (tree != nullptr)
|
||||
if (ProjectTreeItemBase* root = dynamic_cast<ProjectTreeItemBase*> (tree->getRootItem()))
|
||||
if (ProjectTreeItemBase* found = root->findTreeViewItem (itemToRename))
|
||||
found->showRenameBox();
|
||||
}
|
||||
|
||||
private:
|
||||
Component::SafePointer<TreeView> tree;
|
||||
Project::Item itemToRename;
|
||||
};
|
||||
|
||||
(new RenameMessage (getOwnerView(), itemToRename))->post();
|
||||
}
|
||||
|
||||
static void moveItems (OwnedArray <Project::Item>& selectedNodes, Project::Item destNode, int insertIndex)
|
||||
{
|
||||
for (int i = selectedNodes.size(); --i >= 0;)
|
||||
{
|
||||
Project::Item* const n = selectedNodes.getUnchecked(i);
|
||||
|
||||
if (destNode == *n || destNode.state.isAChildOf (n->state)) // Check for recursion.
|
||||
return;
|
||||
|
||||
if (! destNode.canContain (*n))
|
||||
selectedNodes.remove (i);
|
||||
}
|
||||
|
||||
// Don't include any nodes that are children of other selected nodes..
|
||||
for (int i = selectedNodes.size(); --i >= 0;)
|
||||
{
|
||||
Project::Item* const n = selectedNodes.getUnchecked(i);
|
||||
|
||||
for (int j = selectedNodes.size(); --j >= 0;)
|
||||
{
|
||||
if (j != i && n->state.isAChildOf (selectedNodes.getUnchecked(j)->state))
|
||||
{
|
||||
selectedNodes.remove (i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Remove and re-insert them one at a time..
|
||||
for (int i = 0; i < selectedNodes.size(); ++i)
|
||||
{
|
||||
Project::Item* selectedNode = selectedNodes.getUnchecked(i);
|
||||
|
||||
if (selectedNode->state.getParent() == destNode.state
|
||||
&& indexOfNode (destNode.state, selectedNode->state) < insertIndex)
|
||||
--insertIndex;
|
||||
|
||||
selectedNode->removeItemFromProject();
|
||||
destNode.addChild (*selectedNode, insertIndex++);
|
||||
}
|
||||
}
|
||||
|
||||
static int indexOfNode (const ValueTree& parent, const ValueTree& child)
|
||||
{
|
||||
for (int i = parent.getNumChildren(); --i >= 0;)
|
||||
if (parent.getChild (i) == child)
|
||||
return i;
|
||||
|
||||
return -1;
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,154 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
|
||||
Permission is granted to use this software under the terms of either:
|
||||
a) the GPL v2 (or any later version)
|
||||
b) the Affero GPL v3
|
||||
|
||||
Details of these licenses can be found at: www.gnu.org/licenses
|
||||
|
||||
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.juce.com for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
class SourceFileItem : public ProjectTreeItemBase
|
||||
{
|
||||
public:
|
||||
SourceFileItem (const Project::Item& projectItem)
|
||||
: ProjectTreeItemBase (projectItem)
|
||||
{
|
||||
}
|
||||
|
||||
bool acceptsFileDrop (const StringArray&) const override { return false; }
|
||||
bool acceptsDragItems (const OwnedArray <Project::Item>&) override { return false; }
|
||||
|
||||
String getDisplayName() const override
|
||||
{
|
||||
return getFile().getFileName();
|
||||
}
|
||||
|
||||
static File findCorrespondingHeaderOrCpp (const File& f)
|
||||
{
|
||||
if (f.hasFileExtension (sourceFileExtensions))
|
||||
return f.withFileExtension (".h");
|
||||
else if (f.hasFileExtension (headerFileExtensions))
|
||||
return f.withFileExtension (".cpp");
|
||||
|
||||
return File::nonexistent;
|
||||
}
|
||||
|
||||
void setName (const String& newName) override
|
||||
{
|
||||
if (newName != File::createLegalFileName (newName))
|
||||
{
|
||||
AlertWindow::showMessageBox (AlertWindow::WarningIcon, "File Rename",
|
||||
"That filename contained some illegal characters!");
|
||||
triggerAsyncRename (item);
|
||||
return;
|
||||
}
|
||||
|
||||
File oldFile (getFile());
|
||||
File newFile (oldFile.getSiblingFile (newName));
|
||||
File correspondingFile (findCorrespondingHeaderOrCpp (oldFile));
|
||||
|
||||
if (correspondingFile.exists() && newFile.hasFileExtension (oldFile.getFileExtension()))
|
||||
{
|
||||
Project::Item correspondingItem (item.project.getMainGroup().findItemForFile (correspondingFile));
|
||||
|
||||
if (correspondingItem.isValid())
|
||||
{
|
||||
if (AlertWindow::showOkCancelBox (AlertWindow::NoIcon, "File Rename",
|
||||
"Do you also want to rename the corresponding file \"" + correspondingFile.getFileName()
|
||||
+ "\" to match?"))
|
||||
{
|
||||
if (! item.renameFile (newFile))
|
||||
{
|
||||
AlertWindow::showMessageBox (AlertWindow::WarningIcon, "File Rename",
|
||||
"Failed to rename \"" + oldFile.getFullPathName() + "\"!\n\nCheck your file permissions!");
|
||||
return;
|
||||
}
|
||||
|
||||
if (! correspondingItem.renameFile (newFile.withFileExtension (correspondingFile.getFileExtension())))
|
||||
{
|
||||
AlertWindow::showMessageBox (AlertWindow::WarningIcon, "File Rename",
|
||||
"Failed to rename \"" + correspondingFile.getFullPathName() + "\"!\n\nCheck your file permissions!");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (! item.renameFile (newFile))
|
||||
{
|
||||
AlertWindow::showMessageBox (AlertWindow::WarningIcon, "File Rename",
|
||||
"Failed to rename the file!\n\nCheck your file permissions!");
|
||||
}
|
||||
}
|
||||
|
||||
ProjectTreeItemBase* createSubItem (const Project::Item&) override
|
||||
{
|
||||
jassertfalse;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void showDocument() override
|
||||
{
|
||||
const File f (getFile());
|
||||
|
||||
if (f.exists())
|
||||
if (ProjectContentComponent* pcc = getProjectContentComponent())
|
||||
pcc->showEditorForFile (f, false);
|
||||
}
|
||||
|
||||
void showPopupMenu() override
|
||||
{
|
||||
PopupMenu m;
|
||||
|
||||
if (GroupItem* parentGroup = dynamic_cast<GroupItem*> (getParentProjectItem()))
|
||||
{
|
||||
parentGroup->addCreateFileMenuItems (m);
|
||||
m.addSeparator();
|
||||
}
|
||||
|
||||
m.addItem (1, "Open in external editor");
|
||||
m.addItem (2,
|
||||
#if JUCE_MAC
|
||||
"Reveal in Finder");
|
||||
#else
|
||||
"Reveal in Explorer");
|
||||
#endif
|
||||
|
||||
m.addItem (4, "Rename File...");
|
||||
m.addSeparator();
|
||||
m.addItem (3, "Delete");
|
||||
|
||||
launchPopupMenu (m);
|
||||
}
|
||||
|
||||
void handlePopupMenuResult (int resultCode) override
|
||||
{
|
||||
switch (resultCode)
|
||||
{
|
||||
case 1: getFile().startAsProcess(); break;
|
||||
case 2: revealInFinder(); break;
|
||||
case 3: deleteAllSelectedItems(); break;
|
||||
case 4: triggerAsyncRename (item); break;
|
||||
|
||||
default:
|
||||
if (GroupItem* parentGroup = dynamic_cast<GroupItem*> (getParentProjectItem()))
|
||||
parentGroup->processCreateFileMenuItem (resultCode);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,172 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
|
||||
Permission is granted to use this software under the terms of either:
|
||||
a) the GPL v2 (or any later version)
|
||||
b) the Affero GPL v3
|
||||
|
||||
Details of these licenses can be found at: www.gnu.org/licenses
|
||||
|
||||
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.juce.com for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
class GroupItem : public ProjectTreeItemBase
|
||||
{
|
||||
public:
|
||||
GroupItem (const Project::Item& projectItem)
|
||||
: ProjectTreeItemBase (projectItem)
|
||||
{
|
||||
}
|
||||
|
||||
bool isRoot() const override { return item.isMainGroup(); }
|
||||
bool acceptsFileDrop (const StringArray&) const override { return true; }
|
||||
|
||||
void addNewGroup()
|
||||
{
|
||||
Project::Item newGroup (item.addNewSubGroup ("New Group", 0));
|
||||
triggerAsyncRename (newGroup);
|
||||
}
|
||||
|
||||
bool acceptsDragItems (const OwnedArray<Project::Item>& selectedNodes) override
|
||||
{
|
||||
for (int i = selectedNodes.size(); --i >= 0;)
|
||||
if (item.canContain (*selectedNodes.getUnchecked(i)))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void addFiles (const StringArray& files, int insertIndex) override
|
||||
{
|
||||
for (int i = 0; i < files.size(); ++i)
|
||||
{
|
||||
const File file (files[i]);
|
||||
|
||||
if (item.addFile (file, insertIndex, true))
|
||||
++insertIndex;
|
||||
}
|
||||
}
|
||||
|
||||
void moveSelectedItemsTo (OwnedArray<Project::Item>& selectedNodes, int insertIndex) override
|
||||
{
|
||||
moveItems (selectedNodes, item, insertIndex);
|
||||
}
|
||||
|
||||
void checkFileStatus() override
|
||||
{
|
||||
for (int i = 0; i < getNumSubItems(); ++i)
|
||||
if (ProjectTreeItemBase* p = dynamic_cast<ProjectTreeItemBase*> (getSubItem(i)))
|
||||
p->checkFileStatus();
|
||||
}
|
||||
|
||||
ProjectTreeItemBase* createSubItem (const Project::Item& child) override
|
||||
{
|
||||
if (child.isGroup()) return new GroupItem (child);
|
||||
if (child.isFile()) return new SourceFileItem (child);
|
||||
|
||||
jassertfalse;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void showDocument() override
|
||||
{
|
||||
if (ProjectContentComponent* pcc = getProjectContentComponent())
|
||||
pcc->setEditorComponent (new GroupInformationComponent (item), nullptr);
|
||||
}
|
||||
|
||||
static void openOrCloseAllSubGroups (TreeViewItem& item, bool shouldOpen)
|
||||
{
|
||||
item.setOpen (shouldOpen);
|
||||
|
||||
for (int i = item.getNumSubItems(); --i >= 0;)
|
||||
if (TreeViewItem* sub = item.getSubItem(i))
|
||||
openOrCloseAllSubGroups (*sub, shouldOpen);
|
||||
}
|
||||
|
||||
static void setFilesToCompile (Project::Item item, const bool shouldCompile)
|
||||
{
|
||||
if (item.isFile())
|
||||
item.getShouldCompileValue() = shouldCompile;
|
||||
|
||||
for (int i = item.getNumChildren(); --i >= 0;)
|
||||
setFilesToCompile (item.getChild (i), shouldCompile);
|
||||
}
|
||||
|
||||
void showPopupMenu() override
|
||||
{
|
||||
PopupMenu m;
|
||||
addCreateFileMenuItems (m);
|
||||
|
||||
m.addSeparator();
|
||||
|
||||
if (isOpen())
|
||||
m.addItem (1, "Collapse all Sub-groups");
|
||||
else
|
||||
m.addItem (2, "Expand all Sub-groups");
|
||||
|
||||
m.addSeparator();
|
||||
m.addItem (3, "Enable compiling of all enclosed files");
|
||||
m.addItem (4, "Disable compiling of all enclosed files");
|
||||
|
||||
m.addSeparator();
|
||||
m.addItem (5, "Sort Items Alphabetically");
|
||||
m.addItem (6, "Sort Items Alphabetically (Groups first)");
|
||||
m.addSeparator();
|
||||
m.addItem (7, "Rename...");
|
||||
|
||||
if (! isRoot())
|
||||
m.addItem (8, "Delete");
|
||||
|
||||
launchPopupMenu (m);
|
||||
}
|
||||
|
||||
void handlePopupMenuResult (int resultCode) override
|
||||
{
|
||||
switch (resultCode)
|
||||
{
|
||||
case 1: openOrCloseAllSubGroups (*this, false); break;
|
||||
case 2: openOrCloseAllSubGroups (*this, true); break;
|
||||
case 3: setFilesToCompile (item, true); break;
|
||||
case 4: setFilesToCompile (item, false); break;
|
||||
case 5: item.sortAlphabetically (false); break;
|
||||
case 6: item.sortAlphabetically (true); break;
|
||||
case 7: triggerAsyncRename (item); break;
|
||||
case 8: deleteAllSelectedItems(); break;
|
||||
default: processCreateFileMenuItem (resultCode); break;
|
||||
}
|
||||
}
|
||||
|
||||
void addCreateFileMenuItems (PopupMenu& m)
|
||||
{
|
||||
m.addItem (1001, "Add New Group");
|
||||
m.addItem (1002, "Add Existing Files...");
|
||||
|
||||
m.addSeparator();
|
||||
NewFileWizard().addWizardsToMenu (m);
|
||||
}
|
||||
|
||||
void processCreateFileMenuItem (int menuID)
|
||||
{
|
||||
switch (menuID)
|
||||
{
|
||||
case 1001: addNewGroup(); break;
|
||||
case 1002: browseToAddExistingFiles(); break;
|
||||
|
||||
default:
|
||||
NewFileWizard().runWizardFromMenu (menuID, item);
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,364 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
|
||||
Permission is granted to use this software under the terms of either:
|
||||
a) the GPL v2 (or any later version)
|
||||
b) the Affero GPL v3
|
||||
|
||||
Details of these licenses can be found at: www.gnu.org/licenses
|
||||
|
||||
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.juce.com for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#include "jucer_ProjectType.h"
|
||||
#include "../Project Saving/jucer_ProjectExporter.h"
|
||||
#include "../Project Saving/jucer_ProjectSaver.h"
|
||||
#include "jucer_AudioPluginModule.h"
|
||||
|
||||
|
||||
//==============================================================================
|
||||
ProjectType::ProjectType (const String& t, const String& d)
|
||||
: type (t), desc (d)
|
||||
{
|
||||
getAllTypes().add (this);
|
||||
}
|
||||
|
||||
ProjectType::~ProjectType()
|
||||
{
|
||||
getAllTypes().removeFirstMatchingValue (this);
|
||||
}
|
||||
|
||||
Array<ProjectType*>& ProjectType::getAllTypes()
|
||||
{
|
||||
static Array<ProjectType*> types;
|
||||
return types;
|
||||
}
|
||||
|
||||
const ProjectType* ProjectType::findType (const String& typeCode)
|
||||
{
|
||||
const Array<ProjectType*>& types = getAllTypes();
|
||||
|
||||
for (int i = types.size(); --i >= 0;)
|
||||
if (types.getUnchecked(i)->getType() == typeCode)
|
||||
return types.getUnchecked(i);
|
||||
|
||||
jassertfalse;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
class ProjectType_GUIApp : public ProjectType
|
||||
{
|
||||
public:
|
||||
ProjectType_GUIApp() : ProjectType (getTypeName(), "Application (GUI)") {}
|
||||
|
||||
static const char* getTypeName() noexcept { return "guiapp"; }
|
||||
bool isGUIApplication() const { return true; }
|
||||
|
||||
void setMissingProjectProperties (Project&) const
|
||||
{
|
||||
}
|
||||
|
||||
void createPropertyEditors (Project&, PropertyListBuilder&) const
|
||||
{
|
||||
}
|
||||
|
||||
void prepareExporter (ProjectExporter& exporter) const
|
||||
{
|
||||
exporter.xcodePackageType = "APPL";
|
||||
exporter.xcodeBundleSignature = "????";
|
||||
exporter.xcodeCreatePList = true;
|
||||
exporter.xcodeFileType = "wrapper.application";
|
||||
exporter.xcodeBundleExtension = ".app";
|
||||
exporter.xcodeProductType = "com.apple.product-type.application";
|
||||
exporter.xcodeProductInstallPath = "$(HOME)/Applications";
|
||||
|
||||
exporter.msvcIsWindowsSubsystem = true;
|
||||
exporter.msvcTargetSuffix = ".exe";
|
||||
}
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
class ProjectType_ConsoleApp : public ProjectType
|
||||
{
|
||||
public:
|
||||
ProjectType_ConsoleApp() : ProjectType (getTypeName(), "Application (Non-GUI)") {}
|
||||
|
||||
static const char* getTypeName() noexcept { return "consoleapp"; }
|
||||
bool isCommandLineApp() const { return true; }
|
||||
|
||||
void setMissingProjectProperties (Project&) const
|
||||
{
|
||||
}
|
||||
|
||||
void createPropertyEditors (Project&, PropertyListBuilder&) const
|
||||
{
|
||||
}
|
||||
|
||||
void prepareExporter (ProjectExporter& exporter) const
|
||||
{
|
||||
exporter.xcodeCreatePList = false;
|
||||
exporter.xcodeFileType = "compiled.mach-o.executable";
|
||||
exporter.xcodeBundleExtension = String::empty;
|
||||
exporter.xcodeProductType = "com.apple.product-type.tool";
|
||||
exporter.xcodeProductInstallPath = "/usr/bin";
|
||||
|
||||
exporter.msvcIsWindowsSubsystem = false;
|
||||
exporter.msvcTargetSuffix = ".exe";
|
||||
exporter.msvcExtraPreprocessorDefs.set ("_CONSOLE", "");
|
||||
}
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
class ProjectType_StaticLibrary : public ProjectType
|
||||
{
|
||||
public:
|
||||
ProjectType_StaticLibrary() : ProjectType (getTypeName(), "Static Library") {}
|
||||
|
||||
static const char* getTypeName() noexcept { return "library"; }
|
||||
bool isStaticLibrary() const { return true; }
|
||||
|
||||
void setMissingProjectProperties (Project&) const
|
||||
{
|
||||
}
|
||||
|
||||
void createPropertyEditors (Project&, PropertyListBuilder&) const
|
||||
{
|
||||
}
|
||||
|
||||
void prepareExporter (ProjectExporter& exporter) const
|
||||
{
|
||||
exporter.xcodeCreatePList = false;
|
||||
exporter.xcodeFileType = "archive.ar";
|
||||
exporter.xcodeProductType = "com.apple.product-type.library.static";
|
||||
exporter.xcodeProductInstallPath = String::empty;
|
||||
exporter.makefileTargetSuffix = ".a";
|
||||
exporter.msvcTargetSuffix = ".lib";
|
||||
exporter.msvcExtraPreprocessorDefs.set ("_LIB", "");
|
||||
}
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
class ProjectType_DLL : public ProjectType
|
||||
{
|
||||
public:
|
||||
ProjectType_DLL() : ProjectType (getTypeName(), "Dynamic Library") {}
|
||||
|
||||
static const char* getTypeName() noexcept { return "dll"; }
|
||||
bool isDynamicLibrary() const { return true; }
|
||||
|
||||
void setMissingProjectProperties (Project&) const
|
||||
{
|
||||
}
|
||||
|
||||
void createPropertyEditors (Project&, PropertyListBuilder&) const
|
||||
{
|
||||
}
|
||||
|
||||
void prepareExporter (ProjectExporter& exporter) const
|
||||
{
|
||||
exporter.xcodeCreatePList = false;
|
||||
exporter.xcodeFileType = "compiled.mach-o.dylib";
|
||||
exporter.xcodeProductType = "com.apple.product-type.library.dynamic";
|
||||
exporter.xcodeBundleExtension = ".dylib";
|
||||
exporter.xcodeProductInstallPath = String::empty;
|
||||
exporter.makefileTargetSuffix = ".so";
|
||||
exporter.msvcTargetSuffix = ".dll";
|
||||
exporter.msvcExtraPreprocessorDefs.set ("_LIB", "");
|
||||
}
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
class ProjectType_AudioPlugin : public ProjectType
|
||||
{
|
||||
public:
|
||||
ProjectType_AudioPlugin() : ProjectType (getTypeName(), "Audio Plug-in") {}
|
||||
|
||||
static const char* getTypeName() noexcept { return "audioplug"; }
|
||||
bool isAudioPlugin() const { return true; }
|
||||
|
||||
void setMissingProjectProperties (Project& project) const
|
||||
{
|
||||
const String sanitisedProjectName (CodeHelpers::makeValidIdentifier (project.getTitle(), false, true, false));
|
||||
|
||||
setValueIfVoid (shouldBuildVST (project), true);
|
||||
setValueIfVoid (shouldBuildVST3 (project), false);
|
||||
setValueIfVoid (shouldBuildAU (project), true);
|
||||
setValueIfVoid (shouldBuildRTAS (project), false);
|
||||
setValueIfVoid (shouldBuildAAX (project), false);
|
||||
|
||||
setValueIfVoid (getPluginName (project), project.getTitle());
|
||||
setValueIfVoid (getPluginDesc (project), project.getTitle());
|
||||
setValueIfVoid (getPluginManufacturer (project), "yourcompany");
|
||||
setValueIfVoid (getPluginManufacturerCode (project), "Manu");
|
||||
setValueIfVoid (getPluginCode (project), "Plug");
|
||||
setValueIfVoid (getPluginChannelConfigs (project), "{1, 1}, {2, 2}");
|
||||
setValueIfVoid (getPluginIsSynth (project), false);
|
||||
setValueIfVoid (getPluginWantsMidiInput (project), false);
|
||||
setValueIfVoid (getPluginProducesMidiOut (project), false);
|
||||
setValueIfVoid (getPluginSilenceInProducesSilenceOut (project), false);
|
||||
setValueIfVoid (getPluginEditorNeedsKeyFocus (project), false);
|
||||
setValueIfVoid (getPluginAUExportPrefix (project), sanitisedProjectName + "AU");
|
||||
setValueIfVoid (getPluginRTASCategory (project), String::empty);
|
||||
setValueIfVoid (project.getBundleIdentifier(), project.getDefaultBundleIdentifier());
|
||||
setValueIfVoid (project.getAAXIdentifier(), project.getDefaultAAXIdentifier());
|
||||
setValueIfVoid (getPluginAAXCategory (project), "AAX_ePlugInCategory_Dynamics");
|
||||
}
|
||||
|
||||
void createPropertyEditors (Project& project, PropertyListBuilder& props) const
|
||||
{
|
||||
props.add (new BooleanPropertyComponent (shouldBuildVST (project), "Build VST", "Enabled"),
|
||||
"Whether the project should produce a VST plugin.");
|
||||
props.add (new BooleanPropertyComponent (shouldBuildVST3 (project), "Build VST3", "Enabled"),
|
||||
"Whether the project should produce a VST3 plugin.");
|
||||
props.add (new BooleanPropertyComponent (shouldBuildAU (project), "Build AudioUnit", "Enabled"),
|
||||
"Whether the project should produce an AudioUnit plugin.");
|
||||
props.add (new BooleanPropertyComponent (shouldBuildRTAS (project), "Build RTAS", "Enabled"),
|
||||
"Whether the project should produce an RTAS plugin.");
|
||||
props.add (new BooleanPropertyComponent (shouldBuildAAX (project), "Build AAX", "Enabled"),
|
||||
"Whether the project should produce an AAX plugin.");
|
||||
|
||||
props.add (new TextPropertyComponent (getPluginName (project), "Plugin Name", 128, false),
|
||||
"The name of your plugin (keep it short!)");
|
||||
props.add (new TextPropertyComponent (getPluginDesc (project), "Plugin Description", 256, false),
|
||||
"A short description of your plugin.");
|
||||
|
||||
props.add (new TextPropertyComponent (getPluginManufacturer (project), "Plugin Manufacturer", 256, false),
|
||||
"The name of your company (cannot be blank).");
|
||||
props.add (new TextPropertyComponent (getPluginManufacturerCode (project), "Plugin Manufacturer Code", 4, false),
|
||||
"A four-character unique ID for your company. Note that for AU compatibility, this must contain at least one upper-case letter!");
|
||||
props.add (new TextPropertyComponent (getPluginCode (project), "Plugin Code", 4, false),
|
||||
"A four-character unique ID for your plugin. Note that for AU compatibility, this must contain at least one upper-case letter!");
|
||||
|
||||
props.add (new TextPropertyComponent (getPluginChannelConfigs (project), "Plugin Channel Configurations", 1024, false),
|
||||
"This is the set of input/output channel configurations that your plugin can handle. The list is a comma-separated set of pairs of values in the form { numInputs, numOutputs }, and each "
|
||||
"pair indicates a valid configuration that the plugin can handle. So for example, {1, 1}, {2, 2} means that the plugin can be used in just two configurations: either with 1 input "
|
||||
"and 1 output, or with 2 inputs and 2 outputs.");
|
||||
|
||||
props.add (new BooleanPropertyComponent (getPluginIsSynth (project), "Plugin is a Synth", "Is a Synth"),
|
||||
"Enable this if you want your plugin to be treated as a synth or generator. It doesn't make much difference to the plugin itself, but some hosts treat synths differently to other plugins.");
|
||||
|
||||
props.add (new BooleanPropertyComponent (getPluginWantsMidiInput (project), "Plugin Midi Input", "Plugin wants midi input"),
|
||||
"Enable this if you want your plugin to accept midi messages.");
|
||||
|
||||
props.add (new BooleanPropertyComponent (getPluginProducesMidiOut (project), "Plugin Midi Output", "Plugin produces midi output"),
|
||||
"Enable this if your plugin is going to produce midi messages.");
|
||||
|
||||
props.add (new BooleanPropertyComponent (getPluginSilenceInProducesSilenceOut (project), "Silence", "Silence in produces silence out"),
|
||||
"Enable this if your plugin has no tail - i.e. if passing a silent buffer to it will always result in a silent buffer being produced.");
|
||||
|
||||
props.add (new BooleanPropertyComponent (getPluginEditorNeedsKeyFocus (project), "Key Focus", "Plugin editor requires keyboard focus"),
|
||||
"Enable this if your plugin needs keyboard input - some hosts can be a bit funny about keyboard focus..");
|
||||
|
||||
props.add (new TextPropertyComponent (getPluginAUExportPrefix (project), "Plugin AU Export Prefix", 64, false),
|
||||
"A prefix for the names of exported entry-point functions that the component exposes - typically this will be a version of your plugin's name that can be used as part of a C++ token.");
|
||||
|
||||
props.add (new TextPropertyComponent (getPluginAUMainType (project), "Plugin AU Main Type", 128, false),
|
||||
"In an AU, this is the value that is set as JucePlugin_AUMainType. Leave it blank unless you want to use a custom value.");
|
||||
|
||||
props.add (new TextPropertyComponent (getPluginVSTCategory (project), "VST Category", 64, false),
|
||||
"In a VST, this is the value that is set as JucePlugin_VSTCategory. Leave it blank unless you want to use a custom value.");
|
||||
|
||||
props.add (new TextPropertyComponent (getPluginRTASCategory (project), "Plugin RTAS Category", 64, false),
|
||||
"(Leave this blank if your plugin is a synth). This is one of the RTAS categories from FicPluginEnums.h, such as: ePlugInCategory_None, ePlugInCategory_EQ, ePlugInCategory_Dynamics, "
|
||||
"ePlugInCategory_PitchShift, ePlugInCategory_Reverb, ePlugInCategory_Delay, "
|
||||
"ePlugInCategory_Modulation, ePlugInCategory_Harmonic, ePlugInCategory_NoiseReduction, "
|
||||
"ePlugInCategory_Dither, ePlugInCategory_SoundField");
|
||||
|
||||
props.add (new TextPropertyComponent (getPluginAAXCategory (project), "Plugin AAX Category", 64, false),
|
||||
"This is one of the categories from the AAX_EPlugInCategory enum");
|
||||
|
||||
props.add (new TextPropertyComponent (project.getAAXIdentifier(), "Plugin AAX Identifier", 256, false),
|
||||
"The value to use for the JucePlugin_AAXIdentifier setting");
|
||||
}
|
||||
|
||||
void prepareExporter (ProjectExporter& exporter) const
|
||||
{
|
||||
exporter.xcodeIsBundle = true;
|
||||
exporter.xcodeCreatePList = true;
|
||||
exporter.xcodePackageType = "TDMw";
|
||||
exporter.xcodeBundleSignature = "PTul";
|
||||
exporter.xcodeFileType = "wrapper.cfbundle";
|
||||
exporter.xcodeBundleExtension = ".component";
|
||||
exporter.xcodeProductType = "com.apple.product-type.bundle";
|
||||
exporter.xcodeProductInstallPath = "$(HOME)/Library/Audio/Plug-Ins/Components/";
|
||||
|
||||
exporter.xcodeOtherRezFlags = "-d ppc_$ppc -d i386_$i386 -d ppc64_$ppc64 -d x86_64_$x86_64"
|
||||
" -I /System/Library/Frameworks/CoreServices.framework/Frameworks/CarbonCore.framework/Versions/A/Headers"
|
||||
" -I \\\"$(DEVELOPER_DIR)/Extras/CoreAudio/AudioUnits/AUPublic/AUBase\\\"";
|
||||
|
||||
exporter.msvcTargetSuffix = ".dll";
|
||||
exporter.msvcIsDLL = true;
|
||||
|
||||
exporter.makefileIsDLL = true;
|
||||
}
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
class ProjectType_BrowserPlugin : public ProjectType
|
||||
{
|
||||
public:
|
||||
ProjectType_BrowserPlugin() : ProjectType (getTypeName(), "Browser Plug-in") {}
|
||||
|
||||
static const char* getTypeName() noexcept { return "browserplug"; }
|
||||
bool isBrowserPlugin() const { return true; }
|
||||
|
||||
void prepareExporter (ProjectExporter& exporter) const
|
||||
{
|
||||
exporter.xcodeIsBundle = true;
|
||||
exporter.xcodeCreatePList = true;
|
||||
exporter.xcodeFileType = "wrapper.cfbundle";
|
||||
exporter.xcodeBundleExtension = ".plugin";
|
||||
exporter.xcodeProductType = "com.apple.product-type.bundle";
|
||||
exporter.xcodeProductInstallPath = "$(HOME)/Library/Internet Plug-Ins//";
|
||||
|
||||
{
|
||||
XmlElement mimeTypesKey ("key");
|
||||
mimeTypesKey.setText ("WebPluginMIMETypes");
|
||||
|
||||
XmlElement mimeTypesEntry ("dict");
|
||||
const String exeName (exporter.getProject().getProjectFilenameRoot().toLowerCase());
|
||||
mimeTypesEntry.createNewChildElement ("key")->setText ("application/" + exeName + "-plugin");
|
||||
XmlElement* d = mimeTypesEntry.createNewChildElement ("dict");
|
||||
d->createNewChildElement ("key")->setText ("WebPluginExtensions");
|
||||
d->createNewChildElement ("array")
|
||||
->createNewChildElement ("string")->setText (exeName);
|
||||
d->createNewChildElement ("key")->setText ("WebPluginTypeDescription");
|
||||
d->createNewChildElement ("string")->setText (exporter.getProject().getTitle());
|
||||
|
||||
exporter.xcodeExtraPListEntries.add (mimeTypesKey);
|
||||
exporter.xcodeExtraPListEntries.add (mimeTypesEntry);
|
||||
}
|
||||
|
||||
exporter.msvcTargetSuffix = ".dll";
|
||||
exporter.msvcIsDLL = true;
|
||||
|
||||
exporter.makefileIsDLL = true;
|
||||
}
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
static ProjectType_GUIApp guiType;
|
||||
static ProjectType_ConsoleApp consoleType;
|
||||
static ProjectType_StaticLibrary libraryType;
|
||||
static ProjectType_DLL dllType;
|
||||
static ProjectType_AudioPlugin audioPluginType;
|
||||
|
||||
//==============================================================================
|
||||
const char* ProjectType::getGUIAppTypeName() { return ProjectType_GUIApp::getTypeName(); }
|
||||
const char* ProjectType::getConsoleAppTypeName() { return ProjectType_ConsoleApp::getTypeName(); }
|
||||
const char* ProjectType::getStaticLibTypeName() { return ProjectType_StaticLibrary::getTypeName(); }
|
||||
const char* ProjectType::getDynamicLibTypeName() { return ProjectType_DLL::getTypeName(); }
|
||||
const char* ProjectType::getAudioPluginTypeName() { return ProjectType_AudioPlugin::getTypeName(); }
|
||||
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
|
||||
Permission is granted to use this software under the terms of either:
|
||||
a) the GPL v2 (or any later version)
|
||||
b) the Affero GPL v3
|
||||
|
||||
Details of these licenses can be found at: www.gnu.org/licenses
|
||||
|
||||
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.juce.com for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#ifndef __JUCER_PROJECTTYPE_JUCEHEADER__
|
||||
#define __JUCER_PROJECTTYPE_JUCEHEADER__
|
||||
|
||||
#include "../jucer_Headers.h"
|
||||
class Project;
|
||||
class ProjectExporter;
|
||||
|
||||
//==============================================================================
|
||||
class ProjectType
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
virtual ~ProjectType();
|
||||
|
||||
const String& getType() const noexcept { return type; }
|
||||
const String& getDescription() const noexcept { return desc; }
|
||||
|
||||
//==============================================================================
|
||||
static Array<ProjectType*>& getAllTypes();
|
||||
static const ProjectType* findType (const String& typeCode);
|
||||
|
||||
//==============================================================================
|
||||
virtual bool isStaticLibrary() const { return false; }
|
||||
virtual bool isDynamicLibrary() const { return false; }
|
||||
virtual bool isGUIApplication() const { return false; }
|
||||
virtual bool isCommandLineApp() const { return false; }
|
||||
virtual bool isAudioPlugin() const { return false; }
|
||||
virtual bool isBrowserPlugin() const { return false; }
|
||||
|
||||
static const char* getGUIAppTypeName();
|
||||
static const char* getConsoleAppTypeName();
|
||||
static const char* getStaticLibTypeName();
|
||||
static const char* getDynamicLibTypeName();
|
||||
static const char* getAudioPluginTypeName();
|
||||
|
||||
|
||||
virtual void setMissingProjectProperties (Project&) const = 0;
|
||||
virtual void createPropertyEditors (Project&, PropertyListBuilder&) const = 0;
|
||||
virtual void prepareExporter (ProjectExporter&) const = 0;
|
||||
|
||||
protected:
|
||||
ProjectType (const String& type, const String& desc);
|
||||
|
||||
private:
|
||||
const String type, desc;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ProjectType)
|
||||
};
|
||||
|
||||
|
||||
#endif // __JUCER_PROJECTTYPE_JUCEHEADER__
|
||||
Reference in New Issue
Block a user