git subrepo clone --branch=sono6good https://github.com/essej/JUCE.git deps/juce
subrepo: subdir: "deps/juce" merged: "b13f9084e" upstream: origin: "https://github.com/essej/JUCE.git" branch: "sono6good" commit: "b13f9084e" git-subrepo: version: "0.4.3" origin: "https://github.com/ingydotnet/git-subrepo.git" commit: "2f68596"
This commit is contained in:
215
deps/juce/extras/Projucer/Source/Project/Modules/jucer_AvailableModulesList.h
vendored
Normal file
215
deps/juce/extras/Projucer/Source/Project/Modules/jucer_AvailableModulesList.h
vendored
Normal file
@ -0,0 +1,215 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "jucer_ModuleDescription.h"
|
||||
|
||||
//==============================================================================
|
||||
class AvailableModulesList : private AsyncUpdater
|
||||
{
|
||||
public:
|
||||
using ModuleIDAndFolder = std::pair<String, File>;
|
||||
using ModuleIDAndFolderList = std::vector<ModuleIDAndFolder>;
|
||||
|
||||
AvailableModulesList() = default;
|
||||
|
||||
//==============================================================================
|
||||
void scanPaths (const Array<File>& paths)
|
||||
{
|
||||
auto job = createScannerJob (paths);
|
||||
auto& ref = *job;
|
||||
|
||||
removePendingAndAddJob (std::move (job));
|
||||
scanPool.waitForJobToFinish (&ref, -1);
|
||||
}
|
||||
|
||||
void scanPathsAsync (const Array<File>& paths)
|
||||
{
|
||||
removePendingAndAddJob (createScannerJob (paths));
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
ModuleIDAndFolderList getAllModules() const
|
||||
{
|
||||
const ScopedLock readLock (lock);
|
||||
return modulesList;
|
||||
}
|
||||
|
||||
ModuleIDAndFolder getModuleWithID (const String& id) const
|
||||
{
|
||||
const ScopedLock readLock (lock);
|
||||
|
||||
for (auto& mod : modulesList)
|
||||
if (mod.first == id)
|
||||
return mod;
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void removeDuplicates (const ModuleIDAndFolderList& other)
|
||||
{
|
||||
const ScopedLock readLock (lock);
|
||||
|
||||
const auto predicate = [&] (const ModuleIDAndFolder& entry)
|
||||
{
|
||||
return std::find (other.begin(), other.end(), entry) != other.end();
|
||||
};
|
||||
|
||||
modulesList.erase (std::remove_if (modulesList.begin(), modulesList.end(), predicate),
|
||||
modulesList.end());
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
struct Listener
|
||||
{
|
||||
virtual ~Listener() = default;
|
||||
virtual void availableModulesChanged (AvailableModulesList* listThatHasChanged) = 0;
|
||||
};
|
||||
|
||||
void addListener (Listener* listenerToAdd) { listeners.add (listenerToAdd); }
|
||||
void removeListener (Listener* listenerToRemove) { listeners.remove (listenerToRemove); }
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
struct ModuleScannerJob : public ThreadPoolJob
|
||||
{
|
||||
ModuleScannerJob (const Array<File>& paths,
|
||||
std::function<void (const ModuleIDAndFolderList&)>&& callback)
|
||||
: ThreadPoolJob ("ModuleScannerJob"),
|
||||
pathsToScan (paths),
|
||||
completionCallback (std::move (callback))
|
||||
{
|
||||
}
|
||||
|
||||
JobStatus runJob() override
|
||||
{
|
||||
ModuleIDAndFolderList list;
|
||||
|
||||
for (auto& p : pathsToScan)
|
||||
addAllModulesInFolder (p, list);
|
||||
|
||||
if (! shouldExit())
|
||||
{
|
||||
std::sort (list.begin(), list.end(), [] (const ModuleIDAndFolder& m1,
|
||||
const ModuleIDAndFolder& m2)
|
||||
{
|
||||
return m1.first.compareIgnoreCase (m2.first) < 0;
|
||||
});
|
||||
|
||||
completionCallback (list);
|
||||
}
|
||||
|
||||
return jobHasFinished;
|
||||
}
|
||||
|
||||
static bool tryToAddModuleFromFolder (const File& path, ModuleIDAndFolderList& list)
|
||||
{
|
||||
ModuleDescription m (path);
|
||||
|
||||
if (m.isValid()
|
||||
&& std::find_if (list.begin(), list.end(),
|
||||
[&m] (const ModuleIDAndFolder& element) { return element.first == m.getID(); }) == std::end (list))
|
||||
{
|
||||
list.push_back ({ m.getID(), path });
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static void addAllModulesInFolder (const File& topLevelPath, ModuleIDAndFolderList& list)
|
||||
{
|
||||
struct FileAndDepth
|
||||
{
|
||||
File file;
|
||||
int depth;
|
||||
};
|
||||
|
||||
std::queue<FileAndDepth> pathsToCheck;
|
||||
pathsToCheck.push ({ topLevelPath, 0 });
|
||||
|
||||
while (! pathsToCheck.empty())
|
||||
{
|
||||
const auto path = pathsToCheck.front();
|
||||
pathsToCheck.pop();
|
||||
|
||||
if (tryToAddModuleFromFolder (path.file, list) || path.depth == 3)
|
||||
continue;
|
||||
|
||||
for (const auto& iter : RangedDirectoryIterator (path.file, false, "*", File::findDirectories))
|
||||
{
|
||||
if (auto* job = ThreadPoolJob::getCurrentThreadPoolJob())
|
||||
if (job->shouldExit())
|
||||
return;
|
||||
|
||||
pathsToCheck.push({ iter.getFile(), path.depth + 1 });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Array<File> pathsToScan;
|
||||
std::function<void (const ModuleIDAndFolderList&)> completionCallback;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
void handleAsyncUpdate() override
|
||||
{
|
||||
listeners.call ([this] (Listener& l) { l.availableModulesChanged (this); });
|
||||
}
|
||||
|
||||
std::unique_ptr<ThreadPoolJob> createScannerJob (const Array<File>& paths)
|
||||
{
|
||||
return std::make_unique<ModuleScannerJob> (paths, [this] (ModuleIDAndFolderList scannedModulesList)
|
||||
{
|
||||
if (scannedModulesList == modulesList)
|
||||
return;
|
||||
|
||||
{
|
||||
const ScopedLock swapLock (lock);
|
||||
modulesList.swap (scannedModulesList);
|
||||
}
|
||||
|
||||
triggerAsyncUpdate();
|
||||
});
|
||||
}
|
||||
|
||||
void removePendingAndAddJob (std::unique_ptr<ThreadPoolJob> jobToAdd)
|
||||
{
|
||||
scanPool.removeAllJobs (false, 100);
|
||||
scanPool.addJob (jobToAdd.release(), true);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
ThreadPool scanPool { 1 };
|
||||
|
||||
ModuleIDAndFolderList modulesList;
|
||||
ListenerList<Listener> listeners;
|
||||
CriticalSection lock;
|
||||
|
||||
//==============================================================================
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AvailableModulesList)
|
||||
};
|
93
deps/juce/extras/Projucer/Source/Project/Modules/jucer_ModuleDescription.h
vendored
Normal file
93
deps/juce/extras/Projucer/Source/Project/Modules/jucer_ModuleDescription.h
vendored
Normal file
@ -0,0 +1,93 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
|
||||
//==============================================================================
|
||||
class ModuleDescription
|
||||
{
|
||||
public:
|
||||
ModuleDescription() = default;
|
||||
|
||||
ModuleDescription (const File& folder)
|
||||
: moduleFolder (folder),
|
||||
moduleInfo (parseJUCEHeaderMetadata (getHeader()))
|
||||
{
|
||||
}
|
||||
|
||||
bool isValid() const { return getID().isNotEmpty(); }
|
||||
|
||||
String getID() const { return moduleInfo [Ids::ID_uppercase].toString(); }
|
||||
String getVendor() const { return moduleInfo [Ids::vendor].toString(); }
|
||||
String getVersion() const { return moduleInfo [Ids::version].toString(); }
|
||||
String getName() const { return moduleInfo [Ids::name].toString(); }
|
||||
String getDescription() const { return moduleInfo [Ids::description].toString(); }
|
||||
String getLicense() const { return moduleInfo [Ids::license].toString(); }
|
||||
String getMinimumCppStandard() const { return moduleInfo [Ids::minimumCppStandard].toString(); }
|
||||
String getPreprocessorDefs() const { return moduleInfo [Ids::defines].toString(); }
|
||||
String getExtraSearchPaths() const { return moduleInfo [Ids::searchpaths].toString(); }
|
||||
var getModuleInfo() const { return moduleInfo; }
|
||||
File getModuleFolder() const { return moduleFolder; }
|
||||
|
||||
File getFolder() const
|
||||
{
|
||||
jassert (moduleFolder != File());
|
||||
|
||||
return moduleFolder;
|
||||
}
|
||||
|
||||
File getHeader() const
|
||||
{
|
||||
if (moduleFolder != File())
|
||||
{
|
||||
static const char* extensions[] = { ".h", ".hpp", ".hxx" };
|
||||
|
||||
for (auto e : extensions)
|
||||
{
|
||||
auto header = moduleFolder.getChildFile (moduleFolder.getFileName() + e);
|
||||
|
||||
if (header.existsAsFile())
|
||||
return header;
|
||||
}
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
StringArray getDependencies() const
|
||||
{
|
||||
auto moduleDependencies = StringArray::fromTokens (moduleInfo ["dependencies"].toString(), " \t;,", "\"'");
|
||||
moduleDependencies.trim();
|
||||
moduleDependencies.removeEmptyStrings();
|
||||
|
||||
return moduleDependencies;
|
||||
}
|
||||
|
||||
private:
|
||||
File moduleFolder;
|
||||
var moduleInfo;
|
||||
URL url;
|
||||
};
|
713
deps/juce/extras/Projucer/Source/Project/Modules/jucer_Modules.cpp
vendored
Normal file
713
deps/juce/extras/Projucer/Source/Project/Modules/jucer_Modules.cpp
vendored
Normal file
@ -0,0 +1,713 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#include "../../Application/jucer_Headers.h"
|
||||
#include "../../ProjectSaving/jucer_ProjectSaver.h"
|
||||
#include "../../ProjectSaving/jucer_ProjectExport_Xcode.h"
|
||||
#include "../../Application/jucer_Application.h"
|
||||
|
||||
//==============================================================================
|
||||
LibraryModule::LibraryModule (const ModuleDescription& d)
|
||||
: moduleInfo (d)
|
||||
{
|
||||
}
|
||||
|
||||
void LibraryModule::writeIncludes (ProjectSaver& projectSaver, OutputStream& out)
|
||||
{
|
||||
auto& project = projectSaver.getProject();
|
||||
auto& modules = project.getEnabledModules();
|
||||
|
||||
auto moduleID = getID();
|
||||
|
||||
if (modules.shouldCopyModuleFilesLocally (moduleID))
|
||||
{
|
||||
auto juceModuleFolder = moduleInfo.getFolder();
|
||||
|
||||
auto localModuleFolder = project.getLocalModuleFolder (moduleID);
|
||||
localModuleFolder.createDirectory();
|
||||
projectSaver.copyFolder (juceModuleFolder, localModuleFolder);
|
||||
}
|
||||
|
||||
out << "#include <" << moduleInfo.getModuleFolder().getFileName() << "/"
|
||||
<< moduleInfo.getHeader().getFileName()
|
||||
<< ">" << newLine;
|
||||
}
|
||||
|
||||
void LibraryModule::addSearchPathsToExporter (ProjectExporter& exporter) const
|
||||
{
|
||||
auto moduleRelativePath = exporter.getModuleFolderRelativeToProject (getID());
|
||||
|
||||
exporter.addToExtraSearchPaths (moduleRelativePath.getParentDirectory());
|
||||
|
||||
const auto libDirPlatform = [&]() -> String
|
||||
{
|
||||
if (exporter.isLinux())
|
||||
return "Linux";
|
||||
|
||||
if (exporter.isCodeBlocks() && exporter.isWindows())
|
||||
return "MinGW";
|
||||
|
||||
return exporter.getTypeInfoForExporter (exporter.getExporterIdentifier()).targetFolder;
|
||||
}();
|
||||
|
||||
auto libSubdirPath = moduleRelativePath.toUnixStyle() + "/libs/" + libDirPlatform;
|
||||
auto moduleLibDir = exporter.getProject().resolveFilename (libSubdirPath);
|
||||
|
||||
if (moduleLibDir.exists())
|
||||
exporter.addToModuleLibPaths ({ libSubdirPath, moduleRelativePath.getRoot() });
|
||||
|
||||
auto extraInternalSearchPaths = moduleInfo.getExtraSearchPaths().trim();
|
||||
|
||||
if (extraInternalSearchPaths.isNotEmpty())
|
||||
{
|
||||
auto paths = StringArray::fromTokens (extraInternalSearchPaths, true);
|
||||
|
||||
for (auto& path : paths)
|
||||
exporter.addToExtraSearchPaths (moduleRelativePath.getChildFile (path.unquoted()));
|
||||
}
|
||||
}
|
||||
|
||||
void LibraryModule::addDefinesToExporter (ProjectExporter& exporter) const
|
||||
{
|
||||
auto extraDefs = moduleInfo.getPreprocessorDefs().trim();
|
||||
|
||||
if (extraDefs.isNotEmpty())
|
||||
exporter.getExporterPreprocessorDefsValue() = exporter.getExporterPreprocessorDefsString() + "\n" + extraDefs;
|
||||
}
|
||||
|
||||
void LibraryModule::addCompileUnitsToExporter (ProjectExporter& exporter, ProjectSaver& projectSaver) const
|
||||
{
|
||||
auto& project = exporter.getProject();
|
||||
auto& modules = project.getEnabledModules();
|
||||
|
||||
auto moduleID = getID();
|
||||
|
||||
auto localModuleFolder = modules.shouldCopyModuleFilesLocally (moduleID) ? project.getLocalModuleFolder (moduleID)
|
||||
: moduleInfo.getFolder();
|
||||
|
||||
Array<File> compiled;
|
||||
findAndAddCompiledUnits (exporter, &projectSaver, compiled);
|
||||
|
||||
if (modules.shouldShowAllModuleFilesInProject (moduleID))
|
||||
addBrowseableCode (exporter, compiled, localModuleFolder);
|
||||
}
|
||||
|
||||
void LibraryModule::addLibsToExporter (ProjectExporter& exporter) const
|
||||
{
|
||||
auto parseAndAddLibsToList = [] (StringArray& libList, const String& libs)
|
||||
{
|
||||
libList.addTokens (libs, ", ", {});
|
||||
libList.trim();
|
||||
libList.removeDuplicates (false);
|
||||
};
|
||||
|
||||
auto& project = exporter.getProject();
|
||||
|
||||
if (exporter.isXcode())
|
||||
{
|
||||
auto& xcodeExporter = dynamic_cast<XcodeProjectExporter&> (exporter);
|
||||
|
||||
if (project.isAUPluginHost())
|
||||
{
|
||||
xcodeExporter.xcodeFrameworks.add ("CoreAudioKit");
|
||||
|
||||
if (xcodeExporter.isOSX())
|
||||
xcodeExporter.xcodeFrameworks.add ("AudioUnit");
|
||||
}
|
||||
|
||||
auto frameworks = moduleInfo.getModuleInfo() [xcodeExporter.isOSX() ? "OSXFrameworks" : "iOSFrameworks"].toString();
|
||||
xcodeExporter.xcodeFrameworks.addTokens (frameworks, ", ", {});
|
||||
|
||||
parseAndAddLibsToList (xcodeExporter.xcodeLibs, moduleInfo.getModuleInfo() [exporter.isOSX() ? "OSXLibs" : "iOSLibs"].toString());
|
||||
}
|
||||
else if (exporter.isLinux())
|
||||
{
|
||||
parseAndAddLibsToList (exporter.linuxLibs, moduleInfo.getModuleInfo() ["linuxLibs"].toString());
|
||||
parseAndAddLibsToList (exporter.linuxPackages, moduleInfo.getModuleInfo() ["linuxPackages"].toString());
|
||||
}
|
||||
else if (exporter.isWindows())
|
||||
{
|
||||
if (exporter.isCodeBlocks())
|
||||
parseAndAddLibsToList (exporter.mingwLibs, moduleInfo.getModuleInfo() ["mingwLibs"].toString());
|
||||
else
|
||||
parseAndAddLibsToList (exporter.windowsLibs, moduleInfo.getModuleInfo() ["windowsLibs"].toString());
|
||||
}
|
||||
else if (exporter.isAndroid())
|
||||
{
|
||||
parseAndAddLibsToList (exporter.androidLibs, moduleInfo.getModuleInfo() ["androidLibs"].toString());
|
||||
}
|
||||
}
|
||||
|
||||
void LibraryModule::addSettingsForModuleToExporter (ProjectExporter& exporter, ProjectSaver& projectSaver) const
|
||||
{
|
||||
addSearchPathsToExporter (exporter);
|
||||
addDefinesToExporter (exporter);
|
||||
addCompileUnitsToExporter (exporter, projectSaver);
|
||||
addLibsToExporter (exporter);
|
||||
}
|
||||
|
||||
void LibraryModule::getConfigFlags (Project& project, OwnedArray<Project::ConfigFlag>& flags) const
|
||||
{
|
||||
auto header = moduleInfo.getHeader();
|
||||
jassert (header.exists());
|
||||
|
||||
StringArray lines;
|
||||
header.readLines (lines);
|
||||
|
||||
for (int i = 0; i < lines.size(); ++i)
|
||||
{
|
||||
auto line = lines[i].trim();
|
||||
|
||||
if (line.startsWith ("/**") && line.containsIgnoreCase ("Config:"))
|
||||
{
|
||||
auto config = std::make_unique<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 = project.getConfigFlag (config->symbol);
|
||||
|
||||
i += 2;
|
||||
|
||||
if (lines[i].contains ("#define " + config->symbol))
|
||||
{
|
||||
auto value = lines[i].fromFirstOccurrenceOf ("#define " + config->symbol, false, true).trim();
|
||||
config->value.setDefault (value == "0" ? false : true);
|
||||
}
|
||||
|
||||
auto currentValue = config->value.get().toString();
|
||||
|
||||
if (currentValue == "enabled") config->value = true;
|
||||
else if (currentValue == "disabled") config->value = false;
|
||||
|
||||
flags.add (std::move (config));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void addFileWithGroups (Project::Item& group, const build_tools::RelativePath& file, const String& path)
|
||||
{
|
||||
auto slash = path.indexOfChar (File::getSeparatorChar());
|
||||
|
||||
if (slash >= 0)
|
||||
{
|
||||
auto topLevelGroup = path.substring (0, slash);
|
||||
auto remainingPath = path.substring (slash + 1);
|
||||
|
||||
auto newGroup = group.getOrCreateSubGroup (topLevelGroup);
|
||||
addFileWithGroups (newGroup, file, remainingPath);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (! group.containsChildForFile (file))
|
||||
group.addRelativeFile (file, -1, false);
|
||||
}
|
||||
}
|
||||
|
||||
struct FileSorter
|
||||
{
|
||||
static int compareElements (const File& f1, const File& f2)
|
||||
{
|
||||
return f1.getFileName().compareNatural (f2.getFileName());
|
||||
}
|
||||
};
|
||||
|
||||
void LibraryModule::findBrowseableFiles (const File& folder, Array<File>& filesFound) const
|
||||
{
|
||||
Array<File> tempList;
|
||||
FileSorter sorter;
|
||||
|
||||
for (const auto& iter : RangedDirectoryIterator (folder, true, "*", File::findFiles))
|
||||
if (! iter.isHidden() && iter.getFile().hasFileExtension (browseableFileExtensions))
|
||||
tempList.addSorted (sorter, iter.getFile());
|
||||
|
||||
filesFound.addArray (tempList);
|
||||
}
|
||||
|
||||
bool LibraryModule::CompileUnit::isNeededForExporter (ProjectExporter& exporter) const
|
||||
{
|
||||
if ((hasSuffix (file, "_OSX") && ! exporter.isOSX())
|
||||
|| (hasSuffix (file, "_iOS") && ! exporter.isiOS())
|
||||
|| (hasSuffix (file, "_Windows") && ! exporter.isWindows())
|
||||
|| (hasSuffix (file, "_Linux") && ! exporter.isLinux())
|
||||
|| (hasSuffix (file, "_Android") && ! exporter.isAndroid()))
|
||||
return false;
|
||||
|
||||
auto targetType = Project::getTargetTypeFromFilePath (file, false);
|
||||
|
||||
if (targetType != build_tools::ProjectType::Target::unspecified && ! exporter.shouldBuildTargetType (targetType))
|
||||
return false;
|
||||
|
||||
return exporter.usesMMFiles() ? isCompiledForObjC
|
||||
: isCompiledForNonObjC;
|
||||
}
|
||||
|
||||
String LibraryModule::CompileUnit::getFilenameForProxyFile() const
|
||||
{
|
||||
return "include_" + file.getFileName();
|
||||
}
|
||||
|
||||
bool LibraryModule::CompileUnit::hasSuffix (const File& f, const char* suffix)
|
||||
{
|
||||
auto fileWithoutSuffix = f.getFileNameWithoutExtension() + ".";
|
||||
|
||||
return fileWithoutSuffix.containsIgnoreCase (suffix + String ("."))
|
||||
|| fileWithoutSuffix.containsIgnoreCase (suffix + String ("_"));
|
||||
}
|
||||
|
||||
Array<LibraryModule::CompileUnit> LibraryModule::getAllCompileUnits (build_tools::ProjectType::Target::Type forTarget) const
|
||||
{
|
||||
auto files = getFolder().findChildFiles (File::findFiles, false);
|
||||
|
||||
FileSorter sorter;
|
||||
files.sort (sorter);
|
||||
|
||||
Array<LibraryModule::CompileUnit> units;
|
||||
|
||||
for (auto& file : files)
|
||||
{
|
||||
if (file.getFileName().startsWithIgnoreCase (getID())
|
||||
&& file.hasFileExtension (sourceFileExtensions))
|
||||
{
|
||||
if (forTarget == build_tools::ProjectType::Target::unspecified
|
||||
|| forTarget == Project::getTargetTypeFromFilePath (file, true))
|
||||
{
|
||||
CompileUnit cu;
|
||||
cu.file = file;
|
||||
units.add (std::move (cu));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (auto& cu : units)
|
||||
{
|
||||
cu.isCompiledForObjC = true;
|
||||
cu.isCompiledForNonObjC = ! cu.file.hasFileExtension ("mm;m;metal");
|
||||
|
||||
if (cu.isCompiledForNonObjC)
|
||||
if (cu.file.withFileExtension ("mm").existsAsFile())
|
||||
cu.isCompiledForObjC = false;
|
||||
|
||||
jassert (cu.isCompiledForObjC || cu.isCompiledForNonObjC);
|
||||
}
|
||||
|
||||
return units;
|
||||
}
|
||||
|
||||
void LibraryModule::findAndAddCompiledUnits (ProjectExporter& exporter,
|
||||
ProjectSaver* projectSaver,
|
||||
Array<File>& result,
|
||||
build_tools::ProjectType::Target::Type forTarget) const
|
||||
{
|
||||
for (auto& cu : getAllCompileUnits (forTarget))
|
||||
{
|
||||
if (cu.isNeededForExporter (exporter))
|
||||
{
|
||||
auto localFile = exporter.getProject().getGeneratedCodeFolder()
|
||||
.getChildFile (cu.getFilenameForProxyFile());
|
||||
result.add (localFile);
|
||||
|
||||
if (projectSaver != nullptr)
|
||||
projectSaver->addFileToGeneratedGroup (localFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void LibraryModule::addBrowseableCode (ProjectExporter& exporter, const Array<File>& compiled, const File& localModuleFolder) const
|
||||
{
|
||||
if (sourceFiles.isEmpty())
|
||||
findBrowseableFiles (localModuleFolder, sourceFiles);
|
||||
|
||||
auto sourceGroup = Project::Item::createGroup (exporter.getProject(), getID(), "__mainsourcegroup" + getID(), false);
|
||||
auto moduleFromProject = exporter.getModuleFolderRelativeToProject (getID());
|
||||
auto moduleHeader = moduleInfo.getHeader();
|
||||
|
||||
auto& project = exporter.getProject();
|
||||
|
||||
if (project.getEnabledModules().shouldCopyModuleFilesLocally (getID()))
|
||||
moduleHeader = project.getLocalModuleFolder (getID()).getChildFile (moduleHeader.getFileName());
|
||||
|
||||
auto isModuleHeader = [&] (const File& f) { return f.getFileName() == moduleHeader.getFileName(); };
|
||||
|
||||
for (auto& sourceFile : sourceFiles)
|
||||
{
|
||||
auto pathWithinModule = build_tools::getRelativePathFrom (sourceFile, 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 (sourceFile)) && ! isModuleHeader (sourceFile))
|
||||
addFileWithGroups (sourceGroup, moduleFromProject.getChildFile (pathWithinModule), pathWithinModule);
|
||||
}
|
||||
|
||||
sourceGroup.sortAlphabetically (true, true);
|
||||
sourceGroup.addFileAtIndex (moduleHeader, -1, false);
|
||||
|
||||
exporter.getModulesGroup().state.appendChild (sourceGroup.state.createCopy(), nullptr);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
EnabledModulesList::EnabledModulesList (Project& p, const ValueTree& s)
|
||||
: project (p), state (s)
|
||||
{
|
||||
}
|
||||
|
||||
StringArray EnabledModulesList::getAllModules() const
|
||||
{
|
||||
StringArray moduleIDs;
|
||||
|
||||
for (int i = 0; i < getNumModules(); ++i)
|
||||
moduleIDs.add (getModuleID (i));
|
||||
|
||||
return moduleIDs;
|
||||
}
|
||||
|
||||
void EnabledModulesList::createRequiredModules (OwnedArray<LibraryModule>& modules)
|
||||
{
|
||||
for (int i = 0; i < getNumModules(); ++i)
|
||||
modules.add (new LibraryModule (getModuleInfo (getModuleID (i))));
|
||||
}
|
||||
|
||||
void EnabledModulesList::sortAlphabetically()
|
||||
{
|
||||
struct ModuleTreeSorter
|
||||
{
|
||||
static int compareElements (const ValueTree& m1, const ValueTree& m2)
|
||||
{
|
||||
return m1[Ids::ID].toString().compareIgnoreCase (m2[Ids::ID]);
|
||||
}
|
||||
};
|
||||
|
||||
ModuleTreeSorter sorter;
|
||||
|
||||
const ScopedLock sl (stateLock);
|
||||
state.sort (sorter, getUndoManager(), false);
|
||||
}
|
||||
|
||||
File EnabledModulesList::getDefaultModulesFolder() const
|
||||
{
|
||||
File globalPath (getAppSettings().getStoredPath (Ids::defaultJuceModulePath, TargetOS::getThisOS()).get().toString());
|
||||
|
||||
if (globalPath.exists())
|
||||
return globalPath;
|
||||
|
||||
for (auto& exporterPathModule : project.getExporterPathsModulesList().getAllModules())
|
||||
{
|
||||
auto f = exporterPathModule.second;
|
||||
|
||||
if (f.isDirectory())
|
||||
return f.getParentDirectory();
|
||||
}
|
||||
|
||||
return File::getCurrentWorkingDirectory();
|
||||
}
|
||||
|
||||
ModuleDescription EnabledModulesList::getModuleInfo (const String& moduleID) const
|
||||
{
|
||||
return ModuleDescription (project.getModuleWithID (moduleID).second);
|
||||
}
|
||||
|
||||
bool EnabledModulesList::isModuleEnabled (const String& moduleID) const
|
||||
{
|
||||
const ScopedLock sl (stateLock);
|
||||
return state.getChildWithProperty (Ids::ID, moduleID).isValid();
|
||||
}
|
||||
|
||||
static void getDependencies (Project& project, const String& moduleID, StringArray& dependencies)
|
||||
{
|
||||
auto info = project.getEnabledModules().getModuleInfo (moduleID);
|
||||
|
||||
for (auto uid : info.getDependencies())
|
||||
{
|
||||
if (! dependencies.contains (uid, true))
|
||||
{
|
||||
dependencies.add (uid);
|
||||
getDependencies (project, uid, dependencies);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
StringArray EnabledModulesList::getExtraDependenciesNeeded (const String& moduleID) const
|
||||
{
|
||||
StringArray dependencies, extraDepsNeeded;
|
||||
getDependencies (project, moduleID, dependencies);
|
||||
|
||||
for (auto dep : dependencies)
|
||||
if (dep != moduleID && ! isModuleEnabled (dep))
|
||||
extraDepsNeeded.add (dep);
|
||||
|
||||
return extraDepsNeeded;
|
||||
}
|
||||
|
||||
bool EnabledModulesList::tryToFixMissingDependencies (const String& moduleID)
|
||||
{
|
||||
auto copyLocally = areMostModulesCopiedLocally();
|
||||
auto useGlobalPath = areMostModulesUsingGlobalPath();
|
||||
|
||||
StringArray missing;
|
||||
|
||||
for (auto missingModule : getExtraDependenciesNeeded (moduleID))
|
||||
{
|
||||
auto mod = project.getModuleWithID (missingModule);
|
||||
|
||||
if (mod.second != File())
|
||||
addModule (mod.second, copyLocally, useGlobalPath);
|
||||
else
|
||||
missing.add (missingModule);
|
||||
}
|
||||
|
||||
return (missing.size() == 0);
|
||||
}
|
||||
|
||||
bool EnabledModulesList::doesModuleHaveHigherCppStandardThanProject (const String& moduleID) const
|
||||
{
|
||||
auto projectCppStandard = project.getCppStandardString();
|
||||
|
||||
if (projectCppStandard == Project::getCppStandardVars().getLast().toString())
|
||||
return false;
|
||||
|
||||
auto moduleCppStandard = getModuleInfo (moduleID).getMinimumCppStandard();
|
||||
|
||||
return (moduleCppStandard.getIntValue() > projectCppStandard.getIntValue());
|
||||
}
|
||||
|
||||
bool EnabledModulesList::shouldUseGlobalPath (const String& moduleID) const
|
||||
{
|
||||
const ScopedLock sl (stateLock);
|
||||
return (bool) shouldUseGlobalPathValue (moduleID).getValue();
|
||||
}
|
||||
|
||||
Value EnabledModulesList::shouldUseGlobalPathValue (const String& moduleID) const
|
||||
{
|
||||
const ScopedLock sl (stateLock);
|
||||
return state.getChildWithProperty (Ids::ID, moduleID)
|
||||
.getPropertyAsValue (Ids::useGlobalPath, getUndoManager());
|
||||
}
|
||||
|
||||
bool EnabledModulesList::shouldShowAllModuleFilesInProject (const String& moduleID) const
|
||||
{
|
||||
return (bool) shouldShowAllModuleFilesInProjectValue (moduleID).getValue();
|
||||
}
|
||||
|
||||
Value EnabledModulesList::shouldShowAllModuleFilesInProjectValue (const String& moduleID) const
|
||||
{
|
||||
const ScopedLock sl (stateLock);
|
||||
return state.getChildWithProperty (Ids::ID, moduleID)
|
||||
.getPropertyAsValue (Ids::showAllCode, getUndoManager());
|
||||
}
|
||||
|
||||
bool EnabledModulesList::shouldCopyModuleFilesLocally (const String& moduleID) const
|
||||
{
|
||||
return (bool) shouldCopyModuleFilesLocallyValue (moduleID).getValue();
|
||||
}
|
||||
|
||||
Value EnabledModulesList::shouldCopyModuleFilesLocallyValue (const String& moduleID) const
|
||||
{
|
||||
const ScopedLock sl (stateLock);
|
||||
return state.getChildWithProperty (Ids::ID, moduleID)
|
||||
.getPropertyAsValue (Ids::useLocalCopy, getUndoManager());
|
||||
}
|
||||
|
||||
bool EnabledModulesList::areMostModulesUsingGlobalPath() const
|
||||
{
|
||||
int numYes = 0, numNo = 0;
|
||||
|
||||
for (auto i = getNumModules(); --i >= 0;)
|
||||
{
|
||||
if (shouldUseGlobalPath (getModuleID (i)))
|
||||
++numYes;
|
||||
else
|
||||
++numNo;
|
||||
}
|
||||
|
||||
return numYes > numNo;
|
||||
}
|
||||
|
||||
bool EnabledModulesList::areMostModulesCopiedLocally() const
|
||||
{
|
||||
int numYes = 0, numNo = 0;
|
||||
|
||||
for (auto i = getNumModules(); --i >= 0;)
|
||||
{
|
||||
if (shouldCopyModuleFilesLocally (getModuleID (i)))
|
||||
++numYes;
|
||||
else
|
||||
++numNo;
|
||||
}
|
||||
|
||||
return numYes > numNo;
|
||||
}
|
||||
|
||||
StringArray EnabledModulesList::getModulesWithHigherCppStandardThanProject() const
|
||||
{
|
||||
StringArray list;
|
||||
|
||||
for (auto& module : getAllModules())
|
||||
if (doesModuleHaveHigherCppStandardThanProject (module))
|
||||
list.add (module);
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
StringArray EnabledModulesList::getModulesWithMissingDependencies() const
|
||||
{
|
||||
StringArray list;
|
||||
|
||||
for (auto& module : getAllModules())
|
||||
if (getExtraDependenciesNeeded (module).size() > 0)
|
||||
list.add (module);
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
String EnabledModulesList::getHighestModuleCppStandard() const
|
||||
{
|
||||
auto highestCppStandard = Project::getCppStandardVars()[0].toString();
|
||||
|
||||
for (auto& mod : getAllModules())
|
||||
{
|
||||
auto moduleCppStandard = getModuleInfo (mod).getMinimumCppStandard();
|
||||
|
||||
if (moduleCppStandard == "latest")
|
||||
return moduleCppStandard;
|
||||
|
||||
if (moduleCppStandard.getIntValue() > highestCppStandard.getIntValue())
|
||||
highestCppStandard = moduleCppStandard;
|
||||
}
|
||||
|
||||
return highestCppStandard;
|
||||
}
|
||||
|
||||
void EnabledModulesList::addModule (const File& moduleFolder, bool copyLocally, bool useGlobalPath)
|
||||
{
|
||||
ModuleDescription info (moduleFolder);
|
||||
|
||||
if (info.isValid())
|
||||
{
|
||||
auto moduleID = info.getID();
|
||||
|
||||
if (! isModuleEnabled (moduleID))
|
||||
{
|
||||
ValueTree module (Ids::MODULE);
|
||||
module.setProperty (Ids::ID, moduleID, getUndoManager());
|
||||
|
||||
{
|
||||
const ScopedLock sl (stateLock);
|
||||
state.appendChild (module, getUndoManager());
|
||||
}
|
||||
|
||||
sortAlphabetically();
|
||||
|
||||
shouldShowAllModuleFilesInProjectValue (moduleID) = true;
|
||||
shouldCopyModuleFilesLocallyValue (moduleID) = copyLocally;
|
||||
shouldUseGlobalPathValue (moduleID) = useGlobalPath;
|
||||
|
||||
build_tools::RelativePath path (moduleFolder.getParentDirectory(),
|
||||
project.getProjectFolder(),
|
||||
build_tools::RelativePath::projectFolder);
|
||||
|
||||
for (Project::ExporterIterator exporter (project); exporter.next();)
|
||||
exporter->getPathForModuleValue (moduleID) = path.toUnixStyle();
|
||||
|
||||
if (! useGlobalPath)
|
||||
project.rescanExporterPathModules (false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void EnabledModulesList::addModuleInteractive (const String& moduleID)
|
||||
{
|
||||
auto f = project.getModuleWithID (moduleID).second;
|
||||
|
||||
if (f != File())
|
||||
{
|
||||
addModule (f, areMostModulesCopiedLocally(), areMostModulesUsingGlobalPath());
|
||||
return;
|
||||
}
|
||||
|
||||
addModuleFromUserSelectedFile();
|
||||
}
|
||||
|
||||
void EnabledModulesList::addModuleFromUserSelectedFile()
|
||||
{
|
||||
chooser = std::make_unique<FileChooser> ("Select a module to add...", getDefaultModulesFolder(), "");
|
||||
auto flags = FileBrowserComponent::openMode | FileBrowserComponent::canSelectDirectories;
|
||||
|
||||
chooser->launchAsync (flags, [this] (const FileChooser& fc)
|
||||
{
|
||||
if (fc.getResult() == File{})
|
||||
return;
|
||||
|
||||
addModuleOfferingToCopy (fc.getResult(), true);
|
||||
});
|
||||
}
|
||||
|
||||
void EnabledModulesList::addModuleOfferingToCopy (const File& f, bool isFromUserSpecifiedFolder)
|
||||
{
|
||||
ModuleDescription m (f);
|
||||
|
||||
if (! m.isValid())
|
||||
{
|
||||
AlertWindow::showMessageBoxAsync (MessageBoxIconType::InfoIcon,
|
||||
"Add Module", "This wasn't a valid module folder!");
|
||||
return;
|
||||
}
|
||||
|
||||
if (isModuleEnabled (m.getID()))
|
||||
{
|
||||
AlertWindow::showMessageBoxAsync (MessageBoxIconType::InfoIcon,
|
||||
"Add Module", "The project already contains this module!");
|
||||
return;
|
||||
}
|
||||
|
||||
addModule (m.getModuleFolder(), areMostModulesCopiedLocally(),
|
||||
isFromUserSpecifiedFolder ? false : areMostModulesUsingGlobalPath());
|
||||
}
|
||||
|
||||
void EnabledModulesList::removeModule (String moduleID) // must be pass-by-value, and not a const ref!
|
||||
{
|
||||
{
|
||||
const ScopedLock sl (stateLock);
|
||||
|
||||
for (auto 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);
|
||||
}
|
148
deps/juce/extras/Projucer/Source/Project/Modules/jucer_Modules.h
vendored
Normal file
148
deps/juce/extras/Projucer/Source/Project/Modules/jucer_Modules.h
vendored
Normal file
@ -0,0 +1,148 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../jucer_Project.h"
|
||||
|
||||
class ProjectExporter;
|
||||
class ProjectSaver;
|
||||
|
||||
//==============================================================================
|
||||
class LibraryModule
|
||||
{
|
||||
public:
|
||||
LibraryModule (const ModuleDescription&);
|
||||
|
||||
bool isValid() const { return moduleInfo.isValid(); }
|
||||
String getID() const { return moduleInfo.getID(); }
|
||||
String getVendor() const { return moduleInfo.getVendor(); }
|
||||
String getVersion() const { return moduleInfo.getVersion(); }
|
||||
String getName() const { return moduleInfo.getName(); }
|
||||
String getDescription() const { return moduleInfo.getDescription(); }
|
||||
String getLicense() const { return moduleInfo.getLicense(); }
|
||||
String getMinimumCppStandard() const { return moduleInfo.getMinimumCppStandard(); }
|
||||
|
||||
File getFolder() const { return moduleInfo.getFolder(); }
|
||||
|
||||
void writeIncludes (ProjectSaver&, OutputStream&);
|
||||
void addSettingsForModuleToExporter (ProjectExporter&, ProjectSaver&) const;
|
||||
void getConfigFlags (Project&, OwnedArray<Project::ConfigFlag>& flags) const;
|
||||
void findBrowseableFiles (const File& localModuleFolder, Array<File>& files) const;
|
||||
|
||||
struct CompileUnit
|
||||
{
|
||||
File file;
|
||||
bool isCompiledForObjC = false, isCompiledForNonObjC = false;
|
||||
|
||||
bool isNeededForExporter (ProjectExporter&) const;
|
||||
String getFilenameForProxyFile() const;
|
||||
static bool hasSuffix (const File&, const char*);
|
||||
};
|
||||
|
||||
Array<CompileUnit> getAllCompileUnits (build_tools::ProjectType::Target::Type forTarget =
|
||||
build_tools::ProjectType::Target::unspecified) const;
|
||||
void findAndAddCompiledUnits (ProjectExporter&, ProjectSaver*, Array<File>& result,
|
||||
build_tools::ProjectType::Target::Type forTarget =
|
||||
build_tools::ProjectType::Target::unspecified) const;
|
||||
|
||||
ModuleDescription moduleInfo;
|
||||
|
||||
private:
|
||||
void addSearchPathsToExporter (ProjectExporter&) const;
|
||||
void addDefinesToExporter (ProjectExporter&) const;
|
||||
void addCompileUnitsToExporter (ProjectExporter&, ProjectSaver&) const;
|
||||
void addLibsToExporter (ProjectExporter&) const;
|
||||
|
||||
void addBrowseableCode (ProjectExporter&, const Array<File>& compiled, const File& localModuleFolder) const;
|
||||
|
||||
mutable Array<File> sourceFiles;
|
||||
OwnedArray<Project::ConfigFlag> configFlags;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
class EnabledModulesList
|
||||
{
|
||||
public:
|
||||
EnabledModulesList (Project&, const ValueTree&);
|
||||
|
||||
//==============================================================================
|
||||
ValueTree getState() const { return state; }
|
||||
|
||||
StringArray getAllModules() const;
|
||||
void createRequiredModules (OwnedArray<LibraryModule>& modules);
|
||||
void sortAlphabetically();
|
||||
|
||||
File getDefaultModulesFolder() const;
|
||||
|
||||
int getNumModules() const { return state.getNumChildren(); }
|
||||
String getModuleID (int index) const { return state.getChild (index) [Ids::ID].toString(); }
|
||||
|
||||
ModuleDescription getModuleInfo (const String& moduleID) const;
|
||||
|
||||
bool isModuleEnabled (const String& moduleID) const;
|
||||
|
||||
StringArray getExtraDependenciesNeeded (const String& moduleID) const;
|
||||
bool tryToFixMissingDependencies (const String& moduleID);
|
||||
|
||||
bool doesModuleHaveHigherCppStandardThanProject (const String& moduleID) const;
|
||||
|
||||
bool shouldUseGlobalPath (const String& moduleID) const;
|
||||
Value shouldUseGlobalPathValue (const String& moduleID) const;
|
||||
|
||||
bool shouldShowAllModuleFilesInProject (const String& moduleID) const;
|
||||
Value shouldShowAllModuleFilesInProjectValue (const String& moduleID) const;
|
||||
|
||||
bool shouldCopyModuleFilesLocally (const String& moduleID) const;
|
||||
Value shouldCopyModuleFilesLocallyValue (const String& moduleID) const;
|
||||
|
||||
bool areMostModulesUsingGlobalPath() const;
|
||||
bool areMostModulesCopiedLocally() const;
|
||||
|
||||
StringArray getModulesWithHigherCppStandardThanProject() const;
|
||||
StringArray getModulesWithMissingDependencies() const;
|
||||
|
||||
String getHighestModuleCppStandard() const;
|
||||
|
||||
//==============================================================================
|
||||
void addModule (const File& moduleManifestFile, bool copyLocally, bool useGlobalPath);
|
||||
void addModuleInteractive (const String& moduleID);
|
||||
void addModuleFromUserSelectedFile();
|
||||
void addModuleOfferingToCopy (const File&, bool isFromUserSpecifiedFolder);
|
||||
|
||||
void removeModule (String moduleID);
|
||||
|
||||
private:
|
||||
UndoManager* getUndoManager() const { return project.getUndoManagerFor (state); }
|
||||
|
||||
Project& project;
|
||||
|
||||
CriticalSection stateLock;
|
||||
ValueTree state;
|
||||
|
||||
std::unique_ptr<FileChooser> chooser;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (EnabledModulesList)
|
||||
};
|
401
deps/juce/extras/Projucer/Source/Project/UI/Sidebar/jucer_ExporterTreeItems.h
vendored
Normal file
401
deps/juce/extras/Projucer/Source/Project/UI/Sidebar/jucer_ExporterTreeItems.h
vendored
Normal file
@ -0,0 +1,401 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
|
||||
//==============================================================================
|
||||
class ExporterItem : public ProjectTreeItemBase,
|
||||
private Value::Listener
|
||||
{
|
||||
public:
|
||||
ExporterItem (Project& p, ProjectExporter* e, int index)
|
||||
: project (p), exporter (e), configListTree (exporter->getConfigurations()),
|
||||
exporterIndex (index)
|
||||
{
|
||||
exporter->initialiseDependencyPathValues();
|
||||
configListTree.addListener (this);
|
||||
targetLocationValue.referTo (exporter->getTargetLocationValue());
|
||||
targetLocationValue.addListener (this);
|
||||
}
|
||||
|
||||
int getItemHeight() const override { return 25; }
|
||||
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->getUniqueName(); }
|
||||
void setName (const String&) override {}
|
||||
bool isMissing() const override { return false; }
|
||||
String getTooltip() override { return getDisplayName(); }
|
||||
|
||||
static Icon getIconForExporter (ProjectExporter* e)
|
||||
{
|
||||
if (e != nullptr)
|
||||
{
|
||||
if (e->isXcode()) return Icon (getIcons().xcode, Colours::transparentBlack);
|
||||
else if (e->isVisualStudio()) return Icon (getIcons().visualStudio, Colours::transparentBlack);
|
||||
else if (e->isAndroid()) return Icon (getIcons().android, Colours::transparentBlack);
|
||||
else if (e->isCodeBlocks()) return Icon (getIcons().codeBlocks, Colours::transparentBlack);
|
||||
else if (e->isMakefile()) return Icon (getIcons().linux, Colours::transparentBlack);
|
||||
else if (e->isCLion()) return Icon (getIcons().clion, Colours::transparentBlack);
|
||||
}
|
||||
|
||||
return Icon();
|
||||
}
|
||||
|
||||
Icon getIcon() const override
|
||||
{
|
||||
return getIconForExporter (exporter.get()).withColour (getContentColour (true));
|
||||
}
|
||||
|
||||
void showDocument() override
|
||||
{
|
||||
showSettingsPage (new SettingsComp (*exporter));
|
||||
}
|
||||
|
||||
void deleteItem() override
|
||||
{
|
||||
auto resultCallback = [safeThis = WeakReference<ExporterItem> { this }] (int result)
|
||||
{
|
||||
if (safeThis == nullptr || result == 0)
|
||||
return;
|
||||
|
||||
safeThis->closeSettingsPage();
|
||||
|
||||
auto parent = safeThis->exporter->settings.getParent();
|
||||
parent.removeChild (safeThis->exporter->settings,
|
||||
safeThis->project.getUndoManagerFor (parent));
|
||||
};
|
||||
|
||||
AlertWindow::showOkCancelBox (MessageBoxIconType::WarningIcon,
|
||||
"Delete Exporter",
|
||||
"Are you sure you want to delete this export target?",
|
||||
"",
|
||||
"",
|
||||
nullptr,
|
||||
ModalCallbackFunction::create (std::move (resultCallback)));
|
||||
}
|
||||
|
||||
void addSubItems() override
|
||||
{
|
||||
for (ProjectExporter::ConfigIterator config (*exporter); config.next();)
|
||||
addSubItem (new ConfigItem (config.config, *exporter));
|
||||
}
|
||||
|
||||
void showPopupMenu (Point<int> p) override
|
||||
{
|
||||
PopupMenu menu;
|
||||
menu.addItem (1, "Add a new configuration", exporter->supportsUserDefinedConfigurations());
|
||||
menu.addItem (2, "Save this exporter");
|
||||
menu.addSeparator();
|
||||
menu.addItem (3, "Delete this exporter");
|
||||
|
||||
launchPopupMenu (menu, p);
|
||||
}
|
||||
|
||||
void showAddMenu (Point<int> p) override
|
||||
{
|
||||
PopupMenu menu;
|
||||
menu.addItem (1, "Add a new configuration", exporter->supportsUserDefinedConfigurations());
|
||||
|
||||
launchPopupMenu (menu, p);
|
||||
}
|
||||
|
||||
void handlePopupMenuResult (int resultCode) override
|
||||
{
|
||||
if (resultCode == 1)
|
||||
exporter->addNewConfiguration (false);
|
||||
else if (resultCode == 2)
|
||||
project.saveProject (Async::yes, exporter.get(), nullptr);
|
||||
else if (resultCode == 3)
|
||||
deleteAllSelectedItems();
|
||||
}
|
||||
|
||||
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
|
||||
{
|
||||
auto 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&, int) override { refreshIfNeeded (parentTree); }
|
||||
void valueTreeChildOrderChanged (ValueTree& parentTree, int, int) override { refreshIfNeeded (parentTree); }
|
||||
|
||||
void refreshIfNeeded (ValueTree& changedTree)
|
||||
{
|
||||
if (changedTree == configListTree)
|
||||
refreshSubItems();
|
||||
}
|
||||
|
||||
private:
|
||||
Project& project;
|
||||
std::unique_ptr<ProjectExporter> exporter;
|
||||
ValueTree configListTree;
|
||||
int exporterIndex;
|
||||
|
||||
Value targetLocationValue;
|
||||
|
||||
void valueChanged (Value& value) override
|
||||
{
|
||||
if (value == exporter->getTargetLocationValue())
|
||||
refreshSubItems();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
struct SettingsComp : public Component
|
||||
{
|
||||
SettingsComp (ProjectExporter& exp)
|
||||
: group (exp.getUniqueName(),
|
||||
ExporterItem::getIconForExporter (&exp),
|
||||
exp.getDescription())
|
||||
{
|
||||
addAndMakeVisible (group);
|
||||
|
||||
PropertyListBuilder props;
|
||||
exp.createPropertyEditors (props);
|
||||
group.setProperties (props);
|
||||
parentSizeChanged();
|
||||
}
|
||||
|
||||
void parentSizeChanged() override { updateSize (*this, group); }
|
||||
void resized() override { group.setBounds (getLocalBounds().withTrimmedLeft (12)); }
|
||||
|
||||
PropertyGroupComponent group;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (SettingsComp)
|
||||
};
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ExporterItem)
|
||||
JUCE_DECLARE_WEAK_REFERENCEABLE (ExporterItem)
|
||||
};
|
||||
|
||||
|
||||
//==============================================================================
|
||||
class ConfigItem : public ProjectTreeItemBase
|
||||
{
|
||||
public:
|
||||
ConfigItem (const ProjectExporter::BuildConfiguration::Ptr& conf, ProjectExporter& e)
|
||||
: config (conf), exporter (e), configTree (config->config)
|
||||
{
|
||||
jassert (config != nullptr);
|
||||
configTree.addListener (this);
|
||||
}
|
||||
|
||||
bool isMissing() const 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, getContentColour (true)); }
|
||||
void itemOpennessChanged (bool) override {}
|
||||
|
||||
void showDocument() override
|
||||
{
|
||||
showSettingsPage (new SettingsComp (*config));
|
||||
}
|
||||
|
||||
void deleteItem() override
|
||||
{
|
||||
AlertWindow::showOkCancelBox (MessageBoxIconType::WarningIcon,
|
||||
"Delete Configuration",
|
||||
"Are you sure you want to delete this configuration?",
|
||||
"",
|
||||
"",
|
||||
nullptr,
|
||||
ModalCallbackFunction::create ([parent = WeakReference<ConfigItem> { this }] (int result)
|
||||
{
|
||||
if (parent == nullptr)
|
||||
return;
|
||||
|
||||
if (result == 0)
|
||||
return;
|
||||
|
||||
parent->closeSettingsPage();
|
||||
parent->config->removeFromExporter();
|
||||
}));
|
||||
}
|
||||
|
||||
void showPopupMenu (Point<int> p) override
|
||||
{
|
||||
bool enabled = exporter.supportsUserDefinedConfigurations();
|
||||
|
||||
PopupMenu menu;
|
||||
menu.addItem (1, "Create a copy of this configuration", enabled);
|
||||
menu.addSeparator();
|
||||
menu.addItem (2, "Delete this configuration", enabled);
|
||||
|
||||
launchPopupMenu (menu, p);
|
||||
}
|
||||
|
||||
void handlePopupMenuResult (int resultCode) override
|
||||
{
|
||||
if (resultCode == 1)
|
||||
exporter.addNewConfigurationFromExisting (*config);
|
||||
else if (resultCode == 2)
|
||||
deleteAllSelectedItems();
|
||||
}
|
||||
|
||||
var getDragSourceDescription() override
|
||||
{
|
||||
return getParentItem()->getUniqueName() + "||" + config->getName();
|
||||
}
|
||||
|
||||
void valueTreePropertyChanged (ValueTree&, const Identifier&) override { repaintItem(); }
|
||||
|
||||
private:
|
||||
ProjectExporter::BuildConfiguration::Ptr config;
|
||||
ProjectExporter& exporter;
|
||||
ValueTree configTree;
|
||||
|
||||
//==============================================================================
|
||||
class SettingsComp : public Component
|
||||
{
|
||||
public:
|
||||
SettingsComp (ProjectExporter::BuildConfiguration& conf)
|
||||
: group (conf.exporter.getUniqueName() + " - " + conf.getName(), Icon (getIcons().config, Colours::transparentBlack))
|
||||
{
|
||||
addAndMakeVisible (group);
|
||||
|
||||
PropertyListBuilder props;
|
||||
conf.createPropertyEditors (props);
|
||||
group.setProperties (props);
|
||||
parentSizeChanged();
|
||||
}
|
||||
|
||||
void parentSizeChanged() override { updateSize (*this, group); }
|
||||
|
||||
void resized() override { group.setBounds (getLocalBounds().withTrimmedLeft (12)); }
|
||||
|
||||
private:
|
||||
PropertyGroupComponent group;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (SettingsComp)
|
||||
};
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ConfigItem)
|
||||
JUCE_DECLARE_WEAK_REFERENCEABLE (ConfigItem)
|
||||
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
class ExportersTreeRoot : public ProjectTreeItemBase
|
||||
{
|
||||
public:
|
||||
ExportersTreeRoot (Project& p)
|
||||
: project (p),
|
||||
exportersTree (project.getExporters())
|
||||
{
|
||||
exportersTree.addListener (this);
|
||||
}
|
||||
|
||||
bool isRoot() const override { return true; }
|
||||
bool canBeSelected() const override { return true; }
|
||||
bool isMissing() const override { return false; }
|
||||
bool mightContainSubItems() override { return project.getNumExporters() > 0; }
|
||||
String getUniqueName() const override { return "exporters"; }
|
||||
String getRenamingName() const override { return getDisplayName(); }
|
||||
String getDisplayName() const override { return "Exporters"; }
|
||||
void setName (const String&) override {}
|
||||
Icon getIcon() const override { return project.getMainGroup().getIcon (isOpen()).withColour (getContentColour (true)); }
|
||||
|
||||
void showPopupMenu (Point<int>) override
|
||||
{
|
||||
if (auto* pcc = getProjectContentComponent())
|
||||
pcc->showNewExporterMenu();
|
||||
}
|
||||
|
||||
void addSubItems() override
|
||||
{
|
||||
int i = 0;
|
||||
for (Project::ExporterIterator exporter (project); exporter.next(); ++i)
|
||||
addSubItem (new TreeItemTypes::ExporterItem (project, exporter.exporter.release(), i));
|
||||
}
|
||||
|
||||
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), project.getUndoManagerFor (exportersTree));
|
||||
}
|
||||
|
||||
void itemOpennessChanged (bool isNowOpen) override
|
||||
{
|
||||
if (isNowOpen)
|
||||
refreshSubItems();
|
||||
}
|
||||
|
||||
void removeExporter (int index)
|
||||
{
|
||||
if (auto* exporter = dynamic_cast<TreeItemTypes::ExporterItem*> (getSubItem (index)))
|
||||
exporter->deleteItem();
|
||||
}
|
||||
|
||||
private:
|
||||
Project& project;
|
||||
ValueTree exportersTree;
|
||||
|
||||
//==============================================================================
|
||||
void valueTreeChildAdded (ValueTree& parentTree, ValueTree&) override { refreshIfNeeded (parentTree); }
|
||||
void valueTreeChildRemoved (ValueTree& parentTree, ValueTree&, int) override { refreshIfNeeded (parentTree); }
|
||||
void valueTreeChildOrderChanged (ValueTree& parentTree, int, int) override { refreshIfNeeded (parentTree); }
|
||||
|
||||
void refreshIfNeeded (ValueTree& changedTree)
|
||||
{
|
||||
if (changedTree == exportersTree)
|
||||
refreshSubItems();
|
||||
}
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ExportersTreeRoot)
|
||||
};
|
882
deps/juce/extras/Projucer/Source/Project/UI/Sidebar/jucer_FileTreeItems.h
vendored
Normal file
882
deps/juce/extras/Projucer/Source/Project/UI/Sidebar/jucer_FileTreeItems.h
vendored
Normal file
@ -0,0 +1,882 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
|
||||
//==============================================================================
|
||||
class FileTreeItemBase : public JucerTreeViewBase,
|
||||
private ValueTree::Listener
|
||||
{
|
||||
public:
|
||||
FileTreeItemBase (const Project::Item& projectItem)
|
||||
: item (projectItem), isFileMissing (false)
|
||||
{
|
||||
item.state.addListener (this);
|
||||
}
|
||||
|
||||
~FileTreeItemBase() override
|
||||
{
|
||||
item.state.removeListener (this);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
virtual bool acceptsFileDrop (const StringArray& files) const = 0;
|
||||
virtual bool acceptsDragItems (const OwnedArray<Project::Item>& selectedNodes) = 0;
|
||||
|
||||
//==============================================================================
|
||||
String getDisplayName() const override { return item.getName(); }
|
||||
String getRenamingName() const override { return getDisplayName(); }
|
||||
void setName (const String& newName) override { item.getNameValue() = newName; }
|
||||
bool isMissing() const override { return isFileMissing; }
|
||||
virtual File getFile() const { return item.getFile(); }
|
||||
|
||||
void deleteItem() override { item.removeItemFromProject(); }
|
||||
|
||||
void deleteAllSelectedItems() override
|
||||
{
|
||||
auto* tree = getOwnerView();
|
||||
Array<File> filesToTrash;
|
||||
Array<Project::Item> itemsToRemove;
|
||||
|
||||
for (int i = 0; i < tree->getNumSelectedItems(); ++i)
|
||||
{
|
||||
if (auto* p = dynamic_cast<FileTreeItemBase*> (tree->getSelectedItem (i)))
|
||||
{
|
||||
itemsToRemove.add (p->item);
|
||||
|
||||
if (p->item.isGroup())
|
||||
{
|
||||
for (int j = 0; j < p->item.getNumChildren(); ++j)
|
||||
{
|
||||
auto associatedFile = p->item.getChild (j).getFile();
|
||||
|
||||
if (associatedFile.existsAsFile())
|
||||
filesToTrash.addIfNotAlreadyThere (associatedFile);
|
||||
}
|
||||
}
|
||||
else if (p->getFile().existsAsFile())
|
||||
{
|
||||
filesToTrash.addIfNotAlreadyThere (p->getFile());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
WeakReference<FileTreeItemBase> treeRootItem { dynamic_cast<FileTreeItemBase*> (tree->getRootItem()) };
|
||||
|
||||
if (treeRootItem == nullptr)
|
||||
{
|
||||
jassertfalse;
|
||||
return;
|
||||
}
|
||||
|
||||
auto doDelete = [treeRootItem, itemsToRemove] (const Array<File>& fsToTrash)
|
||||
{
|
||||
if (treeRootItem == nullptr)
|
||||
return;
|
||||
|
||||
auto& om = ProjucerApplication::getApp().openDocumentManager;
|
||||
|
||||
for (auto i = fsToTrash.size(); --i >= 0;)
|
||||
{
|
||||
auto f = fsToTrash.getUnchecked(i);
|
||||
|
||||
om.closeFileWithoutSaving (f);
|
||||
|
||||
if (! f.moveToTrash())
|
||||
{
|
||||
// xxx
|
||||
}
|
||||
}
|
||||
|
||||
for (auto i = itemsToRemove.size(); --i >= 0;)
|
||||
{
|
||||
if (auto itemToRemove = treeRootItem->findTreeViewItem (itemsToRemove.getUnchecked (i)))
|
||||
{
|
||||
if (auto* pcc = treeRootItem->getProjectContentComponent())
|
||||
{
|
||||
if (auto* fileInfoComp = dynamic_cast<FileGroupInformationComponent*> (pcc->getEditorComponent()))
|
||||
if (fileInfoComp->getGroupPath() == itemToRemove->getFile().getFullPathName())
|
||||
pcc->hideEditor();
|
||||
}
|
||||
|
||||
om.closeFileWithoutSaving (itemToRemove->getFile());
|
||||
itemToRemove->deleteItem();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if (! filesToTrash.isEmpty())
|
||||
{
|
||||
String fileList;
|
||||
auto maxFilesToList = 10;
|
||||
for (auto 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...";
|
||||
|
||||
AlertWindow::showYesNoCancelBox (MessageBoxIconType::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(),
|
||||
ModalCallbackFunction::create ([treeRootItem, filesToTrash, doDelete] (int r) mutable
|
||||
{
|
||||
if (treeRootItem == nullptr)
|
||||
return;
|
||||
|
||||
if (r == 0)
|
||||
return;
|
||||
|
||||
if (r != 2)
|
||||
filesToTrash.clear();
|
||||
|
||||
doDelete (filesToTrash);
|
||||
}));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
doDelete (filesToTrash);
|
||||
}
|
||||
|
||||
virtual void revealInFinder() const
|
||||
{
|
||||
getFile().revealToUser();
|
||||
}
|
||||
|
||||
virtual void browseToAddExistingFiles()
|
||||
{
|
||||
auto location = item.isGroup() ? item.determineGroupFolder() : getFile();
|
||||
chooser = std::make_unique<FileChooser> ("Add Files to Jucer Project", location, "");
|
||||
auto flags = FileBrowserComponent::openMode
|
||||
| FileBrowserComponent::canSelectFiles
|
||||
| FileBrowserComponent::canSelectDirectories
|
||||
| FileBrowserComponent::canSelectMultipleItems;
|
||||
|
||||
chooser->launchAsync (flags, [this] (const FileChooser& fc)
|
||||
{
|
||||
if (fc.getResults().isEmpty())
|
||||
return;
|
||||
|
||||
StringArray files;
|
||||
|
||||
for (int i = 0; i < fc.getResults().size(); ++i)
|
||||
files.add (fc.getResults().getReference(i).getFullPathName());
|
||||
|
||||
addFilesRetainingSortOrder (files);
|
||||
});
|
||||
}
|
||||
|
||||
virtual void checkFileStatus() // (recursive)
|
||||
{
|
||||
auto file = getFile();
|
||||
auto nowMissing = (file != File() && ! file.exists());
|
||||
|
||||
if (nowMissing != isFileMissing)
|
||||
{
|
||||
isFileMissing = nowMissing;
|
||||
repaintItem();
|
||||
}
|
||||
}
|
||||
|
||||
virtual void addFilesAtIndex (const StringArray& files, int insertIndex)
|
||||
{
|
||||
if (auto* p = getParentProjectItem())
|
||||
p->addFilesAtIndex (files, insertIndex);
|
||||
}
|
||||
|
||||
virtual void addFilesRetainingSortOrder (const StringArray& files)
|
||||
{
|
||||
if (auto* p = getParentProjectItem())
|
||||
p->addFilesRetainingSortOrder (files);
|
||||
}
|
||||
|
||||
virtual void moveSelectedItemsTo (OwnedArray<Project::Item>&, int /*insertIndex*/)
|
||||
{
|
||||
jassertfalse;
|
||||
}
|
||||
|
||||
void showMultiSelectionPopupMenu (Point<int> p) override
|
||||
{
|
||||
PopupMenu m;
|
||||
m.addItem (1, "Delete");
|
||||
|
||||
m.showMenuAsync (PopupMenu::Options().withTargetScreenArea ({ p.x, p.y, 1, 1 }),
|
||||
ModalCallbackFunction::create (treeViewMultiSelectItemChosen, this));
|
||||
}
|
||||
|
||||
static void treeViewMultiSelectItemChosen (int resultCode, FileTreeItemBase* item)
|
||||
{
|
||||
switch (resultCode)
|
||||
{
|
||||
case 1: item->deleteAllSelectedItems(); break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
|
||||
virtual FileTreeItemBase* findTreeViewItem (const Project::Item& itemToFind)
|
||||
{
|
||||
if (item == itemToFind)
|
||||
return this;
|
||||
|
||||
auto wasOpen = isOpen();
|
||||
setOpen (true);
|
||||
|
||||
for (auto i = getNumSubItems(); --i >= 0;)
|
||||
{
|
||||
if (auto* pg = dynamic_cast<FileTreeItemBase*> (getSubItem(i)))
|
||||
if (auto* found = pg->findTreeViewItem (itemToFind))
|
||||
return found;
|
||||
}
|
||||
|
||||
setOpen (wasOpen);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
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 {}; }
|
||||
File getDraggableFile() const override { return getFile(); }
|
||||
|
||||
var getDragSourceDescription() override
|
||||
{
|
||||
cancelDelayedSelectionTimer();
|
||||
return projectItemDragType;
|
||||
}
|
||||
|
||||
void addSubItems() override
|
||||
{
|
||||
for (int i = 0; i < item.getNumChildren(); ++i)
|
||||
if (auto* 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))
|
||||
ProjucerApplication::getApp().openFile (files[0], [] (bool) {});
|
||||
else
|
||||
addFilesAtIndex (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)
|
||||
{
|
||||
auto* tree = getOwnerView();
|
||||
std::unique_ptr<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)
|
||||
{
|
||||
auto* tree = dynamic_cast<TreeView*> (dragSourceDetails.sourceComponent.get());
|
||||
|
||||
if (tree == nullptr)
|
||||
tree = dragSourceDetails.sourceComponent->findParentComponentOfClass<TreeView>();
|
||||
|
||||
if (tree != nullptr)
|
||||
{
|
||||
auto numSelected = tree->getNumSelectedItems();
|
||||
|
||||
for (int i = 0; i < numSelected; ++i)
|
||||
if (auto* p = dynamic_cast<FileTreeItemBase*> (tree->getSelectedItem (i)))
|
||||
selectedNodes.add (new Project::Item (p->item));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FileTreeItemBase* getParentProjectItem() const
|
||||
{
|
||||
return dynamic_cast<FileTreeItemBase*> (getParentItem());
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
Project::Item item;
|
||||
|
||||
protected:
|
||||
//==============================================================================
|
||||
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&, int) override { treeChildrenChanged (parentTree); }
|
||||
void valueTreeChildOrderChanged (ValueTree& parentTree, int, int) override { treeChildrenChanged (parentTree); }
|
||||
|
||||
bool isFileMissing;
|
||||
|
||||
virtual FileTreeItemBase* createSubItem (const Project::Item& node) = 0;
|
||||
|
||||
Icon getIcon() const override
|
||||
{
|
||||
auto colour = getOwnerView()->findColour (isSelected() ? defaultHighlightedTextColourId
|
||||
: treeIconColourId);
|
||||
|
||||
return item.getIcon (isOpen()).withColour (colour);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
struct RenameMessage : public CallbackMessage
|
||||
{
|
||||
RenameMessage (TreeView* const t, const Project::Item& i)
|
||||
: tree (t), itemToRename (i) {}
|
||||
|
||||
void messageCallback() override
|
||||
{
|
||||
if (tree != nullptr)
|
||||
if (auto* root = dynamic_cast<FileTreeItemBase*> (tree->getRootItem()))
|
||||
if (auto* 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 (auto i = selectedNodes.size(); --i >= 0;)
|
||||
{
|
||||
auto* 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 (auto i = selectedNodes.size(); --i >= 0;)
|
||||
{
|
||||
auto* n = selectedNodes.getUnchecked(i);
|
||||
|
||||
for (auto 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)
|
||||
{
|
||||
auto* 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 (auto i = parent.getNumChildren(); --i >= 0;)
|
||||
if (parent.getChild (i) == child)
|
||||
return i;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
private:
|
||||
std::unique_ptr<FileChooser> chooser;
|
||||
|
||||
JUCE_DECLARE_WEAK_REFERENCEABLE (FileTreeItemBase)
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
class SourceFileItem : public FileTreeItemBase
|
||||
{
|
||||
public:
|
||||
SourceFileItem (const Project::Item& projectItem)
|
||||
: FileTreeItemBase (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();
|
||||
}
|
||||
|
||||
void paintItem (Graphics& g, int width, int height) override
|
||||
{
|
||||
JucerTreeViewBase::paintItem (g, width, height);
|
||||
|
||||
if (item.needsSaving())
|
||||
{
|
||||
auto bounds = g.getClipBounds().withY (0).withHeight (height);
|
||||
|
||||
g.setFont (getFont());
|
||||
g.setColour (getContentColour (false));
|
||||
|
||||
g.drawFittedText ("*", bounds.removeFromLeft (height), Justification::centred, 1);
|
||||
}
|
||||
}
|
||||
|
||||
static File findCorrespondingHeaderOrCpp (const File& f)
|
||||
{
|
||||
if (f.hasFileExtension (sourceFileExtensions)) return f.withFileExtension (".h");
|
||||
if (f.hasFileExtension (headerFileExtensions)) return f.withFileExtension (".cpp");
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
void setName (const String& newName) override
|
||||
{
|
||||
if (newName != File::createLegalFileName (newName))
|
||||
{
|
||||
AlertWindow::showMessageBoxAsync (MessageBoxIconType::WarningIcon,
|
||||
"File Rename",
|
||||
"That filename contained some illegal characters!");
|
||||
triggerAsyncRename (item);
|
||||
return;
|
||||
}
|
||||
|
||||
auto oldFile = getFile();
|
||||
auto newFile = oldFile.getSiblingFile (newName);
|
||||
auto correspondingFile = findCorrespondingHeaderOrCpp (oldFile);
|
||||
|
||||
if (correspondingFile.exists() && newFile.hasFileExtension (oldFile.getFileExtension()))
|
||||
{
|
||||
auto correspondingItem = item.project.getMainGroup().findItemForFile (correspondingFile);
|
||||
|
||||
if (correspondingItem.isValid())
|
||||
{
|
||||
AlertWindow::showOkCancelBox (MessageBoxIconType::NoIcon,
|
||||
"File Rename",
|
||||
"Do you also want to rename the corresponding file \"" + correspondingFile.getFileName() + "\" to match?",
|
||||
{},
|
||||
{},
|
||||
nullptr,
|
||||
ModalCallbackFunction::create ([parent = WeakReference<SourceFileItem> { this },
|
||||
oldFile, newFile, correspondingFile, correspondingItem] (int result) mutable
|
||||
{
|
||||
if (parent == nullptr || result == 0)
|
||||
return;
|
||||
|
||||
if (! parent->item.renameFile (newFile))
|
||||
{
|
||||
AlertWindow::showMessageBoxAsync (MessageBoxIconType::WarningIcon,
|
||||
"File Rename",
|
||||
"Failed to rename \"" + oldFile.getFullPathName() + "\"!\n\nCheck your file permissions!");
|
||||
return;
|
||||
}
|
||||
|
||||
if (! correspondingItem.renameFile (newFile.withFileExtension (correspondingFile.getFileExtension())))
|
||||
{
|
||||
AlertWindow::showMessageBoxAsync (MessageBoxIconType::WarningIcon,
|
||||
"File Rename",
|
||||
"Failed to rename \"" + correspondingFile.getFullPathName() + "\"!\n\nCheck your file permissions!");
|
||||
}
|
||||
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
if (! item.renameFile (newFile))
|
||||
{
|
||||
AlertWindow::showMessageBoxAsync (MessageBoxIconType::WarningIcon,
|
||||
"File Rename",
|
||||
"Failed to rename the file!\n\nCheck your file permissions!");
|
||||
}
|
||||
}
|
||||
|
||||
FileTreeItemBase* createSubItem (const Project::Item&) override
|
||||
{
|
||||
jassertfalse;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void showDocument() override
|
||||
{
|
||||
auto f = getFile();
|
||||
|
||||
if (f.exists())
|
||||
if (auto* pcc = getProjectContentComponent())
|
||||
pcc->showEditorForFile (f, false);
|
||||
}
|
||||
|
||||
void showPopupMenu (Point<int> p) override
|
||||
{
|
||||
PopupMenu m;
|
||||
|
||||
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 (5, "Binary Resource", true, item.shouldBeAddedToBinaryResources());
|
||||
m.addItem (6, "Xcode Resource", true, item.shouldBeAddedToXcodeResources());
|
||||
m.addItem (7, "Compile", item.isSourceFile(), item.shouldBeCompiled());
|
||||
m.addItem (8, "Skip PCH", item.shouldBeCompiled(), item.shouldSkipPCH());
|
||||
m.addSeparator();
|
||||
|
||||
m.addItem (3, "Delete");
|
||||
|
||||
launchPopupMenu (m, p);
|
||||
}
|
||||
|
||||
void showAddMenu (Point<int> p) override
|
||||
{
|
||||
if (auto* group = dynamic_cast<GroupItem*> (getParentItem()))
|
||||
group->showAddMenu (p);
|
||||
}
|
||||
|
||||
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;
|
||||
case 5: item.getShouldAddToBinaryResourcesValue().setValue (! item.shouldBeAddedToBinaryResources()); break;
|
||||
case 6: item.getShouldAddToXcodeResourcesValue().setValue (! item.shouldBeAddedToXcodeResources()); break;
|
||||
case 7: item.getShouldCompileValue().setValue (! item.shouldBeCompiled()); break;
|
||||
case 8: item.getShouldSkipPCHValue().setValue (! item.shouldSkipPCH()); break;
|
||||
|
||||
default:
|
||||
if (auto* parentGroup = dynamic_cast<GroupItem*> (getParentProjectItem()))
|
||||
parentGroup->processCreateFileMenuItem (resultCode);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
JUCE_DECLARE_WEAK_REFERENCEABLE (SourceFileItem)
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
class GroupItem : public FileTreeItemBase
|
||||
{
|
||||
public:
|
||||
GroupItem (const Project::Item& projectItem, const String& filter = {})
|
||||
: FileTreeItemBase (projectItem),
|
||||
searchFilter (filter)
|
||||
{
|
||||
}
|
||||
|
||||
bool isRoot() const override { return item.isMainGroup(); }
|
||||
bool acceptsFileDrop (const StringArray&) const override { return true; }
|
||||
|
||||
void addNewGroup()
|
||||
{
|
||||
auto newGroup = item.addNewSubGroup ("New Group", 0);
|
||||
triggerAsyncRename (newGroup);
|
||||
}
|
||||
|
||||
bool acceptsDragItems (const OwnedArray<Project::Item>& selectedNodes) override
|
||||
{
|
||||
for (auto i = selectedNodes.size(); --i >= 0;)
|
||||
if (item.canContain (*selectedNodes.getUnchecked(i)))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void addFilesAtIndex (const StringArray& files, int insertIndex) override
|
||||
{
|
||||
for (auto f : files)
|
||||
{
|
||||
if (item.addFileAtIndex (f, insertIndex, true))
|
||||
++insertIndex;
|
||||
}
|
||||
}
|
||||
|
||||
void addFilesRetainingSortOrder (const StringArray& files) override
|
||||
{
|
||||
for (auto i = files.size(); --i >= 0;)
|
||||
item.addFileRetainingSortOrder (files[i], true);
|
||||
}
|
||||
|
||||
void moveSelectedItemsTo (OwnedArray<Project::Item>& selectedNodes, int insertIndex) override
|
||||
{
|
||||
moveItems (selectedNodes, item, insertIndex);
|
||||
}
|
||||
|
||||
void checkFileStatus() override
|
||||
{
|
||||
for (int i = 0; i < getNumSubItems(); ++i)
|
||||
if (auto* p = dynamic_cast<FileTreeItemBase*> (getSubItem(i)))
|
||||
p->checkFileStatus();
|
||||
}
|
||||
|
||||
bool isGroupEmpty (const Project::Item& group) // recursive
|
||||
{
|
||||
for (int i = 0; i < group.getNumChildren(); ++i)
|
||||
{
|
||||
auto child = group.getChild (i);
|
||||
|
||||
if ((child.isGroup() && ! isGroupEmpty (child))
|
||||
|| (child.isFile() && child.getName().containsIgnoreCase (searchFilter)))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
FileTreeItemBase* createSubItem (const Project::Item& child) override
|
||||
{
|
||||
if (child.isGroup())
|
||||
{
|
||||
if (searchFilter.isNotEmpty() && isGroupEmpty (child))
|
||||
return nullptr;
|
||||
|
||||
return new GroupItem (child, searchFilter);
|
||||
}
|
||||
|
||||
if (child.isFile())
|
||||
{
|
||||
if (child.getName().containsIgnoreCase (searchFilter))
|
||||
return new SourceFileItem (child);
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
jassertfalse;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void showDocument() override
|
||||
{
|
||||
if (auto* pcc = getProjectContentComponent())
|
||||
pcc->setScrollableEditorComponent (std::make_unique<FileGroupInformationComponent> (item));
|
||||
}
|
||||
|
||||
static void openAllGroups (TreeViewItem* root)
|
||||
{
|
||||
for (int i = 0; i < root->getNumSubItems(); ++i)
|
||||
if (auto* sub = root->getSubItem (i))
|
||||
openOrCloseAllSubGroups (*sub, true);
|
||||
}
|
||||
|
||||
static void closeAllGroups (TreeViewItem* root)
|
||||
{
|
||||
for (int i = 0; i < root->getNumSubItems(); ++i)
|
||||
if (auto* sub = root->getSubItem (i))
|
||||
openOrCloseAllSubGroups (*sub, false);
|
||||
}
|
||||
|
||||
static void openOrCloseAllSubGroups (TreeViewItem& treeItem, bool shouldOpen)
|
||||
{
|
||||
treeItem.setOpen (shouldOpen);
|
||||
|
||||
for (auto i = treeItem.getNumSubItems(); --i >= 0;)
|
||||
if (auto* sub = treeItem.getSubItem (i))
|
||||
openOrCloseAllSubGroups (*sub, shouldOpen);
|
||||
}
|
||||
|
||||
static void setFilesToCompile (Project::Item projectItem, const bool shouldCompile)
|
||||
{
|
||||
if (projectItem.isFile() && (projectItem.getFile().hasFileExtension (fileTypesToCompileByDefault)))
|
||||
projectItem.getShouldCompileValue() = shouldCompile;
|
||||
|
||||
for (auto i = projectItem.getNumChildren(); --i >= 0;)
|
||||
setFilesToCompile (projectItem.getChild (i), shouldCompile);
|
||||
}
|
||||
|
||||
void showPopupMenu (Point<int> p) override
|
||||
{
|
||||
PopupMenu m;
|
||||
addCreateFileMenuItems (m);
|
||||
|
||||
m.addSeparator();
|
||||
|
||||
m.addItem (1, "Collapse all Groups");
|
||||
m.addItem (2, "Expand all Groups");
|
||||
|
||||
if (! isRoot())
|
||||
{
|
||||
if (isOpen())
|
||||
m.addItem (3, "Collapse all Sub-groups");
|
||||
else
|
||||
m.addItem (4, "Expand all Sub-groups");
|
||||
}
|
||||
|
||||
m.addSeparator();
|
||||
m.addItem (5, "Enable compiling of all enclosed files");
|
||||
m.addItem (6, "Disable compiling of all enclosed files");
|
||||
|
||||
m.addSeparator();
|
||||
m.addItem (7, "Sort Items Alphabetically");
|
||||
m.addItem (8, "Sort Items Alphabetically (Groups first)");
|
||||
m.addSeparator();
|
||||
|
||||
if (! isRoot())
|
||||
{
|
||||
m.addItem (9, "Rename...");
|
||||
m.addItem (10, "Delete");
|
||||
}
|
||||
|
||||
launchPopupMenu (m, p);
|
||||
}
|
||||
|
||||
void showAddMenu (Point<int> p) override
|
||||
{
|
||||
PopupMenu m;
|
||||
addCreateFileMenuItems (m);
|
||||
|
||||
launchPopupMenu (m, p);
|
||||
}
|
||||
|
||||
void handlePopupMenuResult (int resultCode) override
|
||||
{
|
||||
switch (resultCode)
|
||||
{
|
||||
case 1: closeAllGroups (getOwnerView()->getRootItem()); break;
|
||||
case 2: openAllGroups (getOwnerView()->getRootItem()); break;
|
||||
case 3: openOrCloseAllSubGroups (*this, false); break;
|
||||
case 4: openOrCloseAllSubGroups (*this, true); break;
|
||||
case 5: setFilesToCompile (item, true); break;
|
||||
case 6: setFilesToCompile (item, false); break;
|
||||
case 7: item.sortAlphabetically (false, false); break;
|
||||
case 8: item.sortAlphabetically (true, false); break;
|
||||
case 9: triggerAsyncRename (item); break;
|
||||
case 10: deleteAllSelectedItems(); break;
|
||||
default: processCreateFileMenuItem (resultCode); break;
|
||||
}
|
||||
}
|
||||
|
||||
void addCreateFileMenuItems (PopupMenu& m)
|
||||
{
|
||||
m.addItem (1001, "Add New Group");
|
||||
m.addItem (1002, "Add Existing Files...");
|
||||
|
||||
m.addSeparator();
|
||||
wizard.addWizardsToMenu (m);
|
||||
}
|
||||
|
||||
void processCreateFileMenuItem (int menuID)
|
||||
{
|
||||
switch (menuID)
|
||||
{
|
||||
case 1001: addNewGroup(); break;
|
||||
case 1002: browseToAddExistingFiles(); break;
|
||||
|
||||
default:
|
||||
jassert (getProject() != nullptr);
|
||||
wizard.runWizardFromMenu (menuID, *getProject(), item);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Project* getProject()
|
||||
{
|
||||
if (auto* tv = getOwnerView())
|
||||
if (auto* pcc = tv->findParentComponentOfClass<ProjectContentComponent>())
|
||||
return pcc->getProject();
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void setSearchFilter (const String& filter) override
|
||||
{
|
||||
searchFilter = filter;
|
||||
refreshSubItems();
|
||||
}
|
||||
|
||||
String searchFilter;
|
||||
NewFileWizard wizard;
|
||||
};
|
694
deps/juce/extras/Projucer/Source/Project/UI/Sidebar/jucer_ModuleTreeItems.h
vendored
Normal file
694
deps/juce/extras/Projucer/Source/Project/UI/Sidebar/jucer_ModuleTreeItems.h
vendored
Normal file
@ -0,0 +1,694 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
|
||||
//==============================================================================
|
||||
class ModuleItem : public ProjectTreeItemBase
|
||||
{
|
||||
public:
|
||||
ModuleItem (Project& p, const String& modID)
|
||||
: project (p), moduleID (modID)
|
||||
{
|
||||
missingDependencies = project.getEnabledModules().getExtraDependenciesNeeded (moduleID).size() > 0;
|
||||
cppStandardHigherThanProject = project.getEnabledModules().doesModuleHaveHigherCppStandardThanProject (moduleID);
|
||||
|
||||
moduleInfo = project.getEnabledModules().getModuleInfo (moduleID);
|
||||
}
|
||||
|
||||
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() const override { return missingDependencies; }
|
||||
bool hasWarnings() const override { return cppStandardHigherThanProject; }
|
||||
|
||||
void showDocument() override
|
||||
{
|
||||
showSettingsPage (new ModuleSettingsPanel (project, moduleID, getOwnerView()));
|
||||
}
|
||||
|
||||
void deleteItem() override
|
||||
{
|
||||
closeSettingsPage();
|
||||
project.getEnabledModules().removeModule (moduleID);
|
||||
}
|
||||
|
||||
Icon getIcon() const override
|
||||
{
|
||||
auto iconColour = getOwnerView()->findColour (isSelected() ? defaultHighlightedTextColourId
|
||||
: treeIconColourId);
|
||||
|
||||
if (! isSelected())
|
||||
{
|
||||
if (moduleInfo.isValid() && moduleInfo.getVendor() == "juce")
|
||||
{
|
||||
if (moduleInfo.getLicense() == "ISC")
|
||||
iconColour = Colours::lightblue;
|
||||
else if (moduleInfo.getLicense() == "GPL/Commercial")
|
||||
iconColour = Colours::orange;
|
||||
}
|
||||
}
|
||||
|
||||
return Icon (getIcons().singleModule, iconColour);
|
||||
}
|
||||
|
||||
void showAddMenu (Point<int> p) override
|
||||
{
|
||||
if (auto* parent = dynamic_cast<EnabledModulesItem*> (getParentItem()))
|
||||
parent->showPopupMenu (p);
|
||||
}
|
||||
|
||||
void showPopupMenu (Point<int> p) override
|
||||
{
|
||||
PopupMenu menu;
|
||||
menu.addItem (1, "Remove this module");
|
||||
launchPopupMenu (menu, p);
|
||||
}
|
||||
|
||||
void handlePopupMenuResult (int resultCode) override
|
||||
{
|
||||
if (resultCode == 1)
|
||||
deleteItem();
|
||||
}
|
||||
|
||||
bool checkCppStandard()
|
||||
{
|
||||
auto oldVal = cppStandardHigherThanProject;
|
||||
cppStandardHigherThanProject = project.getEnabledModules().doesModuleHaveHigherCppStandardThanProject (moduleID);
|
||||
|
||||
if (oldVal != cppStandardHigherThanProject)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
Project& project;
|
||||
String moduleID;
|
||||
|
||||
private:
|
||||
ModuleDescription moduleInfo;
|
||||
bool missingDependencies = false;
|
||||
bool cppStandardHigherThanProject = false;
|
||||
|
||||
//==============================================================================
|
||||
class ModuleSettingsPanel : public Component,
|
||||
private Value::Listener,
|
||||
private Timer
|
||||
{
|
||||
public:
|
||||
ModuleSettingsPanel (Project& p, const String& modID, TreeView* tree)
|
||||
: group (p.getEnabledModules().getModuleInfo (modID).getID(),
|
||||
Icon (getIcons().singleModule, Colours::transparentBlack)),
|
||||
project (p),
|
||||
modulesTree (tree),
|
||||
moduleID (modID)
|
||||
{
|
||||
addAndMakeVisible (group);
|
||||
refresh();
|
||||
}
|
||||
|
||||
void refresh()
|
||||
{
|
||||
auto& modules = project.getEnabledModules();
|
||||
|
||||
setEnabled (modules.isModuleEnabled (moduleID));
|
||||
|
||||
PropertyListBuilder props;
|
||||
|
||||
props.add (new ModuleInfoComponent (project, moduleID));
|
||||
|
||||
if (modules.getExtraDependenciesNeeded (moduleID).size() > 0)
|
||||
props.add (new MissingDependenciesComponent (project, moduleID));
|
||||
|
||||
if (modules.doesModuleHaveHigherCppStandardThanProject (moduleID))
|
||||
props.add (new CppStandardWarningComponent());
|
||||
|
||||
group.clearProperties();
|
||||
exporterModulePathDefaultValues.clear();
|
||||
exporterModulePathValues.clear();
|
||||
globalPathValues.clear();
|
||||
|
||||
for (Project::ExporterIterator exporter (project); exporter.next();)
|
||||
{
|
||||
if (exporter->isCLion())
|
||||
continue;
|
||||
|
||||
exporterModulePathDefaultValues.add (exporter->getPathForModuleValue (moduleID));
|
||||
auto& defaultValue = exporterModulePathDefaultValues.getReference (exporterModulePathDefaultValues.size() - 1);
|
||||
|
||||
exporterModulePathValues.add (defaultValue.getPropertyAsValue());
|
||||
|
||||
auto pathComponent = std::make_unique<FilePathPropertyComponent> (defaultValue,
|
||||
"Path for " + exporter->getUniqueName().quoted(),
|
||||
true,
|
||||
exporter->getTargetOSForExporter() == TargetOS::getThisOS(),
|
||||
"*",
|
||||
project.getProjectFolder());
|
||||
|
||||
pathComponent->setEnabled (! modules.shouldUseGlobalPath (moduleID));
|
||||
|
||||
props.add (pathComponent.release(),
|
||||
"A path to the folder that contains the " + moduleID + " module when compiling the "
|
||||
+ exporter->getUniqueName().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. If this "
|
||||
"is empty then the global path will be used.");
|
||||
|
||||
globalPathValues.add (getAppSettings().getStoredPath (isJUCEModule (moduleID) ? Ids::defaultJuceModulePath : Ids::defaultUserModulePath,
|
||||
exporter->getTargetOSForExporter()).getPropertyAsValue());
|
||||
}
|
||||
|
||||
for (int i = 0; i < exporterModulePathDefaultValues.size(); ++i)
|
||||
{
|
||||
exporterModulePathDefaultValues.getReference (i).onDefaultChange = [this] { startTimer (50); };
|
||||
|
||||
exporterModulePathValues.getReference (i).addListener (this);
|
||||
globalPathValues.getReference (i).addListener (this);
|
||||
}
|
||||
|
||||
useGlobalPathValue.removeListener (this);
|
||||
useGlobalPathValue.referTo (modules.shouldUseGlobalPathValue (moduleID));
|
||||
useGlobalPathValue.addListener (this);
|
||||
|
||||
auto menuItemString = (TargetOS::getThisOS() == TargetOS::osx ? "\"Projucer->Global Paths...\""
|
||||
: "\"File->Global Paths...\"");
|
||||
|
||||
props.add (new BooleanPropertyComponent (useGlobalPathValue,
|
||||
"Use global path", "Use global path for this module"),
|
||||
String ("If this is enabled, then the locally-stored global path (set in the ") + menuItemString + " menu item) "
|
||||
"will be used as the path to this module. "
|
||||
"This means that if this Projucer project is opened on another machine it will use that machine's global path as the path to this module.");
|
||||
|
||||
props.add (new BooleanPropertyComponent (modules.shouldCopyModuleFilesLocallyValue (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 (modules.shouldShowAllModuleFilesInProjectValue (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.");
|
||||
|
||||
auto info = modules.getModuleInfo (moduleID);
|
||||
|
||||
if (info.isValid())
|
||||
{
|
||||
configFlags.clear();
|
||||
LibraryModule (info).getConfigFlags (project, configFlags);
|
||||
|
||||
for (auto* flag : configFlags)
|
||||
{
|
||||
auto* c = new ChoicePropertyComponent (flag->value, flag->symbol);
|
||||
c->setTooltip (flag->description);
|
||||
props.add (c);
|
||||
}
|
||||
}
|
||||
|
||||
group.setProperties (props);
|
||||
parentSizeChanged();
|
||||
}
|
||||
|
||||
void parentSizeChanged() override { updateSize (*this, group); }
|
||||
void resized() override { group.setBounds (getLocalBounds().withTrimmedLeft (12)); }
|
||||
|
||||
String getModuleID() const noexcept { return moduleID; }
|
||||
|
||||
private:
|
||||
void valueChanged (Value& v) override
|
||||
{
|
||||
auto isExporterPathValue = [&]
|
||||
{
|
||||
for (auto& exporterValue : exporterModulePathValues)
|
||||
if (exporterValue.refersToSameSourceAs (v))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}();
|
||||
|
||||
if (isExporterPathValue)
|
||||
project.rescanExporterPathModules();
|
||||
|
||||
startTimer (50);
|
||||
}
|
||||
|
||||
void timerCallback() override
|
||||
{
|
||||
stopTimer();
|
||||
refresh();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
Array<ValueWithDefault> exporterModulePathDefaultValues;
|
||||
Array<Value> exporterModulePathValues, globalPathValues;
|
||||
Value useGlobalPathValue;
|
||||
|
||||
OwnedArray<Project::ConfigFlag> configFlags;
|
||||
|
||||
PropertyGroupComponent group;
|
||||
Project& project;
|
||||
SafePointer<TreeView> modulesTree;
|
||||
String moduleID;
|
||||
|
||||
//==============================================================================
|
||||
class ModuleInfoComponent : public PropertyComponent,
|
||||
private Value::Listener
|
||||
{
|
||||
public:
|
||||
ModuleInfoComponent (Project& p, const String& modID)
|
||||
: PropertyComponent ("Module", 150), project (p), moduleID (modID)
|
||||
{
|
||||
for (Project::ExporterIterator exporter (project); exporter.next();)
|
||||
listeningValues.add (new Value (exporter->getPathForModuleValue (moduleID).getPropertyAsValue()))->addListener (this);
|
||||
|
||||
refresh();
|
||||
}
|
||||
|
||||
void refresh() override
|
||||
{
|
||||
info = project.getEnabledModules().getModuleInfo (moduleID);
|
||||
repaint();
|
||||
}
|
||||
|
||||
private:
|
||||
void paint (Graphics& g) override
|
||||
{
|
||||
auto bounds = getLocalBounds().reduced (10);
|
||||
bounds.removeFromTop (5);
|
||||
|
||||
if (info.isValid())
|
||||
{
|
||||
auto topSlice = bounds.removeFromTop (bounds.getHeight() / 2);
|
||||
bounds.removeFromTop (bounds.getHeight() / 6);
|
||||
auto bottomSlice = bounds;
|
||||
|
||||
g.setColour (findColour (defaultTextColourId));
|
||||
|
||||
g.drawFittedText (info.getName(), topSlice.removeFromTop (topSlice.getHeight() / 4), Justification::centredLeft, 1);
|
||||
g.drawFittedText ("Version: " + info.getVersion(), topSlice.removeFromTop (topSlice.getHeight() / 3), Justification::centredLeft, 1);
|
||||
g.drawFittedText ("License: " + info.getLicense(), topSlice.removeFromTop (topSlice.getHeight() / 2), Justification::centredLeft, 1);
|
||||
g.drawFittedText ("Location: " + info.getFolder().getParentDirectory().getFullPathName(),
|
||||
topSlice.removeFromTop (topSlice.getHeight()), Justification::centredLeft, 1);
|
||||
|
||||
g.drawFittedText (info.getDescription(), bottomSlice, Justification::topLeft, 3, 1.0f);
|
||||
}
|
||||
else
|
||||
{
|
||||
g.setColour (Colours::red);
|
||||
g.drawFittedText ("Cannot find this module at the specified path!", bounds, Justification::centred, 1);
|
||||
}
|
||||
}
|
||||
|
||||
void valueChanged (Value&) override
|
||||
{
|
||||
refresh();
|
||||
}
|
||||
|
||||
Project& project;
|
||||
String moduleID;
|
||||
OwnedArray<Value> listeningValues;
|
||||
ModuleDescription info;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ModuleInfoComponent)
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
class MissingDependenciesComponent : public PropertyComponent
|
||||
{
|
||||
public:
|
||||
MissingDependenciesComponent (Project& p, const String& modID)
|
||||
: PropertyComponent ("Dependencies", 100),
|
||||
project (p), moduleID (modID),
|
||||
missingDependencies (project.getEnabledModules().getExtraDependenciesNeeded (modID))
|
||||
{
|
||||
addAndMakeVisible (fixButton);
|
||||
fixButton.setColour (TextButton::buttonColourId, Colours::red);
|
||||
fixButton.setColour (TextButton::textColourOffId, Colours::white);
|
||||
fixButton.onClick = [this] { fixDependencies(); };
|
||||
}
|
||||
|
||||
void refresh() override {}
|
||||
|
||||
void paint (Graphics& g) override
|
||||
{
|
||||
String text ("This module has missing dependencies!\n\n"
|
||||
"To build correctly, it requires the following modules to be added:\n");
|
||||
text << missingDependencies.joinIntoString (", ");
|
||||
|
||||
g.setColour (Colours::red);
|
||||
g.drawFittedText (text, getLocalBounds().reduced (10), Justification::topLeft, 3);
|
||||
}
|
||||
|
||||
void fixDependencies()
|
||||
{
|
||||
auto& enabledModules = project.getEnabledModules();
|
||||
|
||||
if (enabledModules.tryToFixMissingDependencies (moduleID))
|
||||
{
|
||||
missingDependencies.clear();
|
||||
}
|
||||
else
|
||||
{
|
||||
missingDependencies = enabledModules.getExtraDependenciesNeeded (moduleID);
|
||||
|
||||
AlertWindow::showMessageBoxAsync (MessageBoxIconType::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() override
|
||||
{
|
||||
fixButton.setBounds (getWidth() - 168, getHeight() - 26, 160, 22);
|
||||
}
|
||||
|
||||
private:
|
||||
Project& project;
|
||||
|
||||
String moduleID;
|
||||
StringArray missingDependencies;
|
||||
TextButton fixButton { "Add Required Modules" };
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MissingDependenciesComponent)
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
struct CppStandardWarningComponent : public PropertyComponent
|
||||
{
|
||||
CppStandardWarningComponent()
|
||||
: PropertyComponent ("CppStandard", 100)
|
||||
{
|
||||
}
|
||||
|
||||
void refresh() override {}
|
||||
|
||||
void paint (Graphics& g) override
|
||||
{
|
||||
auto text = String ("This module has a higher C++ language standard requirement than your project!\n\n"
|
||||
"To use this module you need to increase the C++ standard of the project.\n");
|
||||
|
||||
g.setColour (findColour (defaultHighlightColourId));
|
||||
g.drawFittedText (text, getLocalBounds().reduced (10), Justification::topLeft, 3);
|
||||
}
|
||||
|
||||
StringArray configsToWarnAbout;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CppStandardWarningComponent)
|
||||
};
|
||||
};
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ModuleItem)
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
class EnabledModulesItem : public ProjectTreeItemBase,
|
||||
private Value::Listener,
|
||||
private AvailableModulesList::Listener
|
||||
{
|
||||
public:
|
||||
EnabledModulesItem (Project& p)
|
||||
: project (p),
|
||||
modulesListTree (project.getEnabledModules().getState())
|
||||
{
|
||||
modulesListTree.addListener (this);
|
||||
|
||||
projectCppStandardValue.referTo (project.getProjectValue (Ids::cppLanguageStandard));
|
||||
projectCppStandardValue.addListener (this);
|
||||
|
||||
ProjucerApplication::getApp().getJUCEPathModulesList().addListener (this);
|
||||
ProjucerApplication::getApp().getUserPathsModulesList().addListener (this);
|
||||
|
||||
project.getExporterPathsModulesList().addListener (this);
|
||||
}
|
||||
|
||||
~EnabledModulesItem() override
|
||||
{
|
||||
ProjucerApplication::getApp().getJUCEPathModulesList().removeListener (this);
|
||||
ProjucerApplication::getApp().getUserPathsModulesList().removeListener (this);
|
||||
|
||||
project.getExporterPathsModulesList().removeListener (this);
|
||||
}
|
||||
|
||||
int getItemHeight() const override { return 22; }
|
||||
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() const override { return false; }
|
||||
Icon getIcon() const override { return Icon (getIcons().graph, getContentColour (true)); }
|
||||
|
||||
void showDocument() override
|
||||
{
|
||||
if (auto* pcc = getProjectContentComponent())
|
||||
pcc->setScrollableEditorComponent (std::make_unique<ModulesInformationComponent> (project));
|
||||
}
|
||||
|
||||
static File getModuleFolder (const File& draggedFile)
|
||||
{
|
||||
if (draggedFile.hasFileExtension (headerFileExtensions))
|
||||
return draggedFile.getParentDirectory();
|
||||
|
||||
return draggedFile;
|
||||
}
|
||||
|
||||
bool isInterestedInFileDrag (const StringArray& files) override
|
||||
{
|
||||
for (auto i = files.size(); --i >= 0;)
|
||||
if (ModuleDescription (getModuleFolder (files[i])).isValid())
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void filesDropped (const StringArray& files, int /*insertIndex*/) override
|
||||
{
|
||||
Array<ModuleDescription> modules;
|
||||
|
||||
for (auto f : files)
|
||||
{
|
||||
ModuleDescription m (getModuleFolder (f));
|
||||
|
||||
if (m.isValid())
|
||||
modules.add (m);
|
||||
}
|
||||
|
||||
for (int i = 0; i < modules.size(); ++i)
|
||||
project.getEnabledModules().addModule (modules.getReference (i).getModuleFolder(),
|
||||
project.getEnabledModules().areMostModulesCopiedLocally(),
|
||||
project.getEnabledModules().areMostModulesUsingGlobalPath());
|
||||
}
|
||||
|
||||
void addSubItems() override
|
||||
{
|
||||
for (int i = 0; i < project.getEnabledModules().getNumModules(); ++i)
|
||||
addSubItem (new ModuleItem (project, project.getEnabledModules().getModuleID (i)));
|
||||
}
|
||||
|
||||
void showPopupMenu (Point<int> p) override
|
||||
{
|
||||
auto& enabledModules = project.getEnabledModules();
|
||||
PopupMenu allModules;
|
||||
|
||||
int index = 100;
|
||||
|
||||
// JUCE path
|
||||
PopupMenu jucePathModules;
|
||||
|
||||
for (auto& mod : ProjucerApplication::getApp().getJUCEPathModulesList().getAllModules())
|
||||
jucePathModules.addItem (index++, mod.first, ! enabledModules.isModuleEnabled (mod.first));
|
||||
|
||||
jucePathModules.addSeparator();
|
||||
jucePathModules.addItem (-1, "Re-scan path");
|
||||
|
||||
allModules.addSubMenu ("Global JUCE modules path", jucePathModules);
|
||||
|
||||
// User path
|
||||
index = 200;
|
||||
PopupMenu userPathModules;
|
||||
|
||||
for (auto& mod : ProjucerApplication::getApp().getUserPathsModulesList().getAllModules())
|
||||
userPathModules.addItem (index++, mod.first, ! enabledModules.isModuleEnabled (mod.first));
|
||||
|
||||
userPathModules.addSeparator();
|
||||
userPathModules.addItem (-2, "Re-scan path");
|
||||
|
||||
allModules.addSubMenu ("Global user modules path", userPathModules);
|
||||
|
||||
// Exporter path
|
||||
index = 300;
|
||||
PopupMenu exporterPathModules;
|
||||
|
||||
for (auto& mod : project.getExporterPathsModulesList().getAllModules())
|
||||
exporterPathModules.addItem (index++, mod.first, ! enabledModules.isModuleEnabled (mod.first));
|
||||
|
||||
exporterPathModules.addSeparator();
|
||||
exporterPathModules.addItem (-3, "Re-scan path");
|
||||
|
||||
allModules.addSubMenu ("Exporter paths", exporterPathModules);
|
||||
|
||||
PopupMenu menu;
|
||||
menu.addSubMenu ("Add a module", allModules);
|
||||
|
||||
menu.addSeparator();
|
||||
menu.addItem (1001, "Add a module from a specified folder...");
|
||||
|
||||
launchPopupMenu (menu, p);
|
||||
}
|
||||
|
||||
void handlePopupMenuResult (int resultCode) override
|
||||
{
|
||||
if (resultCode == 1001)
|
||||
{
|
||||
project.getEnabledModules().addModuleFromUserSelectedFile();
|
||||
}
|
||||
else if (resultCode < 0)
|
||||
{
|
||||
if (resultCode == -1) ProjucerApplication::getApp().rescanJUCEPathModules();
|
||||
else if (resultCode == -2) ProjucerApplication::getApp().rescanUserPathModules();
|
||||
else if (resultCode == -3) project.rescanExporterPathModules();
|
||||
}
|
||||
else if (resultCode > 0)
|
||||
{
|
||||
std::vector<AvailableModulesList::ModuleIDAndFolder> list;
|
||||
int offset = -1;
|
||||
|
||||
if (resultCode < 200)
|
||||
{
|
||||
list = ProjucerApplication::getApp().getJUCEPathModulesList().getAllModules();
|
||||
offset = 100;
|
||||
}
|
||||
else if (resultCode < 300)
|
||||
{
|
||||
list = ProjucerApplication::getApp().getUserPathsModulesList().getAllModules();
|
||||
offset = 200;
|
||||
}
|
||||
else if (resultCode < 400)
|
||||
{
|
||||
list = project.getExporterPathsModulesList().getAllModules();
|
||||
offset = 300;
|
||||
}
|
||||
|
||||
if (offset != -1)
|
||||
project.getEnabledModules().addModuleInteractive (list[(size_t) (resultCode - offset)].first);
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void valueTreeChildAdded (ValueTree& parentTree, ValueTree&) override { refreshIfNeeded (parentTree); }
|
||||
void valueTreeChildRemoved (ValueTree& parentTree, ValueTree&, int) override { refreshIfNeeded (parentTree); }
|
||||
void valueTreeChildOrderChanged (ValueTree& parentTree, int, int) override { refreshIfNeeded (parentTree); }
|
||||
|
||||
void refreshIfNeeded (ValueTree& changedTree)
|
||||
{
|
||||
if (changedTree == modulesListTree)
|
||||
{
|
||||
auto selectedID = getSelectedItemID();
|
||||
|
||||
refreshSubItems();
|
||||
|
||||
if (selectedID.isNotEmpty())
|
||||
setSelectedItem (selectedID);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
Project& project;
|
||||
ValueTree modulesListTree;
|
||||
Value projectCppStandardValue;
|
||||
|
||||
//==============================================================================
|
||||
void valueChanged (Value& v) override
|
||||
{
|
||||
if (v == projectCppStandardValue)
|
||||
{
|
||||
for (int i = 0; i < getNumSubItems(); ++i)
|
||||
{
|
||||
if (auto* moduleItem = dynamic_cast<ModuleItem*> (getSubItem (i)))
|
||||
{
|
||||
if (moduleItem->checkCppStandard())
|
||||
{
|
||||
refreshSubItems();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void removeDuplicateModules()
|
||||
{
|
||||
auto jucePathModulesList = ProjucerApplication::getApp().getJUCEPathModulesList().getAllModules();
|
||||
|
||||
auto& userPathModules = ProjucerApplication::getApp().getUserPathsModulesList();
|
||||
userPathModules.removeDuplicates (jucePathModulesList);
|
||||
|
||||
auto& exporterPathModules = project.getExporterPathsModulesList();
|
||||
exporterPathModules.removeDuplicates (jucePathModulesList);
|
||||
exporterPathModules.removeDuplicates (userPathModules.getAllModules());
|
||||
}
|
||||
|
||||
void availableModulesChanged (AvailableModulesList*) override
|
||||
{
|
||||
removeDuplicateModules();
|
||||
refreshSubItems();
|
||||
}
|
||||
|
||||
String getSelectedItemID() const
|
||||
{
|
||||
for (int i = 0; i < getNumSubItems(); ++i)
|
||||
if (auto* item = getSubItem (i))
|
||||
if (item->isSelected())
|
||||
return item->getUniqueName();
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
void setSelectedItem (const String& itemID)
|
||||
{
|
||||
for (int i = 0; i < getNumSubItems(); ++i)
|
||||
{
|
||||
if (auto* item = getSubItem (i))
|
||||
{
|
||||
if (item->getUniqueName() == itemID)
|
||||
{
|
||||
item->setSelected (true, true);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (EnabledModulesItem)
|
||||
};
|
81
deps/juce/extras/Projucer/Source/Project/UI/Sidebar/jucer_ProjectTreeItemBase.h
vendored
Normal file
81
deps/juce/extras/Projucer/Source/Project/UI/Sidebar/jucer_ProjectTreeItemBase.h
vendored
Normal file
@ -0,0 +1,81 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
|
||||
//==============================================================================
|
||||
struct ProjectTreeItemBase : public JucerTreeViewBase,
|
||||
public ValueTree::Listener
|
||||
{
|
||||
ProjectTreeItemBase() {}
|
||||
|
||||
void showSettingsPage (Component* content)
|
||||
{
|
||||
content->setComponentID (getUniqueName());
|
||||
std::unique_ptr<Component> comp (content);
|
||||
|
||||
if (auto* pcc = getProjectContentComponent())
|
||||
pcc->setScrollableEditorComponent (std::move (comp));
|
||||
}
|
||||
|
||||
void closeSettingsPage()
|
||||
{
|
||||
if (auto* pcc = getProjectContentComponent())
|
||||
if (auto* content = pcc->getEditorComponent())
|
||||
if (content->getComponentID() == getUniqueName())
|
||||
pcc->hideEditor();
|
||||
}
|
||||
|
||||
void deleteAllSelectedItems() override
|
||||
{
|
||||
auto* tree = getOwnerView();
|
||||
jassert (tree->getNumSelectedItems() <= 1); // multi-select should be disabled
|
||||
|
||||
if (auto* s = dynamic_cast<ProjectTreeItemBase*> (tree->getSelectedItem (0)))
|
||||
s->deleteItem();
|
||||
}
|
||||
|
||||
void itemOpennessChanged (bool isNowOpen) override
|
||||
{
|
||||
if (isNowOpen)
|
||||
refreshSubItems();
|
||||
}
|
||||
|
||||
virtual bool isProjectSettings() const { return false; }
|
||||
virtual bool isModulesList() const { return false; }
|
||||
|
||||
static void updateSize (Component& comp, PropertyGroupComponent& group)
|
||||
{
|
||||
auto width = jmax (550, comp.getParentWidth() - 12);
|
||||
|
||||
auto y = 0;
|
||||
y += group.updateSize (12, y, width - 12);
|
||||
|
||||
y = jmax (comp.getParentHeight(), y);
|
||||
|
||||
comp.setSize (width, y);
|
||||
}
|
||||
};
|
560
deps/juce/extras/Projucer/Source/Project/UI/Sidebar/jucer_Sidebar.h
vendored
Normal file
560
deps/juce/extras/Projucer/Source/Project/UI/Sidebar/jucer_Sidebar.h
vendored
Normal file
@ -0,0 +1,560 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
//==============================================================================
|
||||
class ConcertinaHeader : public Component,
|
||||
public ChangeBroadcaster
|
||||
{
|
||||
public:
|
||||
ConcertinaHeader (String n, Path p)
|
||||
: Component (n), name (n), iconPath (p)
|
||||
{
|
||||
setTitle (getName());
|
||||
|
||||
panelIcon = Icon (iconPath, Colours::white);
|
||||
|
||||
nameLabel.setText (name, dontSendNotification);
|
||||
nameLabel.setJustificationType (Justification::centredLeft);
|
||||
nameLabel.setInterceptsMouseClicks (false, false);
|
||||
nameLabel.setAccessible (false);
|
||||
nameLabel.setColour (Label::textColourId, Colours::white);
|
||||
|
||||
addAndMakeVisible (nameLabel);
|
||||
}
|
||||
|
||||
void resized() override
|
||||
{
|
||||
auto b = getLocalBounds().toFloat();
|
||||
|
||||
iconBounds = b.removeFromLeft (b.getHeight()).reduced (7, 7);
|
||||
arrowBounds = b.removeFromRight (b.getHeight());
|
||||
nameLabel.setBounds (b.toNearestInt());
|
||||
}
|
||||
|
||||
void paint (Graphics& g) override
|
||||
{
|
||||
g.setColour (findColour (defaultButtonBackgroundColourId));
|
||||
g.fillRoundedRectangle (getLocalBounds().reduced (2, 3).toFloat(), 2.0f);
|
||||
|
||||
g.setColour (Colours::white);
|
||||
g.fillPath (ProjucerLookAndFeel::getArrowPath (arrowBounds,
|
||||
getParentComponent()->getBoundsInParent().getY() == yPosition ? 2 : 0,
|
||||
true, Justification::centred));
|
||||
|
||||
panelIcon.draw (g, iconBounds.toFloat(), false);
|
||||
}
|
||||
|
||||
void mouseUp (const MouseEvent& e) override
|
||||
{
|
||||
if (! e.mouseWasDraggedSinceMouseDown())
|
||||
sendChangeMessage();
|
||||
}
|
||||
|
||||
std::unique_ptr<AccessibilityHandler> createAccessibilityHandler() override
|
||||
{
|
||||
return std::make_unique<AccessibilityHandler> (*this,
|
||||
AccessibilityRole::button,
|
||||
AccessibilityActions().addAction (AccessibilityActionType::press,
|
||||
[this] { sendChangeMessage(); }));
|
||||
}
|
||||
|
||||
int direction = 0;
|
||||
int yPosition = 0;
|
||||
|
||||
private:
|
||||
String name;
|
||||
Label nameLabel;
|
||||
|
||||
Path iconPath;
|
||||
Icon panelIcon;
|
||||
|
||||
Rectangle<float> arrowBounds, iconBounds;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ConcertinaHeader)
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
class FindPanel : public Component,
|
||||
private Timer,
|
||||
private FocusChangeListener
|
||||
{
|
||||
public:
|
||||
FindPanel (std::function<void (const String&)> cb)
|
||||
: callback (cb)
|
||||
{
|
||||
addAndMakeVisible (editor);
|
||||
editor.onTextChange = [this] { startTimer (250); };
|
||||
editor.onFocusLost = [this]
|
||||
{
|
||||
isFocused = false;
|
||||
repaint();
|
||||
};
|
||||
|
||||
Desktop::getInstance().addFocusChangeListener (this);
|
||||
|
||||
lookAndFeelChanged();
|
||||
}
|
||||
|
||||
~FindPanel() override
|
||||
{
|
||||
Desktop::getInstance().removeFocusChangeListener (this);
|
||||
}
|
||||
|
||||
void paintOverChildren (Graphics& g) override
|
||||
{
|
||||
if (! isFocused)
|
||||
return;
|
||||
|
||||
g.setColour (findColour (defaultHighlightColourId));
|
||||
|
||||
Path p;
|
||||
p.addRoundedRectangle (getLocalBounds().reduced (2), 3.0f);
|
||||
g.strokePath (p, PathStrokeType (2.0f));
|
||||
}
|
||||
|
||||
|
||||
void resized() override
|
||||
{
|
||||
editor.setBounds (getLocalBounds().reduced (2));
|
||||
}
|
||||
|
||||
private:
|
||||
TextEditor editor;
|
||||
bool isFocused = false;
|
||||
std::function<void (const String&)> callback;
|
||||
|
||||
//==============================================================================
|
||||
void lookAndFeelChanged() override
|
||||
{
|
||||
editor.setTextToShowWhenEmpty ("Filter...", findColour (widgetTextColourId).withAlpha (0.3f));
|
||||
}
|
||||
|
||||
void globalFocusChanged (Component* focusedComponent) override
|
||||
{
|
||||
if (focusedComponent == &editor)
|
||||
{
|
||||
isFocused = true;
|
||||
repaint();
|
||||
}
|
||||
}
|
||||
|
||||
void timerCallback() override
|
||||
{
|
||||
stopTimer();
|
||||
callback (editor.getText());
|
||||
}
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FindPanel)
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
class ConcertinaTreeComponent : public Component
|
||||
{
|
||||
public:
|
||||
class AdditionalComponents
|
||||
{
|
||||
public:
|
||||
enum Type
|
||||
{
|
||||
addButton = (1 << 0),
|
||||
settingsButton = (1 << 1),
|
||||
findPanel = (1 << 2)
|
||||
};
|
||||
|
||||
AdditionalComponents with (Type t)
|
||||
{
|
||||
auto copy = *this;
|
||||
copy.componentTypes |= t;
|
||||
|
||||
return copy;
|
||||
}
|
||||
|
||||
bool has (Type t) const noexcept
|
||||
{
|
||||
return (componentTypes & t) != 0;
|
||||
}
|
||||
|
||||
private:
|
||||
int componentTypes = 0;
|
||||
};
|
||||
|
||||
ConcertinaTreeComponent (const String& name,
|
||||
TreePanelBase* tree,
|
||||
AdditionalComponents additionalComponents)
|
||||
: Component (name),
|
||||
treeToDisplay (tree)
|
||||
{
|
||||
setTitle (getName());
|
||||
setFocusContainerType (FocusContainerType::focusContainer);
|
||||
|
||||
if (additionalComponents.has (AdditionalComponents::addButton))
|
||||
{
|
||||
addButton = std::make_unique<IconButton> ("Add", getIcons().plus);
|
||||
addAndMakeVisible (addButton.get());
|
||||
addButton->onClick = [this] { showAddMenu(); };
|
||||
}
|
||||
|
||||
if (additionalComponents.has (AdditionalComponents::settingsButton))
|
||||
{
|
||||
settingsButton = std::make_unique<IconButton> ("Settings", getIcons().settings);
|
||||
addAndMakeVisible (settingsButton.get());
|
||||
settingsButton->onClick = [this] { showSettings(); };
|
||||
}
|
||||
|
||||
if (additionalComponents.has (AdditionalComponents::findPanel))
|
||||
{
|
||||
findPanel = std::make_unique<FindPanel> ([this] (const String& filter) { treeToDisplay->rootItem->setSearchFilter (filter); });
|
||||
addAndMakeVisible (findPanel.get());
|
||||
}
|
||||
|
||||
addAndMakeVisible (treeToDisplay.get());
|
||||
}
|
||||
|
||||
void resized() override
|
||||
{
|
||||
auto bounds = getLocalBounds();
|
||||
|
||||
if (addButton != nullptr || settingsButton != nullptr || findPanel != nullptr)
|
||||
{
|
||||
auto bottomSlice = bounds.removeFromBottom (25);
|
||||
bottomSlice.removeFromRight (3);
|
||||
|
||||
if (addButton != nullptr)
|
||||
addButton->setBounds (bottomSlice.removeFromRight (25).reduced (2));
|
||||
|
||||
if (settingsButton != nullptr)
|
||||
settingsButton->setBounds (bottomSlice.removeFromRight (25).reduced (2));
|
||||
|
||||
if (findPanel != nullptr)
|
||||
findPanel->setBounds (bottomSlice.reduced (2));
|
||||
}
|
||||
|
||||
treeToDisplay->setBounds (bounds);
|
||||
}
|
||||
|
||||
TreePanelBase* getTree() const noexcept { return treeToDisplay.get(); }
|
||||
|
||||
private:
|
||||
std::unique_ptr<TreePanelBase> treeToDisplay;
|
||||
std::unique_ptr<IconButton> addButton, settingsButton;
|
||||
std::unique_ptr<FindPanel> findPanel;
|
||||
|
||||
void showAddMenu()
|
||||
{
|
||||
auto numSelected = treeToDisplay->tree.getNumSelectedItems();
|
||||
|
||||
if (numSelected > 1)
|
||||
return;
|
||||
|
||||
if (numSelected == 0)
|
||||
{
|
||||
if (auto* root = dynamic_cast<JucerTreeViewBase*> (treeToDisplay->tree.getRootItem()))
|
||||
root->showPopupMenu (addButton->getScreenBounds().getCentre());
|
||||
}
|
||||
else
|
||||
{
|
||||
if (auto* item = dynamic_cast<JucerTreeViewBase*> (treeToDisplay->tree.getSelectedItem (0)))
|
||||
item->showAddMenu (addButton->getScreenBounds().getCentre());
|
||||
}
|
||||
}
|
||||
|
||||
void showSettings()
|
||||
{
|
||||
if (auto* root = dynamic_cast<JucerTreeViewBase*> (treeToDisplay->tree.getRootItem()))
|
||||
{
|
||||
treeToDisplay->tree.clearSelectedItems();
|
||||
root->showDocument();
|
||||
}
|
||||
}
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ConcertinaTreeComponent)
|
||||
};
|
||||
|
||||
|
||||
//==============================================================================
|
||||
struct ProjectSettingsComponent : public Component,
|
||||
private ChangeListener
|
||||
{
|
||||
ProjectSettingsComponent (Project& p)
|
||||
: project (p),
|
||||
group (project.getProjectFilenameRootString(),
|
||||
Icon (getIcons().settings, Colours::transparentBlack))
|
||||
{
|
||||
setTitle ("Project Settings");
|
||||
setFocusContainerType (FocusContainerType::focusContainer);
|
||||
|
||||
addAndMakeVisible (group);
|
||||
|
||||
updatePropertyList();
|
||||
project.addChangeListener (this);
|
||||
}
|
||||
|
||||
~ProjectSettingsComponent() override
|
||||
{
|
||||
project.removeChangeListener (this);
|
||||
}
|
||||
|
||||
void resized() override
|
||||
{
|
||||
group.updateSize (12, 0, getWidth() - 24);
|
||||
group.setBounds (getLocalBounds().reduced (12, 0));
|
||||
}
|
||||
|
||||
void updatePropertyList()
|
||||
{
|
||||
PropertyListBuilder props;
|
||||
project.createPropertyEditors (props);
|
||||
group.setProperties (props);
|
||||
group.setName ("Project Settings");
|
||||
|
||||
lastProjectType = project.getProjectTypeString();
|
||||
parentSizeChanged();
|
||||
}
|
||||
|
||||
void changeListenerCallback (ChangeBroadcaster*) override
|
||||
{
|
||||
if (lastProjectType != project.getProjectTypeString())
|
||||
updatePropertyList();
|
||||
}
|
||||
|
||||
void parentSizeChanged() override
|
||||
{
|
||||
auto width = jmax (550, getParentWidth());
|
||||
auto y = group.updateSize (12, 0, width - 12);
|
||||
|
||||
y = jmax (getParentHeight(), y);
|
||||
|
||||
setSize (width, y);
|
||||
}
|
||||
|
||||
Project& project;
|
||||
var lastProjectType;
|
||||
PropertyGroupComponent group;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ProjectSettingsComponent)
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
struct FileTreePanel : public TreePanelBase
|
||||
{
|
||||
FileTreePanel (Project& p)
|
||||
: TreePanelBase (&p, "fileTreeState")
|
||||
{
|
||||
tree.setMultiSelectEnabled (true);
|
||||
setRoot (std::make_unique<TreeItemTypes::GroupItem> (p.getMainGroup()));
|
||||
tree.setRootItemVisible (false);
|
||||
}
|
||||
|
||||
void updateMissingFileStatuses()
|
||||
{
|
||||
if (auto* p = dynamic_cast<TreeItemTypes::FileTreeItemBase*> (rootItem.get()))
|
||||
p->checkFileStatus();
|
||||
}
|
||||
};
|
||||
|
||||
struct ModuleTreePanel : public TreePanelBase
|
||||
{
|
||||
ModuleTreePanel (Project& p)
|
||||
: TreePanelBase (&p, "moduleTreeState")
|
||||
{
|
||||
tree.setMultiSelectEnabled (false);
|
||||
setRoot (std::make_unique<TreeItemTypes::EnabledModulesItem> (p));
|
||||
tree.setRootItemVisible (false);
|
||||
}
|
||||
};
|
||||
|
||||
struct ExportersTreePanel : public TreePanelBase
|
||||
{
|
||||
ExportersTreePanel (Project& p)
|
||||
: TreePanelBase (&p, "exportersTreeState")
|
||||
{
|
||||
tree.setMultiSelectEnabled (false);
|
||||
setRoot (std::make_unique<TreeItemTypes::ExportersTreeRoot> (p));
|
||||
tree.setRootItemVisible (false);
|
||||
}
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
class Sidebar : public Component,
|
||||
private ChangeListener
|
||||
{
|
||||
public:
|
||||
Sidebar (Project* p)
|
||||
: project (p)
|
||||
{
|
||||
setFocusContainerType (FocusContainerType::focusContainer);
|
||||
|
||||
if (project != nullptr)
|
||||
buildConcertina();
|
||||
}
|
||||
|
||||
~Sidebar() override
|
||||
{
|
||||
TreePanelBase* panels[] = { getFileTreePanel(), getModuleTreePanel(), getExportersTreePanel() };
|
||||
|
||||
for (auto* panel : panels)
|
||||
if (panel != nullptr)
|
||||
panel->saveOpenness();
|
||||
}
|
||||
|
||||
void paint (Graphics& g) override
|
||||
{
|
||||
g.fillAll (findColour (secondaryBackgroundColourId));
|
||||
}
|
||||
|
||||
void resized() override
|
||||
{
|
||||
concertinaPanel.setBounds (getLocalBounds().withTrimmedBottom (3));
|
||||
}
|
||||
|
||||
TreePanelBase* getTreeWithSelectedItems()
|
||||
{
|
||||
for (auto i = concertinaPanel.getNumPanels() - 1; i >= 0; --i)
|
||||
{
|
||||
if (auto* treeComponent = dynamic_cast<ConcertinaTreeComponent*> (concertinaPanel.getPanel (i)))
|
||||
{
|
||||
if (auto* base = treeComponent->getTree())
|
||||
if (base->tree.getNumSelectedItems() != 0)
|
||||
return base;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
FileTreePanel* getFileTreePanel() { return getPanel<FileTreePanel> (0); }
|
||||
ModuleTreePanel* getModuleTreePanel() { return getPanel<ModuleTreePanel> (1); }
|
||||
ExportersTreePanel* getExportersTreePanel() { return getPanel<ExportersTreePanel> (2); }
|
||||
|
||||
void showPanel (int panelIndex)
|
||||
{
|
||||
jassert (isPositiveAndBelow (panelIndex, concertinaPanel.getNumPanels()));
|
||||
|
||||
concertinaPanel.expandPanelFully (concertinaPanel.getPanel (panelIndex), true);
|
||||
}
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
template <typename PanelType>
|
||||
PanelType* getPanel (int panelIndex)
|
||||
{
|
||||
if (auto* panel = dynamic_cast<ConcertinaTreeComponent*> (concertinaPanel.getPanel (panelIndex)))
|
||||
return dynamic_cast<PanelType*> (panel->getTree());
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void changeListenerCallback (ChangeBroadcaster* source) override
|
||||
{
|
||||
const auto pointerMatches = [source] (const std::unique_ptr<ConcertinaHeader>& header) { return header.get() == source; };
|
||||
const auto it = std::find_if (headers.begin(), headers.end(), pointerMatches);
|
||||
const auto index = (int) std::distance (headers.begin(), it);
|
||||
|
||||
if (index != (int) headers.size())
|
||||
concertinaPanel.expandPanelFully (concertinaPanel.getPanel (index), true);
|
||||
}
|
||||
|
||||
void buildConcertina()
|
||||
{
|
||||
for (auto i = concertinaPanel.getNumPanels() - 1; i >= 0 ; --i)
|
||||
concertinaPanel.removePanel (concertinaPanel.getPanel (i));
|
||||
|
||||
headers.clear();
|
||||
|
||||
auto addPanel = [this] (const String& name,
|
||||
TreePanelBase* tree,
|
||||
ConcertinaTreeComponent::AdditionalComponents components,
|
||||
const Path& icon)
|
||||
{
|
||||
if (project != nullptr)
|
||||
concertinaPanel.addPanel (-1, new ConcertinaTreeComponent (name, tree, components), true);
|
||||
|
||||
headers.push_back (std::make_unique<ConcertinaHeader> (name, icon));
|
||||
};
|
||||
|
||||
using AdditionalComponents = ConcertinaTreeComponent::AdditionalComponents;
|
||||
|
||||
addPanel ("File Explorer", new FileTreePanel (*project),
|
||||
AdditionalComponents{}
|
||||
.with (AdditionalComponents::addButton)
|
||||
.with (AdditionalComponents::findPanel),
|
||||
getIcons().fileExplorer);
|
||||
|
||||
addPanel ("Modules", new ModuleTreePanel (*project),
|
||||
AdditionalComponents{}
|
||||
.with (AdditionalComponents::addButton)
|
||||
.with (AdditionalComponents::settingsButton),
|
||||
getIcons().modules);
|
||||
|
||||
addPanel ("Exporters", new ExportersTreePanel (*project),
|
||||
AdditionalComponents{}.with (AdditionalComponents::addButton),
|
||||
getIcons().exporter);
|
||||
|
||||
for (int i = 0; i < concertinaPanel.getNumPanels(); ++i)
|
||||
{
|
||||
auto* p = concertinaPanel.getPanel (i);
|
||||
auto* h = headers[(size_t) i].get();
|
||||
p->addMouseListener (this, true);
|
||||
|
||||
h->addChangeListener (this);
|
||||
h->yPosition = i * 30;
|
||||
|
||||
concertinaPanel.setCustomPanelHeader (p, h, false);
|
||||
concertinaPanel.setPanelHeaderSize (p, 30);
|
||||
}
|
||||
|
||||
addAndMakeVisible (concertinaPanel);
|
||||
}
|
||||
|
||||
void mouseDown (const MouseEvent& e) override
|
||||
{
|
||||
for (auto i = concertinaPanel.getNumPanels() - 1; i >= 0; --i)
|
||||
{
|
||||
if (auto* p = concertinaPanel.getPanel (i))
|
||||
{
|
||||
if (! (p->isParentOf (e.eventComponent)))
|
||||
{
|
||||
auto* base = dynamic_cast<TreePanelBase*> (p);
|
||||
|
||||
if (base == nullptr)
|
||||
if (auto* concertina = dynamic_cast<ConcertinaTreeComponent*> (p))
|
||||
base = concertina->getTree();
|
||||
|
||||
if (base != nullptr)
|
||||
base->tree.clearSelectedItems();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
ConcertinaPanel concertinaPanel;
|
||||
std::vector<std::unique_ptr<ConcertinaHeader>> headers;
|
||||
Project* project = nullptr;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Sidebar)
|
||||
};
|
42
deps/juce/extras/Projucer/Source/Project/UI/Sidebar/jucer_TreeItemTypes.h
vendored
Normal file
42
deps/juce/extras/Projucer/Source/Project/UI/Sidebar/jucer_TreeItemTypes.h
vendored
Normal file
@ -0,0 +1,42 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../../../ProjectSaving/jucer_ProjectExporter.h"
|
||||
#include "../../../Application/Windows/jucer_TranslationToolWindowComponent.h"
|
||||
#include "../../../Utility/UI/jucer_JucerTreeViewBase.h"
|
||||
#include "../../../Utility/Helpers/jucer_NewFileWizard.h"
|
||||
#include "../jucer_ContentViewComponents.h"
|
||||
#include "../jucer_FileGroupInformationComponent.h"
|
||||
#include "../jucer_ModulesInformationComponent.h"
|
||||
|
||||
struct TreeItemTypes
|
||||
{
|
||||
#include "jucer_ProjectTreeItemBase.h"
|
||||
#include "jucer_ExporterTreeItems.h"
|
||||
#include "jucer_ModuleTreeItems.h"
|
||||
#include "jucer_FileTreeItems.h"
|
||||
};
|
112
deps/juce/extras/Projucer/Source/Project/UI/jucer_ContentViewComponent.h
vendored
Normal file
112
deps/juce/extras/Projucer/Source/Project/UI/jucer_ContentViewComponent.h
vendored
Normal file
@ -0,0 +1,112 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
|
||||
//==============================================================================
|
||||
class ContentViewComponent : public Component
|
||||
{
|
||||
public:
|
||||
ContentViewComponent()
|
||||
{
|
||||
setTitle ("Content");
|
||||
setFocusContainerType (Component::FocusContainerType::focusContainer);
|
||||
|
||||
addAndMakeVisible (logoComponent);
|
||||
|
||||
addAndMakeVisible (fileNameLabel);
|
||||
fileNameLabel.setJustificationType (Justification::centred);
|
||||
}
|
||||
|
||||
void resized() override
|
||||
{
|
||||
auto bounds = getLocalBounds();
|
||||
|
||||
fileNameLabel.setBounds (bounds.removeFromTop (15));
|
||||
|
||||
if (content != nullptr)
|
||||
content->setBounds (bounds);
|
||||
else
|
||||
logoComponent.setBounds (bounds);
|
||||
}
|
||||
|
||||
Component* getCurrentComponent() noexcept
|
||||
{
|
||||
return content.get();
|
||||
}
|
||||
|
||||
void setContent (std::unique_ptr<Component> newContent,
|
||||
const String& labelText)
|
||||
{
|
||||
content = std::move (newContent);
|
||||
addAndMakeVisible (content.get());
|
||||
|
||||
fileNameLabel.setVisible (labelText.isNotEmpty());
|
||||
fileNameLabel.setText (labelText, dontSendNotification);
|
||||
|
||||
resized();
|
||||
}
|
||||
|
||||
private:
|
||||
class LogoComponent : public Component
|
||||
{
|
||||
public:
|
||||
void paint (Graphics& g) override
|
||||
{
|
||||
g.setColour (findColour (defaultTextColourId));
|
||||
|
||||
auto bounds = getLocalBounds();
|
||||
bounds.reduce (bounds.getWidth() / 6, bounds.getHeight() / 6);
|
||||
|
||||
g.setFont (15.0f);
|
||||
g.drawFittedText (versionInfo, bounds.removeFromBottom (50), Justification::centredBottom, 3);
|
||||
|
||||
if (logo != nullptr)
|
||||
logo->drawWithin (g, bounds.withTrimmedBottom (bounds.getHeight() / 4).toFloat(),
|
||||
RectanglePlacement (RectanglePlacement::centred), 1.0f);
|
||||
}
|
||||
|
||||
private:
|
||||
std::unique_ptr<Drawable> logo = []() -> std::unique_ptr<Drawable>
|
||||
{
|
||||
if (auto svg = parseXML (BinaryData::background_logo_svg))
|
||||
return Drawable::createFromSVG (*svg);
|
||||
|
||||
jassertfalse;
|
||||
return {};
|
||||
}();
|
||||
|
||||
String versionInfo = SystemStats::getJUCEVersion()
|
||||
+ newLine
|
||||
+ ProjucerApplication::getApp().getVersionDescription();
|
||||
};
|
||||
|
||||
std::unique_ptr<Component> content;
|
||||
LogoComponent logoComponent;
|
||||
Label fileNameLabel;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ContentViewComponent)
|
||||
};
|
446
deps/juce/extras/Projucer/Source/Project/UI/jucer_ContentViewComponents.h
vendored
Normal file
446
deps/juce/extras/Projucer/Source/Project/UI/jucer_ContentViewComponents.h
vendored
Normal file
@ -0,0 +1,446 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../../Utility/UI/PropertyComponents/jucer_LabelPropertyComponent.h"
|
||||
|
||||
//==============================================================================
|
||||
struct ContentViewHeader : public Component
|
||||
{
|
||||
ContentViewHeader (String headerName, Icon headerIcon)
|
||||
: name (headerName), icon (headerIcon)
|
||||
{
|
||||
setTitle (name);
|
||||
}
|
||||
|
||||
void paint (Graphics& g) override
|
||||
{
|
||||
g.fillAll (findColour (contentHeaderBackgroundColourId));
|
||||
|
||||
auto bounds = getLocalBounds().reduced (20, 0);
|
||||
|
||||
icon.withColour (Colours::white).draw (g, bounds.toFloat().removeFromRight (30), false);
|
||||
|
||||
g.setColour (Colours::white);
|
||||
g.setFont (Font (18.0f));
|
||||
g.drawFittedText (name, bounds, Justification::centredLeft, 1);
|
||||
}
|
||||
|
||||
String name;
|
||||
Icon icon;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
class ListBoxHeader : public Component
|
||||
{
|
||||
public:
|
||||
ListBoxHeader (Array<String> columnHeaders)
|
||||
{
|
||||
for (auto s : columnHeaders)
|
||||
{
|
||||
addAndMakeVisible (headers.add (new Label (s, s)));
|
||||
widths.add (1.0f / (float) columnHeaders.size());
|
||||
}
|
||||
|
||||
setSize (200, 40);
|
||||
}
|
||||
|
||||
ListBoxHeader (Array<String> columnHeaders, Array<float> columnWidths)
|
||||
{
|
||||
jassert (columnHeaders.size() == columnWidths.size());
|
||||
|
||||
auto index = 0;
|
||||
for (auto s : columnHeaders)
|
||||
{
|
||||
addAndMakeVisible (headers.add (new Label (s, s)));
|
||||
widths.add (columnWidths.getUnchecked (index++));
|
||||
}
|
||||
|
||||
recalculateWidths();
|
||||
|
||||
setSize (200, 40);
|
||||
}
|
||||
|
||||
void resized() override
|
||||
{
|
||||
auto bounds = getLocalBounds();
|
||||
auto width = bounds.getWidth();
|
||||
|
||||
auto index = 0;
|
||||
for (auto h : headers)
|
||||
{
|
||||
auto headerWidth = roundToInt ((float) width * widths.getUnchecked (index));
|
||||
h->setBounds (bounds.removeFromLeft (headerWidth));
|
||||
++index;
|
||||
}
|
||||
}
|
||||
|
||||
void setColumnHeaderWidth (int index, float proportionOfWidth)
|
||||
{
|
||||
if (! (isPositiveAndBelow (index, headers.size()) && isPositiveAndNotGreaterThan (proportionOfWidth, 1.0f)))
|
||||
{
|
||||
jassertfalse;
|
||||
return;
|
||||
}
|
||||
|
||||
widths.set (index, proportionOfWidth);
|
||||
recalculateWidths (index);
|
||||
}
|
||||
|
||||
int getColumnX (int index)
|
||||
{
|
||||
auto prop = 0.0f;
|
||||
for (int i = 0; i < index; ++i)
|
||||
prop += widths.getUnchecked (i);
|
||||
|
||||
return roundToInt (prop * (float) getWidth());
|
||||
}
|
||||
|
||||
float getProportionAtIndex (int index)
|
||||
{
|
||||
jassert (isPositiveAndBelow (index, widths.size()));
|
||||
return widths.getUnchecked (index);
|
||||
}
|
||||
|
||||
private:
|
||||
OwnedArray<Label> headers;
|
||||
Array<float> widths;
|
||||
|
||||
void recalculateWidths (int indexToIgnore = -1)
|
||||
{
|
||||
auto total = 0.0f;
|
||||
|
||||
for (auto w : widths)
|
||||
total += w;
|
||||
|
||||
if (total == 1.0f)
|
||||
return;
|
||||
|
||||
auto diff = 1.0f - total;
|
||||
auto amount = diff / static_cast<float> (indexToIgnore == -1 ? widths.size() : widths.size() - 1);
|
||||
|
||||
for (int i = 0; i < widths.size(); ++i)
|
||||
{
|
||||
if (i != indexToIgnore)
|
||||
{
|
||||
auto val = widths.getUnchecked (i);
|
||||
widths.set (i, val + amount);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
class InfoButton : public Button
|
||||
{
|
||||
public:
|
||||
InfoButton (const String& infoToDisplay = {})
|
||||
: Button ({})
|
||||
{
|
||||
setTitle ("Info");
|
||||
|
||||
if (infoToDisplay.isNotEmpty())
|
||||
setInfoToDisplay (infoToDisplay);
|
||||
|
||||
setSize (20, 20);
|
||||
}
|
||||
|
||||
void paintButton (Graphics& g, bool isMouseOverButton, bool isButtonDown) override
|
||||
{
|
||||
auto bounds = getLocalBounds().toFloat().reduced (2);
|
||||
auto& icon = getIcons().info;
|
||||
|
||||
g.setColour (findColour (treeIconColourId).withMultipliedAlpha (isMouseOverButton || isButtonDown ? 1.0f : 0.5f));
|
||||
|
||||
if (isButtonDown)
|
||||
g.fillEllipse (bounds);
|
||||
else
|
||||
g.fillPath (icon, RectanglePlacement (RectanglePlacement::centred)
|
||||
.getTransformToFit (icon.getBounds(), bounds));
|
||||
}
|
||||
|
||||
void clicked() override
|
||||
{
|
||||
auto w = std::make_unique<InfoWindow> (info);
|
||||
w->setSize (width, w->getHeight() * numLines + 10);
|
||||
|
||||
CallOutBox::launchAsynchronously (std::move (w), getScreenBounds(), nullptr);
|
||||
}
|
||||
|
||||
using Button::clicked;
|
||||
|
||||
void setInfoToDisplay (const String& infoToDisplay)
|
||||
{
|
||||
if (infoToDisplay.isNotEmpty())
|
||||
{
|
||||
info = infoToDisplay;
|
||||
|
||||
auto stringWidth = roundToInt (Font (14.0f).getStringWidthFloat (info));
|
||||
width = jmin (300, stringWidth);
|
||||
|
||||
numLines += static_cast<int> (stringWidth / width);
|
||||
|
||||
setHelpText (info);
|
||||
}
|
||||
}
|
||||
|
||||
void setAssociatedComponent (Component* comp) { associatedComponent = comp; }
|
||||
Component* getAssociatedComponent() { return associatedComponent; }
|
||||
|
||||
private:
|
||||
String info;
|
||||
Component* associatedComponent = nullptr;
|
||||
int width;
|
||||
int numLines = 1;
|
||||
|
||||
//==============================================================================
|
||||
struct InfoWindow : public Component
|
||||
{
|
||||
InfoWindow (const String& s)
|
||||
: stringToDisplay (s)
|
||||
{
|
||||
setSize (150, 14);
|
||||
}
|
||||
|
||||
void paint (Graphics& g) override
|
||||
{
|
||||
g.fillAll (findColour (secondaryBackgroundColourId));
|
||||
|
||||
g.setColour (findColour (defaultTextColourId));
|
||||
g.setFont (Font (14.0f));
|
||||
g.drawFittedText (stringToDisplay, getLocalBounds(), Justification::centred, 15, 0.75f);
|
||||
}
|
||||
|
||||
String stringToDisplay;
|
||||
};
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (InfoButton)
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
class PropertyGroupComponent : public Component,
|
||||
private TextPropertyComponent::Listener
|
||||
{
|
||||
public:
|
||||
PropertyGroupComponent (String name, Icon icon, String desc = {})
|
||||
: header (name, icon),
|
||||
description (desc)
|
||||
{
|
||||
addAndMakeVisible (header);
|
||||
}
|
||||
|
||||
void setProperties (const PropertyListBuilder& newProps)
|
||||
{
|
||||
clearProperties();
|
||||
|
||||
if (description.isNotEmpty())
|
||||
properties.push_back (std::make_unique<LabelPropertyComponent> (description, 16, Font (16.0f),
|
||||
Justification::centredLeft));
|
||||
|
||||
for (auto* comp : newProps.components)
|
||||
properties.push_back (std::unique_ptr<PropertyComponent> (comp));
|
||||
|
||||
for (auto& prop : properties)
|
||||
{
|
||||
const auto propertyTooltip = prop->getTooltip();
|
||||
|
||||
if (propertyTooltip.isNotEmpty())
|
||||
{
|
||||
// set the tooltip to empty so it only displays when its button is clicked
|
||||
prop->setTooltip ({});
|
||||
|
||||
auto infoButton = std::make_unique<InfoButton> (propertyTooltip);
|
||||
infoButton->setAssociatedComponent (prop.get());
|
||||
|
||||
auto propertyAndInfoWrapper = std::make_unique<PropertyAndInfoWrapper> (*prop, *infoButton.get());
|
||||
addAndMakeVisible (propertyAndInfoWrapper.get());
|
||||
propertyComponentsWithInfo.push_back (std::move (propertyAndInfoWrapper));
|
||||
|
||||
infoButtons.push_back (std::move (infoButton));
|
||||
}
|
||||
else
|
||||
{
|
||||
addAndMakeVisible (prop.get());
|
||||
}
|
||||
|
||||
if (auto* multiChoice = dynamic_cast<MultiChoicePropertyComponent*> (prop.get()))
|
||||
multiChoice->onHeightChange = [this] { updateSize(); };
|
||||
|
||||
if (auto* text = dynamic_cast<TextPropertyComponent*> (prop.get()))
|
||||
if (text->isTextEditorMultiLine())
|
||||
text->addListener (this);
|
||||
}
|
||||
}
|
||||
|
||||
int updateSize (int x, int y, int width)
|
||||
{
|
||||
header.setBounds (0, 0, width, headerSize);
|
||||
auto height = header.getBottom() + 10;
|
||||
|
||||
for (auto& pp : properties)
|
||||
{
|
||||
const auto propertyHeight = pp->getPreferredHeight()
|
||||
+ (getHeightMultiplier (pp.get()) * pp->getPreferredHeight());
|
||||
|
||||
auto iter = std::find_if (propertyComponentsWithInfo.begin(), propertyComponentsWithInfo.end(),
|
||||
[&pp] (const std::unique_ptr<PropertyAndInfoWrapper>& w) { return &w->propertyComponent == pp.get(); });
|
||||
|
||||
if (iter != propertyComponentsWithInfo.end())
|
||||
(*iter)->setBounds (0, height, width - 10, propertyHeight);
|
||||
else
|
||||
pp->setBounds (40, height, width - 50, propertyHeight);
|
||||
|
||||
if (shouldResizePropertyComponent (pp.get()))
|
||||
resizePropertyComponent (pp.get());
|
||||
|
||||
height += pp->getHeight() + 10;
|
||||
}
|
||||
|
||||
height += 16;
|
||||
|
||||
setBounds (x, y, width, jmax (height, getParentHeight()));
|
||||
|
||||
return height;
|
||||
}
|
||||
|
||||
void paint (Graphics& g) override
|
||||
{
|
||||
g.fillAll (findColour (secondaryBackgroundColourId));
|
||||
}
|
||||
|
||||
const std::vector<std::unique_ptr<PropertyComponent>>& getProperties() const noexcept
|
||||
{
|
||||
return properties;
|
||||
}
|
||||
|
||||
void clearProperties()
|
||||
{
|
||||
propertyComponentsWithInfo.clear();
|
||||
infoButtons.clear();
|
||||
properties.clear();
|
||||
}
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
struct PropertyAndInfoWrapper : public Component
|
||||
{
|
||||
PropertyAndInfoWrapper (PropertyComponent& c, InfoButton& i)
|
||||
: propertyComponent (c),
|
||||
infoButton (i)
|
||||
{
|
||||
setFocusContainerType (FocusContainerType::focusContainer);
|
||||
setTitle (propertyComponent.getName());
|
||||
|
||||
addAndMakeVisible (propertyComponent);
|
||||
addAndMakeVisible (infoButton);
|
||||
}
|
||||
|
||||
void resized() override
|
||||
{
|
||||
auto bounds = getLocalBounds();
|
||||
|
||||
bounds.removeFromLeft (40);
|
||||
bounds.removeFromRight (10);
|
||||
|
||||
propertyComponent.setBounds (bounds);
|
||||
infoButton.setCentrePosition (20, bounds.getHeight() / 2);
|
||||
}
|
||||
|
||||
PropertyComponent& propertyComponent;
|
||||
InfoButton& infoButton;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
void textPropertyComponentChanged (TextPropertyComponent* comp) override
|
||||
{
|
||||
auto fontHeight = [comp]
|
||||
{
|
||||
Label tmpLabel;
|
||||
return comp->getLookAndFeel().getLabelFont (tmpLabel).getHeight();
|
||||
}();
|
||||
|
||||
auto lines = StringArray::fromLines (comp->getText());
|
||||
|
||||
comp->setPreferredHeight (jmax (100, 10 + roundToInt (fontHeight * (float) lines.size())));
|
||||
|
||||
updateSize();
|
||||
}
|
||||
|
||||
void updateSize()
|
||||
{
|
||||
updateSize (getX(), getY(), getWidth());
|
||||
|
||||
if (auto* parent = getParentComponent())
|
||||
parent->parentSizeChanged();
|
||||
}
|
||||
|
||||
bool shouldResizePropertyComponent (PropertyComponent* p)
|
||||
{
|
||||
if (auto* textComp = dynamic_cast<TextPropertyComponent*> (p))
|
||||
return ! textComp->isTextEditorMultiLine();
|
||||
|
||||
return (dynamic_cast<ChoicePropertyComponent*> (p) != nullptr
|
||||
|| dynamic_cast<ButtonPropertyComponent*> (p) != nullptr
|
||||
|| dynamic_cast<BooleanPropertyComponent*> (p) != nullptr);
|
||||
}
|
||||
|
||||
void resizePropertyComponent (PropertyComponent* pp)
|
||||
{
|
||||
for (auto i = pp->getNumChildComponents() - 1; i >= 0; --i)
|
||||
{
|
||||
auto* child = pp->getChildComponent (i);
|
||||
|
||||
auto bounds = child->getBounds();
|
||||
child->setBounds (bounds.withSizeKeepingCentre (child->getWidth(), pp->getPreferredHeight()));
|
||||
}
|
||||
}
|
||||
|
||||
int getHeightMultiplier (PropertyComponent* pp)
|
||||
{
|
||||
auto availableTextWidth = ProjucerLookAndFeel::getTextWidthForPropertyComponent (pp);
|
||||
|
||||
auto font = ProjucerLookAndFeel::getPropertyComponentFont();
|
||||
auto nameWidth = font.getStringWidthFloat (pp->getName());
|
||||
|
||||
if (availableTextWidth == 0)
|
||||
return 0;
|
||||
|
||||
return static_cast<int> (nameWidth / (float) availableTextWidth);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
static constexpr int headerSize = 40;
|
||||
|
||||
std::vector<std::unique_ptr<PropertyComponent>> properties;
|
||||
std::vector<std::unique_ptr<InfoButton>> infoButtons;
|
||||
std::vector<std::unique_ptr<PropertyAndInfoWrapper>> propertyComponentsWithInfo;
|
||||
|
||||
ContentViewHeader header;
|
||||
String description;
|
||||
|
||||
//==============================================================================
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (PropertyGroupComponent)
|
||||
};
|
363
deps/juce/extras/Projucer/Source/Project/UI/jucer_FileGroupInformationComponent.h
vendored
Normal file
363
deps/juce/extras/Projucer/Source/Project/UI/jucer_FileGroupInformationComponent.h
vendored
Normal file
@ -0,0 +1,363 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
|
||||
//==============================================================================
|
||||
class FileGroupInformationComponent : public Component,
|
||||
private ListBoxModel,
|
||||
private ValueTree::Listener
|
||||
{
|
||||
public:
|
||||
FileGroupInformationComponent (const Project::Item& group)
|
||||
: item (group),
|
||||
header (item.getName(), { getIcons().openFolder, Colours::transparentBlack })
|
||||
{
|
||||
list.setHeaderComponent (std::make_unique<ListBoxHeader> (Array<String> { "File", "Binary Resource", "Xcode Resource", "Compile", "Skip PCH", "Compiler Flag Scheme" },
|
||||
Array<float> { 0.25f, 0.125f, 0.125f, 0.125f, 0.125f, 0.25f }));
|
||||
list.setModel (this);
|
||||
list.setColour (ListBox::backgroundColourId, Colours::transparentBlack);
|
||||
addAndMakeVisible (list);
|
||||
list.updateContent();
|
||||
list.setRowHeight (30);
|
||||
item.state.addListener (this);
|
||||
lookAndFeelChanged();
|
||||
|
||||
addAndMakeVisible (header);
|
||||
}
|
||||
|
||||
~FileGroupInformationComponent() override
|
||||
{
|
||||
item.state.removeListener (this);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void paint (Graphics& g) override
|
||||
{
|
||||
g.setColour (findColour (secondaryBackgroundColourId));
|
||||
g.fillRect (getLocalBounds().reduced (12, 0));
|
||||
}
|
||||
|
||||
void resized() override
|
||||
{
|
||||
auto bounds = getLocalBounds().reduced (12, 0);
|
||||
|
||||
header.setBounds (bounds.removeFromTop (40));
|
||||
list.setBounds (bounds.reduced (10, 4));
|
||||
}
|
||||
|
||||
void parentSizeChanged() override
|
||||
{
|
||||
setSize (jmax (550, getParentWidth()), getParentHeight());
|
||||
}
|
||||
|
||||
int getNumRows() override
|
||||
{
|
||||
return item.getNumChildren();
|
||||
}
|
||||
|
||||
void paintListBoxItem (int rowNumber, Graphics& g, int width, int height, bool /*rowIsSelected*/) override
|
||||
{
|
||||
g.setColour (findColour (rowNumber % 2 == 0 ? widgetBackgroundColourId
|
||||
: secondaryWidgetBackgroundColourId));
|
||||
g.fillRect (0, 0, width, height - 1);
|
||||
}
|
||||
|
||||
Component* refreshComponentForRow (int rowNumber, bool /*isRowSelected*/, Component* existingComponentToUpdate) override
|
||||
{
|
||||
std::unique_ptr<Component> existing (existingComponentToUpdate);
|
||||
|
||||
if (rowNumber < getNumRows())
|
||||
{
|
||||
auto child = item.getChild (rowNumber);
|
||||
|
||||
if (existingComponentToUpdate == nullptr
|
||||
|| dynamic_cast<FileOptionComponent*> (existing.get())->item != child)
|
||||
{
|
||||
existing.reset();
|
||||
existing.reset (new FileOptionComponent (child, dynamic_cast<ListBoxHeader*> (list.getHeaderComponent())));
|
||||
}
|
||||
}
|
||||
|
||||
return existing.release();
|
||||
}
|
||||
|
||||
String getGroupPath() const { return item.getFile().getFullPathName(); }
|
||||
|
||||
//==============================================================================
|
||||
void valueTreePropertyChanged (ValueTree&, const Identifier&) override { itemChanged(); }
|
||||
void valueTreeChildAdded (ValueTree&, ValueTree&) override { itemChanged(); }
|
||||
void valueTreeChildRemoved (ValueTree&, ValueTree&, int) override { itemChanged(); }
|
||||
void valueTreeChildOrderChanged (ValueTree&, int, int) override { itemChanged(); }
|
||||
void valueTreeParentChanged (ValueTree&) override { itemChanged(); }
|
||||
|
||||
private:
|
||||
Project::Item item;
|
||||
ListBox list;
|
||||
ContentViewHeader header;
|
||||
|
||||
void itemChanged()
|
||||
{
|
||||
list.updateContent();
|
||||
repaint();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
class FileOptionComponent : public Component
|
||||
{
|
||||
public:
|
||||
FileOptionComponent (const Project::Item& fileItem, ListBoxHeader* listBoxHeader)
|
||||
: item (fileItem),
|
||||
header (listBoxHeader),
|
||||
compilerFlagSchemeSelector (item)
|
||||
{
|
||||
if (item.isFile())
|
||||
{
|
||||
auto isSourceFile = item.isSourceFile();
|
||||
|
||||
if (isSourceFile)
|
||||
{
|
||||
addAndMakeVisible (compileButton);
|
||||
compileButton.getToggleStateValue().referTo (item.getShouldCompileValue());
|
||||
compileButton.onStateChange = [this] { compileEnablementChanged(); };
|
||||
}
|
||||
|
||||
addAndMakeVisible (binaryResourceButton);
|
||||
binaryResourceButton.getToggleStateValue().referTo (item.getShouldAddToBinaryResourcesValue());
|
||||
|
||||
addAndMakeVisible (xcodeResourceButton);
|
||||
xcodeResourceButton.getToggleStateValue().referTo (item.getShouldAddToXcodeResourcesValue());
|
||||
|
||||
if (isSourceFile)
|
||||
{
|
||||
addChildComponent (skipPCHButton);
|
||||
skipPCHButton.getToggleStateValue().referTo (item.getShouldSkipPCHValue());
|
||||
|
||||
addChildComponent (compilerFlagSchemeSelector);
|
||||
|
||||
compileEnablementChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void paint (Graphics& g) override
|
||||
{
|
||||
if (header != nullptr)
|
||||
{
|
||||
auto textBounds = getLocalBounds().removeFromLeft (roundToInt (header->getProportionAtIndex (0) * (float) getWidth()));
|
||||
|
||||
auto iconBounds = textBounds.removeFromLeft (25);
|
||||
|
||||
if (item.isImageFile())
|
||||
iconBounds.reduce (5, 5);
|
||||
|
||||
item.getIcon().withColour (findColour (treeIconColourId)).draw (g, iconBounds.toFloat(), item.isIconCrossedOut());
|
||||
|
||||
g.setColour (findColour (widgetTextColourId));
|
||||
|
||||
g.drawText (item.getName(), textBounds, Justification::centredLeft);
|
||||
}
|
||||
}
|
||||
|
||||
void resized() override
|
||||
{
|
||||
if (header != nullptr)
|
||||
{
|
||||
auto bounds = getLocalBounds();
|
||||
auto width = (float) getWidth();
|
||||
|
||||
bounds.removeFromLeft (roundToInt (header->getProportionAtIndex (0) * width));
|
||||
|
||||
binaryResourceButton.setBounds (bounds.removeFromLeft (roundToInt (header->getProportionAtIndex (1) * width)));
|
||||
xcodeResourceButton.setBounds (bounds.removeFromLeft (roundToInt (header->getProportionAtIndex (2) * width)));
|
||||
compileButton.setBounds (bounds.removeFromLeft (roundToInt (header->getProportionAtIndex (3) * width)));
|
||||
skipPCHButton.setBounds (bounds.removeFromLeft (roundToInt (header->getProportionAtIndex (4) * width)));
|
||||
compilerFlagSchemeSelector.setBounds (bounds.removeFromLeft (roundToInt (header->getProportionAtIndex (5) * width)));
|
||||
}
|
||||
}
|
||||
|
||||
Project::Item item;
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
class CompilerFlagSchemeSelector : public Component,
|
||||
private Value::Listener
|
||||
{
|
||||
public:
|
||||
CompilerFlagSchemeSelector (Project::Item& it)
|
||||
: item (it)
|
||||
{
|
||||
schemeBox.setTextWhenNothingSelected ("None");
|
||||
updateCompilerFlagSchemeComboBox();
|
||||
schemeBox.onChange = [this] { handleComboBoxSelection(); };
|
||||
|
||||
addAndMakeVisible (schemeBox);
|
||||
addChildComponent (newSchemeLabel);
|
||||
|
||||
newSchemeLabel.setEditable (true);
|
||||
newSchemeLabel.setJustificationType (Justification::centredLeft);
|
||||
newSchemeLabel.onEditorHide = [this]
|
||||
{
|
||||
newSchemeLabel.setVisible (false);
|
||||
schemeBox.setVisible (true);
|
||||
|
||||
auto newScheme = newSchemeLabel.getText();
|
||||
|
||||
item.project.addCompilerFlagScheme (newScheme);
|
||||
|
||||
if (item.getCompilerFlagSchemeString().isEmpty())
|
||||
item.setCompilerFlagScheme (newScheme);
|
||||
|
||||
updateCompilerFlagSchemeComboBox();
|
||||
};
|
||||
|
||||
selectScheme (item.getCompilerFlagSchemeString());
|
||||
|
||||
projectCompilerFlagSchemesValue = item.project.getProjectValue (Ids::compilerFlagSchemes);
|
||||
projectCompilerFlagSchemesValue.addListener (this);
|
||||
|
||||
lookAndFeelChanged();
|
||||
}
|
||||
|
||||
void resized() override
|
||||
{
|
||||
auto b = getLocalBounds();
|
||||
|
||||
schemeBox.setBounds (b);
|
||||
newSchemeLabel.setBounds (b);
|
||||
}
|
||||
|
||||
private:
|
||||
void valueChanged (Value&) override { updateCompilerFlagSchemeComboBox(); }
|
||||
|
||||
void lookAndFeelChanged() override
|
||||
{
|
||||
schemeBox.setColour (ComboBox::outlineColourId, Colours::transparentBlack);
|
||||
schemeBox.setColour (ComboBox::textColourId, findColour (defaultTextColourId));
|
||||
}
|
||||
|
||||
void updateCompilerFlagSchemeComboBox()
|
||||
{
|
||||
auto itemScheme = item.getCompilerFlagSchemeString();
|
||||
auto allSchemes = item.project.getCompilerFlagSchemes();
|
||||
|
||||
if (itemScheme.isNotEmpty() && ! allSchemes.contains (itemScheme))
|
||||
{
|
||||
item.clearCurrentCompilerFlagScheme();
|
||||
itemScheme = {};
|
||||
}
|
||||
|
||||
schemeBox.clear();
|
||||
|
||||
schemeBox.addItemList (allSchemes, 1);
|
||||
schemeBox.addSeparator();
|
||||
schemeBox.addItem ("Add a new scheme...", -1);
|
||||
schemeBox.addItem ("Delete selected scheme", -2);
|
||||
schemeBox.addItem ("Clear", -3);
|
||||
|
||||
selectScheme (itemScheme);
|
||||
}
|
||||
|
||||
void handleComboBoxSelection()
|
||||
{
|
||||
auto selectedID = schemeBox.getSelectedId();
|
||||
|
||||
if (selectedID > 0)
|
||||
{
|
||||
item.setCompilerFlagScheme (schemeBox.getItemText (selectedID - 1));
|
||||
}
|
||||
else if (selectedID == -1)
|
||||
{
|
||||
newSchemeLabel.setText ("NewScheme", dontSendNotification);
|
||||
|
||||
schemeBox.setVisible (false);
|
||||
newSchemeLabel.setVisible (true);
|
||||
|
||||
newSchemeLabel.showEditor();
|
||||
|
||||
if (auto* ed = newSchemeLabel.getCurrentTextEditor())
|
||||
ed->setInputRestrictions (64, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890_");
|
||||
}
|
||||
else if (selectedID == -2)
|
||||
{
|
||||
auto currentScheme = item.getCompilerFlagSchemeString();
|
||||
|
||||
if (currentScheme.isNotEmpty())
|
||||
{
|
||||
item.project.removeCompilerFlagScheme (currentScheme);
|
||||
item.clearCurrentCompilerFlagScheme();
|
||||
}
|
||||
|
||||
updateCompilerFlagSchemeComboBox();
|
||||
}
|
||||
else if (selectedID == -3)
|
||||
{
|
||||
schemeBox.setSelectedId (0);
|
||||
item.clearCurrentCompilerFlagScheme();
|
||||
}
|
||||
}
|
||||
|
||||
void selectScheme (const String& schemeToSelect)
|
||||
{
|
||||
if (schemeToSelect.isNotEmpty())
|
||||
{
|
||||
for (int i = 0; i < schemeBox.getNumItems(); ++i)
|
||||
{
|
||||
if (schemeBox.getItemText (i) == schemeToSelect)
|
||||
{
|
||||
schemeBox.setSelectedItemIndex (i);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
schemeBox.setSelectedId (0);
|
||||
}
|
||||
|
||||
Project::Item& item;
|
||||
Value projectCompilerFlagSchemesValue;
|
||||
|
||||
ComboBox schemeBox;
|
||||
Label newSchemeLabel;
|
||||
};
|
||||
|
||||
void compileEnablementChanged()
|
||||
{
|
||||
auto shouldBeCompiled = compileButton.getToggleState();
|
||||
|
||||
skipPCHButton.setVisible (shouldBeCompiled);
|
||||
compilerFlagSchemeSelector.setVisible (shouldBeCompiled);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
ListBoxHeader* header;
|
||||
|
||||
ToggleButton compileButton, binaryResourceButton, xcodeResourceButton, skipPCHButton;
|
||||
CompilerFlagSchemeSelector compilerFlagSchemeSelector;
|
||||
};
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FileGroupInformationComponent)
|
||||
};
|
279
deps/juce/extras/Projucer/Source/Project/UI/jucer_HeaderComponent.cpp
vendored
Normal file
279
deps/juce/extras/Projucer/Source/Project/UI/jucer_HeaderComponent.cpp
vendored
Normal file
@ -0,0 +1,279 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#include "jucer_HeaderComponent.h"
|
||||
|
||||
#include "../../Application/jucer_Application.h"
|
||||
|
||||
#include "../../ProjectSaving/jucer_ProjectExporter.h"
|
||||
#include "../../Project/UI/jucer_ProjectContentComponent.h"
|
||||
|
||||
//==============================================================================
|
||||
HeaderComponent::HeaderComponent (ProjectContentComponent* pcc)
|
||||
: projectContentComponent (pcc)
|
||||
{
|
||||
setTitle ("Header");
|
||||
setFocusContainerType (FocusContainerType::focusContainer);
|
||||
|
||||
addAndMakeVisible (configLabel);
|
||||
addAndMakeVisible (exporterBox);
|
||||
|
||||
exporterBox.onChange = [this] { updateExporterButton(); };
|
||||
|
||||
juceIcon.setImage (ImageCache::getFromMemory (BinaryData::juce_icon_png, BinaryData::juce_icon_pngSize), RectanglePlacement::centred);
|
||||
addAndMakeVisible (juceIcon);
|
||||
|
||||
addAndMakeVisible (userAvatar);
|
||||
userAvatar.addChangeListener (this);
|
||||
|
||||
projectNameLabel.setText ({}, dontSendNotification);
|
||||
addAndMakeVisible (projectNameLabel);
|
||||
|
||||
initialiseButtons();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void HeaderComponent::resized()
|
||||
{
|
||||
auto bounds = getLocalBounds();
|
||||
configLabel.setFont ({ (float) bounds.getHeight() / 3.0f });
|
||||
|
||||
{
|
||||
auto headerBounds = bounds.removeFromLeft (tabsWidth);
|
||||
|
||||
const int buttonSize = 25;
|
||||
auto buttonBounds = headerBounds.removeFromRight (buttonSize);
|
||||
|
||||
projectSettingsButton.setBounds (buttonBounds.removeFromBottom (buttonSize).reduced (2));
|
||||
|
||||
juceIcon.setBounds (headerBounds.removeFromLeft (headerBounds.getHeight()).reduced (2));
|
||||
|
||||
headerBounds.removeFromRight (5);
|
||||
projectNameLabel.setBounds (headerBounds);
|
||||
}
|
||||
|
||||
{
|
||||
auto exporterWidth = jmin (400, bounds.getWidth() / 2);
|
||||
Rectangle<int> exporterBounds (0, 0, exporterWidth, bounds.getHeight());
|
||||
|
||||
exporterBounds.setCentre (bounds.getCentre());
|
||||
|
||||
saveAndOpenInIDEButton.setBounds (exporterBounds.removeFromRight (exporterBounds.getHeight()).reduced (2));
|
||||
|
||||
exporterBounds.removeFromRight (5);
|
||||
exporterBox.setBounds (exporterBounds.removeFromBottom (roundToInt ((float) exporterBounds.getHeight() / 1.8f)));
|
||||
configLabel.setBounds (exporterBounds);
|
||||
}
|
||||
|
||||
userAvatar.setBounds (bounds.removeFromRight (userAvatar.isDisplaingGPLLogo() ? roundToInt ((float) bounds.getHeight() * 1.9f)
|
||||
: bounds.getHeight()).reduced (2));
|
||||
}
|
||||
|
||||
void HeaderComponent::paint (Graphics& g)
|
||||
{
|
||||
g.fillAll (findColour (backgroundColourId));
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void HeaderComponent::setCurrentProject (Project* newProject)
|
||||
{
|
||||
stopTimer();
|
||||
repaint();
|
||||
|
||||
projectNameLabel.setText ({}, dontSendNotification);
|
||||
|
||||
project = newProject;
|
||||
|
||||
if (project != nullptr)
|
||||
{
|
||||
exportersTree = project->getExporters();
|
||||
exportersTree.addListener (this);
|
||||
updateExporters();
|
||||
|
||||
projectNameValue.referTo (project->getProjectValue (Ids::name));
|
||||
projectNameValue.addListener (this);
|
||||
updateName();
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void HeaderComponent::updateExporters()
|
||||
{
|
||||
auto selectedExporter = getSelectedExporter();
|
||||
|
||||
exporterBox.clear();
|
||||
auto preferredExporterIndex = -1;
|
||||
|
||||
int i = 0;
|
||||
for (Project::ExporterIterator exporter (*project); exporter.next(); ++i)
|
||||
{
|
||||
auto exporterName = exporter->getUniqueName();
|
||||
|
||||
exporterBox.addItem (exporterName, i + 1);
|
||||
|
||||
if (selectedExporter != nullptr && exporterName == selectedExporter->getUniqueName())
|
||||
exporterBox.setSelectedId (i + 1);
|
||||
|
||||
if (exporterName.contains (ProjectExporter::getCurrentPlatformExporterTypeInfo().displayName) && preferredExporterIndex == -1)
|
||||
preferredExporterIndex = i;
|
||||
}
|
||||
|
||||
if (exporterBox.getSelectedItemIndex() == -1)
|
||||
{
|
||||
if (preferredExporterIndex == -1)
|
||||
{
|
||||
i = 0;
|
||||
for (Project::ExporterIterator exporter (*project); exporter.next(); ++i)
|
||||
{
|
||||
if (exporter->canLaunchProject())
|
||||
{
|
||||
preferredExporterIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
exporterBox.setSelectedItemIndex (preferredExporterIndex != -1 ? preferredExporterIndex : 0);
|
||||
}
|
||||
|
||||
updateExporterButton();
|
||||
}
|
||||
|
||||
std::unique_ptr<ProjectExporter> HeaderComponent::getSelectedExporter() const
|
||||
{
|
||||
if (project != nullptr)
|
||||
{
|
||||
int i = 0;
|
||||
auto selectedIndex = exporterBox.getSelectedItemIndex();
|
||||
|
||||
for (Project::ExporterIterator exporter (*project); exporter.next();)
|
||||
if (i++ == selectedIndex)
|
||||
return std::move (exporter.exporter);
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool HeaderComponent::canCurrentExporterLaunchProject() const
|
||||
{
|
||||
if (project != nullptr)
|
||||
{
|
||||
if (auto selectedExporter = getSelectedExporter())
|
||||
{
|
||||
for (Project::ExporterIterator exporter (*project); exporter.next();)
|
||||
if (exporter->canLaunchProject() && exporter->getUniqueName() == selectedExporter->getUniqueName())
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void HeaderComponent::sidebarTabsWidthChanged (int newWidth)
|
||||
{
|
||||
tabsWidth = newWidth;
|
||||
resized();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void HeaderComponent::changeListenerCallback (ChangeBroadcaster* source)
|
||||
{
|
||||
if (source == &userAvatar)
|
||||
resized();
|
||||
}
|
||||
|
||||
void HeaderComponent::valueChanged (Value&)
|
||||
{
|
||||
updateName();
|
||||
}
|
||||
|
||||
void HeaderComponent::timerCallback()
|
||||
{
|
||||
repaint();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void HeaderComponent::initialiseButtons()
|
||||
{
|
||||
addAndMakeVisible (projectSettingsButton);
|
||||
projectSettingsButton.onClick = [this] { projectContentComponent->showProjectSettings(); };
|
||||
|
||||
addAndMakeVisible (saveAndOpenInIDEButton);
|
||||
saveAndOpenInIDEButton.setBackgroundColour (Colours::white);
|
||||
saveAndOpenInIDEButton.setIconInset (7);
|
||||
saveAndOpenInIDEButton.onClick = [this]
|
||||
{
|
||||
if (project == nullptr)
|
||||
return;
|
||||
|
||||
if (! project->isSaveAndExportDisabled())
|
||||
{
|
||||
projectContentComponent->openInSelectedIDE (true);
|
||||
return;
|
||||
}
|
||||
|
||||
auto setWarningVisible = [this] (const Identifier& identifier)
|
||||
{
|
||||
auto child = project->getProjectMessages().getChildWithName (ProjectMessages::Ids::warning)
|
||||
.getChildWithName (identifier);
|
||||
|
||||
if (child.isValid())
|
||||
child.setProperty (ProjectMessages::Ids::isVisible, true, nullptr);
|
||||
};
|
||||
|
||||
if (project->hasIncompatibleLicenseTypeAndSplashScreenSetting())
|
||||
setWarningVisible (ProjectMessages::Ids::incompatibleLicense);
|
||||
|
||||
if (project->isFileModificationCheckPending())
|
||||
setWarningVisible (ProjectMessages::Ids::jucerFileModified);
|
||||
};
|
||||
|
||||
updateExporterButton();
|
||||
}
|
||||
|
||||
void HeaderComponent::updateName()
|
||||
{
|
||||
if (project != nullptr)
|
||||
projectNameLabel.setText (project->getDocumentTitle(), dontSendNotification);
|
||||
}
|
||||
|
||||
void HeaderComponent::updateExporterButton()
|
||||
{
|
||||
if (auto selectedExporter = getSelectedExporter())
|
||||
{
|
||||
auto selectedName = selectedExporter->getUniqueName();
|
||||
|
||||
for (auto info : ProjectExporter::getExporterTypeInfos())
|
||||
{
|
||||
if (selectedName.contains (info.displayName))
|
||||
{
|
||||
saveAndOpenInIDEButton.setImage (info.icon);
|
||||
saveAndOpenInIDEButton.repaint();
|
||||
saveAndOpenInIDEButton.setEnabled (canCurrentExporterLaunchProject());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
101
deps/juce/extras/Projucer/Source/Project/UI/jucer_HeaderComponent.h
vendored
Normal file
101
deps/juce/extras/Projucer/Source/Project/UI/jucer_HeaderComponent.h
vendored
Normal file
@ -0,0 +1,101 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../../Application/jucer_Headers.h"
|
||||
#include "../../Utility/UI/jucer_IconButton.h"
|
||||
#include "jucer_UserAvatarComponent.h"
|
||||
|
||||
class Project;
|
||||
class ProjectContentComponent;
|
||||
class ProjectExporter;
|
||||
|
||||
//==============================================================================
|
||||
class HeaderComponent : public Component,
|
||||
private ValueTree::Listener,
|
||||
private ChangeListener,
|
||||
private Value::Listener,
|
||||
private Timer
|
||||
{
|
||||
public:
|
||||
HeaderComponent (ProjectContentComponent* projectContentComponent);
|
||||
|
||||
//==============================================================================
|
||||
void resized() override;
|
||||
void paint (Graphics&) override;
|
||||
|
||||
//==============================================================================
|
||||
void setCurrentProject (Project*);
|
||||
|
||||
void updateExporters();
|
||||
std::unique_ptr<ProjectExporter> getSelectedExporter() const;
|
||||
bool canCurrentExporterLaunchProject() const;
|
||||
|
||||
void sidebarTabsWidthChanged (int newWidth);
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
void changeListenerCallback (ChangeBroadcaster* source) override;
|
||||
void valueChanged (Value&) override;
|
||||
void timerCallback() override;
|
||||
|
||||
//==============================================================================
|
||||
void valueTreeChildAdded (ValueTree& parentTree, ValueTree&) override { updateIfNeeded (parentTree); }
|
||||
void valueTreeChildRemoved (ValueTree& parentTree, ValueTree&, int) override { updateIfNeeded (parentTree); }
|
||||
void valueTreeChildOrderChanged (ValueTree& parentTree, int, int) override { updateIfNeeded (parentTree); }
|
||||
|
||||
void updateIfNeeded (ValueTree tree)
|
||||
{
|
||||
if (tree == exportersTree)
|
||||
updateExporters();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void initialiseButtons();
|
||||
|
||||
void updateName();
|
||||
void updateExporterButton();
|
||||
|
||||
//==============================================================================
|
||||
int tabsWidth = 200;
|
||||
|
||||
ProjectContentComponent* projectContentComponent = nullptr;
|
||||
Project* project = nullptr;
|
||||
ValueTree exportersTree;
|
||||
|
||||
Value projectNameValue;
|
||||
|
||||
ComboBox exporterBox;
|
||||
Label configLabel { "Config Label", "Selected exporter" }, projectNameLabel;
|
||||
|
||||
ImageComponent juceIcon;
|
||||
UserAvatarComponent userAvatar { true };
|
||||
|
||||
IconButton projectSettingsButton { "Project Settings", getIcons().settings },
|
||||
saveAndOpenInIDEButton { "Save and Open in IDE", Image() };
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (HeaderComponent)
|
||||
};
|
341
deps/juce/extras/Projucer/Source/Project/UI/jucer_ModulesInformationComponent.h
vendored
Normal file
341
deps/juce/extras/Projucer/Source/Project/UI/jucer_ModulesInformationComponent.h
vendored
Normal file
@ -0,0 +1,341 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
|
||||
//==============================================================================
|
||||
class ModulesInformationComponent : public Component,
|
||||
private ListBoxModel,
|
||||
private ValueTree::Listener
|
||||
{
|
||||
public:
|
||||
ModulesInformationComponent (Project& p)
|
||||
: project (p),
|
||||
modulesValueTree (project.getEnabledModules().getState())
|
||||
{
|
||||
auto tempHeader = std::make_unique<ListBoxHeader> (Array<String> { "Module", "Version", "Make Local Copy", "Paths" },
|
||||
Array<float> { 0.25f, 0.2f, 0.2f, 0.35f });
|
||||
listHeader = tempHeader.get();
|
||||
list.setHeaderComponent (std::move (tempHeader));
|
||||
list.setModel (this);
|
||||
list.setColour (ListBox::backgroundColourId, Colours::transparentBlack);
|
||||
addAndMakeVisible (list);
|
||||
list.updateContent();
|
||||
list.setRowHeight (30);
|
||||
list.setMultipleSelectionEnabled (true);
|
||||
|
||||
addAndMakeVisible (header);
|
||||
|
||||
addAndMakeVisible (setCopyModeButton);
|
||||
setCopyModeButton.setTriggeredOnMouseDown (true);
|
||||
setCopyModeButton.onClick = [this] { showCopyModeMenu(); };
|
||||
|
||||
addAndMakeVisible (copyPathButton);
|
||||
copyPathButton.setTriggeredOnMouseDown (true);
|
||||
copyPathButton.onClick = [this] { showSetPathsMenu(); };
|
||||
|
||||
addAndMakeVisible (globalPathsButton);
|
||||
globalPathsButton.onClick = [this] { showGlobalPathsMenu(); };
|
||||
|
||||
modulesValueTree.addListener (this);
|
||||
lookAndFeelChanged();
|
||||
}
|
||||
|
||||
void paint (Graphics& g) override
|
||||
{
|
||||
g.setColour (findColour (secondaryBackgroundColourId));
|
||||
g.fillRect (getLocalBounds().reduced (12, 0));
|
||||
}
|
||||
|
||||
void resized() override
|
||||
{
|
||||
auto bounds = getLocalBounds().reduced (12, 0);
|
||||
|
||||
header.setBounds (bounds.removeFromTop (40));
|
||||
|
||||
bounds.reduce (10, 0);
|
||||
list.setBounds (bounds.removeFromTop (list.getRowPosition (getNumRows() - 1, true).getBottom() + 20));
|
||||
|
||||
if (bounds.getHeight() < 35)
|
||||
{
|
||||
parentSizeChanged();
|
||||
}
|
||||
else
|
||||
{
|
||||
auto buttonRow = bounds.removeFromTop (35);
|
||||
setCopyModeButton.setBounds (buttonRow.removeFromLeft (jmin (200, bounds.getWidth() / 3)));
|
||||
buttonRow.removeFromLeft (8);
|
||||
copyPathButton.setBounds (buttonRow.removeFromLeft (jmin (200, bounds.getWidth() / 3)));
|
||||
buttonRow.removeFromLeft (8);
|
||||
globalPathsButton.setBounds (buttonRow.removeFromLeft (jmin (200, bounds.getWidth() / 3)));
|
||||
}
|
||||
}
|
||||
|
||||
void parentSizeChanged() override
|
||||
{
|
||||
auto width = jmax (550, getParentWidth());
|
||||
auto y = list.getRowPosition (getNumRows() - 1, true).getBottom() + 200;
|
||||
|
||||
y = jmax (getParentHeight(), y);
|
||||
|
||||
setSize (width, y);
|
||||
}
|
||||
|
||||
int getNumRows() override
|
||||
{
|
||||
return project.getEnabledModules().getNumModules();
|
||||
}
|
||||
|
||||
void paintListBoxItem (int rowNumber, Graphics& g, int width, int height, bool rowIsSelected) override
|
||||
{
|
||||
ignoreUnused (height);
|
||||
|
||||
Rectangle<int> bounds (0, 0, width, height);
|
||||
|
||||
g.setColour (rowIsSelected ? findColour (defaultHighlightColourId) : findColour (rowNumber % 2 == 0 ? widgetBackgroundColourId
|
||||
: secondaryWidgetBackgroundColourId));
|
||||
g.fillRect (bounds.withTrimmedBottom (1));
|
||||
|
||||
bounds.removeFromLeft (5);
|
||||
g.setColour (rowIsSelected ? findColour (defaultHighlightedTextColourId) : findColour (widgetTextColourId));
|
||||
|
||||
//==============================================================================
|
||||
auto moduleID = project.getEnabledModules().getModuleID (rowNumber);
|
||||
|
||||
g.drawFittedText (moduleID, bounds.removeFromLeft (roundToInt (listHeader->getProportionAtIndex (0) * (float) width)), Justification::centredLeft, 1);
|
||||
|
||||
//==============================================================================
|
||||
auto version = project.getEnabledModules().getModuleInfo (moduleID).getVersion();
|
||||
if (version.isEmpty())
|
||||
version = "?";
|
||||
|
||||
g.drawFittedText (version, bounds.removeFromLeft (roundToInt (listHeader->getProportionAtIndex (1) * (float) width)), Justification::centredLeft, 1);
|
||||
|
||||
//==============================================================================
|
||||
g.drawFittedText (String (project.getEnabledModules().shouldCopyModuleFilesLocally (moduleID) ? "Yes" : "No"),
|
||||
bounds.removeFromLeft (roundToInt (listHeader->getProportionAtIndex (2) * (float) width)), Justification::centredLeft, 1);
|
||||
|
||||
//==============================================================================
|
||||
String pathText;
|
||||
|
||||
if (project.getEnabledModules().shouldUseGlobalPath (moduleID))
|
||||
{
|
||||
pathText = "Global";
|
||||
}
|
||||
else
|
||||
{
|
||||
StringArray paths;
|
||||
|
||||
for (Project::ExporterIterator exporter (project); exporter.next();)
|
||||
paths.addIfNotAlreadyThere (exporter->getPathForModuleString (moduleID).trim());
|
||||
|
||||
pathText = paths.joinIntoString (", ");
|
||||
}
|
||||
|
||||
g.drawFittedText (pathText, bounds.removeFromLeft (roundToInt (listHeader->getProportionAtIndex (3) * (float) width)), Justification::centredLeft, 1);
|
||||
}
|
||||
|
||||
void listBoxItemDoubleClicked (int row, const MouseEvent&) override
|
||||
{
|
||||
auto moduleID = project.getEnabledModules().getModuleID (row);
|
||||
|
||||
if (moduleID.isNotEmpty())
|
||||
if (auto* pcc = findParentComponentOfClass<ProjectContentComponent>())
|
||||
pcc->showModule (moduleID);
|
||||
}
|
||||
|
||||
void deleteKeyPressed (int row) override
|
||||
{
|
||||
project.getEnabledModules().removeModule (project.getEnabledModules().getModuleID (row));
|
||||
}
|
||||
|
||||
void lookAndFeelChanged() override
|
||||
{
|
||||
setCopyModeButton.setColour (TextButton::buttonColourId, findColour (secondaryButtonBackgroundColourId));
|
||||
copyPathButton.setColour (TextButton::buttonColourId, findColour (defaultButtonBackgroundColourId));
|
||||
globalPathsButton.setColour (TextButton::buttonColourId, findColour (defaultButtonBackgroundColourId));
|
||||
}
|
||||
|
||||
private:
|
||||
enum
|
||||
{
|
||||
nameCol = 1,
|
||||
versionCol,
|
||||
copyCol,
|
||||
pathCol
|
||||
};
|
||||
|
||||
Project& project;
|
||||
ValueTree modulesValueTree;
|
||||
|
||||
ContentViewHeader header { "Modules", { getIcons().modules, Colours::transparentBlack } };
|
||||
ListBox list;
|
||||
ListBoxHeader* listHeader;
|
||||
|
||||
TextButton setCopyModeButton { "Set copy-mode for all modules..." };
|
||||
TextButton copyPathButton { "Set paths for all modules..." };
|
||||
TextButton globalPathsButton { "Enable/disable global path for modules..." };
|
||||
|
||||
std::map<String, var> modulePathClipboard;
|
||||
|
||||
void valueTreePropertyChanged (ValueTree&, const Identifier&) override { itemChanged(); }
|
||||
void valueTreeChildAdded (ValueTree&, ValueTree&) override { itemChanged(); }
|
||||
void valueTreeChildRemoved (ValueTree&, ValueTree&, int) override { itemChanged(); }
|
||||
void valueTreeChildOrderChanged (ValueTree&, int, int) override { itemChanged(); }
|
||||
void valueTreeParentChanged (ValueTree&) override { itemChanged(); }
|
||||
|
||||
void itemChanged()
|
||||
{
|
||||
list.updateContent();
|
||||
resized();
|
||||
repaint();
|
||||
}
|
||||
|
||||
static void setLocalCopyModeForAllModules (Project& project, bool copyLocally)
|
||||
{
|
||||
auto& modules = project.getEnabledModules();
|
||||
|
||||
for (auto i = modules.getNumModules(); --i >= 0;)
|
||||
modules.shouldCopyModuleFilesLocallyValue (modules.getModuleID (i)) = copyLocally;
|
||||
}
|
||||
|
||||
void showCopyModeMenu()
|
||||
{
|
||||
PopupMenu m;
|
||||
|
||||
m.addItem (PopupMenu::Item ("Set all modules to copy locally")
|
||||
.setAction ([&] { setLocalCopyModeForAllModules (project, true); }));
|
||||
|
||||
m.addItem (PopupMenu::Item ("Set all modules to not copy locally")
|
||||
.setAction ([&] { setLocalCopyModeForAllModules (project, false); }));
|
||||
|
||||
m.showMenuAsync (PopupMenu::Options().withTargetComponent (setCopyModeButton));
|
||||
}
|
||||
|
||||
static void setAllModulesToUseGlobalPaths (Project& project, bool useGlobal)
|
||||
{
|
||||
auto& modules = project.getEnabledModules();
|
||||
|
||||
for (auto moduleID : modules.getAllModules())
|
||||
modules.shouldUseGlobalPathValue (moduleID) = useGlobal;
|
||||
}
|
||||
|
||||
static void setSelectedModulesToUseGlobalPaths (Project& project, SparseSet<int> selected, bool useGlobal)
|
||||
{
|
||||
auto& modules = project.getEnabledModules();
|
||||
|
||||
for (int i = 0; i < selected.size(); ++i)
|
||||
modules.shouldUseGlobalPathValue (modules.getModuleID (selected[i])) = useGlobal;
|
||||
}
|
||||
|
||||
void showGlobalPathsMenu()
|
||||
{
|
||||
PopupMenu m;
|
||||
|
||||
m.addItem (PopupMenu::Item ("Set all modules to use global paths")
|
||||
.setAction ([&] { setAllModulesToUseGlobalPaths (project, true); }));
|
||||
|
||||
m.addItem (PopupMenu::Item ("Set all modules to not use global paths")
|
||||
.setAction ([&] { setAllModulesToUseGlobalPaths (project, false); }));
|
||||
|
||||
m.addItem (PopupMenu::Item ("Set selected modules to use global paths")
|
||||
.setEnabled (list.getNumSelectedRows() > 0)
|
||||
.setAction ([&] { setSelectedModulesToUseGlobalPaths (project, list.getSelectedRows(), true); }));
|
||||
|
||||
m.addItem (PopupMenu::Item ("Set selected modules to not use global paths")
|
||||
.setEnabled (list.getNumSelectedRows() > 0)
|
||||
.setAction ([&] { setSelectedModulesToUseGlobalPaths (project, list.getSelectedRows(), false); }));
|
||||
|
||||
m.showMenuAsync (PopupMenu::Options().withTargetComponent (globalPathsButton));
|
||||
}
|
||||
|
||||
void showSetPathsMenu()
|
||||
{
|
||||
PopupMenu m;
|
||||
auto moduleToCopy = project.getEnabledModules().getModuleID (list.getSelectedRow());
|
||||
|
||||
if (moduleToCopy.isNotEmpty())
|
||||
{
|
||||
m.addItem (PopupMenu::Item ("Copy the paths from the module '" + moduleToCopy + "' to all other modules")
|
||||
.setAction ([this, moduleToCopy]
|
||||
{
|
||||
auto& modulesList = project.getEnabledModules();
|
||||
|
||||
for (Project::ExporterIterator exporter (project); exporter.next();)
|
||||
{
|
||||
for (int i = 0; i < modulesList.getNumModules(); ++i)
|
||||
{
|
||||
auto modID = modulesList.getModuleID (i);
|
||||
|
||||
if (modID != moduleToCopy)
|
||||
exporter->getPathForModuleValue (modID) = exporter->getPathForModuleValue (moduleToCopy).get();
|
||||
}
|
||||
}
|
||||
|
||||
list.repaint();
|
||||
}));
|
||||
|
||||
m.addItem (PopupMenu::Item ("Copy paths from selected module")
|
||||
.setEnabled (list.getNumSelectedRows() == 1)
|
||||
.setAction ([this, moduleToCopy]
|
||||
{
|
||||
modulePathClipboard.clear();
|
||||
|
||||
for (Project::ExporterIterator exporter (project); exporter.next();)
|
||||
modulePathClipboard[exporter->getUniqueName()] = exporter->getPathForModuleValue (moduleToCopy).get();
|
||||
|
||||
list.repaint();
|
||||
}));
|
||||
|
||||
m.addItem (PopupMenu::Item ("Paste paths to selected modules")
|
||||
.setEnabled (! modulePathClipboard.empty())
|
||||
.setAction ([this]
|
||||
{
|
||||
for (int selectionId = 0; selectionId < list.getNumSelectedRows(); ++selectionId)
|
||||
{
|
||||
auto rowNumber = list.getSelectedRow (selectionId);
|
||||
auto modID = project.getEnabledModules().getModuleID (rowNumber);
|
||||
|
||||
for (Project::ExporterIterator exporter (project); exporter.next();)
|
||||
exporter->getPathForModuleValue (modID) = modulePathClipboard[exporter->getUniqueName()];
|
||||
}
|
||||
|
||||
list.repaint();
|
||||
}));
|
||||
}
|
||||
else
|
||||
{
|
||||
m.addItem (PopupMenu::Item ("(Select a module in the list above to use this option)")
|
||||
.setEnabled (false));
|
||||
}
|
||||
|
||||
m.showMenuAsync (PopupMenu::Options()
|
||||
.withDeletionCheck (*this)
|
||||
.withTargetComponent (copyPathButton));
|
||||
}
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ModulesInformationComponent)
|
||||
};
|
910
deps/juce/extras/Projucer/Source/Project/UI/jucer_ProjectContentComponent.cpp
vendored
Normal file
910
deps/juce/extras/Projucer/Source/Project/UI/jucer_ProjectContentComponent.cpp
vendored
Normal file
@ -0,0 +1,910 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#include "../../Application/jucer_Headers.h"
|
||||
#include "jucer_ProjectContentComponent.h"
|
||||
|
||||
#include "Sidebar/jucer_Sidebar.h"
|
||||
|
||||
struct WizardHolder
|
||||
{
|
||||
std::unique_ptr<NewFileWizard::Type> wizard;
|
||||
};
|
||||
|
||||
NewFileWizard::Type* createGUIComponentWizard (Project&);
|
||||
|
||||
//==============================================================================
|
||||
ProjectContentComponent::ProjectContentComponent()
|
||||
: sidebar (std::make_unique<Sidebar> (project))
|
||||
{
|
||||
setOpaque (true);
|
||||
setWantsKeyboardFocus (true);
|
||||
|
||||
addAndMakeVisible (headerComponent);
|
||||
addAndMakeVisible (projectMessagesComponent);
|
||||
addAndMakeVisible (contentViewComponent);
|
||||
|
||||
sidebarSizeConstrainer.setMinimumWidth (200);
|
||||
sidebarSizeConstrainer.setMaximumWidth (500);
|
||||
|
||||
ProjucerApplication::getApp().openDocumentManager.addListener (this);
|
||||
|
||||
getGlobalProperties().addChangeListener (this);
|
||||
}
|
||||
|
||||
ProjectContentComponent::~ProjectContentComponent()
|
||||
{
|
||||
getGlobalProperties().removeChangeListener (this);
|
||||
ProjucerApplication::getApp().openDocumentManager.removeListener (this);
|
||||
|
||||
setProject (nullptr);
|
||||
removeChildComponent (&bubbleMessage);
|
||||
}
|
||||
|
||||
void ProjectContentComponent::paint (Graphics& g)
|
||||
{
|
||||
g.fillAll (findColour (backgroundColourId));
|
||||
}
|
||||
|
||||
void ProjectContentComponent::resized()
|
||||
{
|
||||
auto r = getLocalBounds();
|
||||
|
||||
r.removeFromRight (10);
|
||||
r.removeFromLeft (15);
|
||||
r.removeFromTop (5);
|
||||
|
||||
projectMessagesComponent.setBounds (r.removeFromBottom (40).withWidth (100).reduced (0, 5));
|
||||
headerComponent.setBounds (r.removeFromTop (40));
|
||||
|
||||
r.removeFromTop (10);
|
||||
|
||||
auto sidebarArea = r.removeFromLeft (sidebar != nullptr && sidebar->getWidth() != 0 ? sidebar->getWidth()
|
||||
: r.getWidth() / 4);
|
||||
|
||||
if (sidebar != nullptr && sidebar->isVisible())
|
||||
sidebar->setBounds (sidebarArea);
|
||||
|
||||
if (resizerBar != nullptr)
|
||||
resizerBar->setBounds (r.withWidth (4));
|
||||
|
||||
contentViewComponent.setBounds (r);
|
||||
|
||||
headerComponent.sidebarTabsWidthChanged (sidebarArea.getWidth());
|
||||
}
|
||||
|
||||
void ProjectContentComponent::lookAndFeelChanged()
|
||||
{
|
||||
repaint();
|
||||
|
||||
if (translationTool != nullptr)
|
||||
translationTool->repaint();
|
||||
}
|
||||
|
||||
void ProjectContentComponent::childBoundsChanged (Component* child)
|
||||
{
|
||||
if (child == sidebar.get())
|
||||
resized();
|
||||
}
|
||||
|
||||
void ProjectContentComponent::setProject (Project* newProject)
|
||||
{
|
||||
if (project != newProject)
|
||||
{
|
||||
if (project != nullptr)
|
||||
project->removeChangeListener (this);
|
||||
|
||||
hideEditor();
|
||||
resizerBar = nullptr;
|
||||
sidebar = nullptr;
|
||||
|
||||
project = newProject;
|
||||
|
||||
if (project != nullptr)
|
||||
{
|
||||
sidebar = std::make_unique<Sidebar> (project);
|
||||
addAndMakeVisible (sidebar.get());
|
||||
|
||||
//==============================================================================
|
||||
resizerBar = std::make_unique<ResizableEdgeComponent> (sidebar.get(), &sidebarSizeConstrainer,
|
||||
ResizableEdgeComponent::rightEdge);
|
||||
addAndMakeVisible (resizerBar.get());
|
||||
resizerBar->setAlwaysOnTop (true);
|
||||
|
||||
project->addChangeListener (this);
|
||||
|
||||
updateMissingFileStatuses();
|
||||
|
||||
headerComponent.setVisible (true);
|
||||
headerComponent.setCurrentProject (project);
|
||||
|
||||
projectMessagesComponent.setVisible (true);
|
||||
}
|
||||
else
|
||||
{
|
||||
headerComponent.setVisible (false);
|
||||
projectMessagesComponent.setVisible (false);
|
||||
}
|
||||
|
||||
projectMessagesComponent.setProject (project);
|
||||
|
||||
resized();
|
||||
}
|
||||
}
|
||||
|
||||
void ProjectContentComponent::saveOpenDocumentList()
|
||||
{
|
||||
if (project != nullptr)
|
||||
{
|
||||
std::unique_ptr<XmlElement> xml (recentDocumentList.createXML());
|
||||
|
||||
if (xml != nullptr)
|
||||
project->getStoredProperties().setValue ("lastDocs", xml.get());
|
||||
}
|
||||
}
|
||||
|
||||
void ProjectContentComponent::reloadLastOpenDocuments()
|
||||
{
|
||||
if (project != nullptr)
|
||||
{
|
||||
if (auto xml = project->getStoredProperties().getXmlValue ("lastDocs"))
|
||||
{
|
||||
recentDocumentList.restoreFromXML (*project, *xml);
|
||||
showDocument (recentDocumentList.getCurrentDocument(), true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool ProjectContentComponent::documentAboutToClose (OpenDocumentManager::Document* document)
|
||||
{
|
||||
hideDocument (document);
|
||||
return true;
|
||||
}
|
||||
|
||||
void ProjectContentComponent::changeListenerCallback (ChangeBroadcaster* broadcaster)
|
||||
{
|
||||
if (broadcaster == project)
|
||||
updateMissingFileStatuses();
|
||||
}
|
||||
|
||||
void ProjectContentComponent::refreshProjectTreeFileStatuses()
|
||||
{
|
||||
if (sidebar != nullptr)
|
||||
if (auto* fileTree = sidebar->getFileTreePanel())
|
||||
fileTree->repaint();
|
||||
}
|
||||
|
||||
void ProjectContentComponent::updateMissingFileStatuses()
|
||||
{
|
||||
if (sidebar != nullptr)
|
||||
if (auto* tree = sidebar->getFileTreePanel())
|
||||
tree->updateMissingFileStatuses();
|
||||
}
|
||||
|
||||
bool ProjectContentComponent::showEditorForFile (const File& fileToShow, bool grabFocus)
|
||||
{
|
||||
if (getCurrentFile() != fileToShow)
|
||||
return showDocument (ProjucerApplication::getApp().openDocumentManager.openFile (project, fileToShow), grabFocus);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ProjectContentComponent::hasFileInRecentList (const File& f) const
|
||||
{
|
||||
return recentDocumentList.contains (f);
|
||||
}
|
||||
|
||||
File ProjectContentComponent::getCurrentFile() const
|
||||
{
|
||||
return currentDocument != nullptr ? currentDocument->getFile()
|
||||
: File();
|
||||
}
|
||||
|
||||
bool ProjectContentComponent::showDocument (OpenDocumentManager::Document* doc, bool grabFocus)
|
||||
{
|
||||
if (doc == nullptr)
|
||||
return false;
|
||||
|
||||
if (doc->hasFileBeenModifiedExternally())
|
||||
doc->reloadFromFile();
|
||||
|
||||
if (doc != getCurrentDocument())
|
||||
{
|
||||
recentDocumentList.newDocumentOpened (doc);
|
||||
setEditorDocument (doc->createEditor(), doc);
|
||||
}
|
||||
|
||||
if (grabFocus && contentViewComponent.isShowing())
|
||||
contentViewComponent.grabKeyboardFocus();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ProjectContentComponent::hideEditor()
|
||||
{
|
||||
currentDocument = nullptr;
|
||||
contentViewComponent.setContent ({}, {});
|
||||
|
||||
ProjucerApplication::getCommandManager().commandStatusChanged();
|
||||
resized();
|
||||
}
|
||||
|
||||
void ProjectContentComponent::hideDocument (OpenDocumentManager::Document* doc)
|
||||
{
|
||||
if (doc != currentDocument)
|
||||
return;
|
||||
|
||||
if (auto* replacement = recentDocumentList.getClosestPreviousDocOtherThan (currentDocument))
|
||||
showDocument (replacement, true);
|
||||
else
|
||||
hideEditor();
|
||||
}
|
||||
|
||||
void ProjectContentComponent::setScrollableEditorComponent (std::unique_ptr<Component> component)
|
||||
{
|
||||
jassert (component.get() != nullptr);
|
||||
|
||||
class ContentViewport : public Component
|
||||
{
|
||||
public:
|
||||
ContentViewport (std::unique_ptr<Component> content)
|
||||
{
|
||||
contentViewport.setViewedComponent (content.release(), true);
|
||||
addAndMakeVisible (contentViewport);
|
||||
}
|
||||
|
||||
void resized() override
|
||||
{
|
||||
contentViewport.setBounds (getLocalBounds());
|
||||
}
|
||||
|
||||
private:
|
||||
Viewport contentViewport;
|
||||
};
|
||||
|
||||
contentViewComponent.setContent (std::make_unique<ContentViewport> (std::move (component)), {});
|
||||
currentDocument = nullptr;
|
||||
|
||||
ProjucerApplication::getCommandManager().commandStatusChanged();
|
||||
}
|
||||
|
||||
void ProjectContentComponent::setEditorDocument (std::unique_ptr<Component> component, OpenDocumentManager::Document* doc)
|
||||
{
|
||||
currentDocument = doc;
|
||||
contentViewComponent.setContent (std::move (component),
|
||||
currentDocument != nullptr ? currentDocument->getFile().getFileName()
|
||||
: String());
|
||||
|
||||
ProjucerApplication::getCommandManager().commandStatusChanged();
|
||||
}
|
||||
|
||||
Component* ProjectContentComponent::getEditorComponent()
|
||||
{
|
||||
return contentViewComponent.getCurrentComponent();
|
||||
}
|
||||
|
||||
void ProjectContentComponent::closeDocument()
|
||||
{
|
||||
if (currentDocument != nullptr)
|
||||
{
|
||||
ProjucerApplication::getApp().openDocumentManager
|
||||
.closeDocumentAsync (currentDocument, OpenDocumentManager::SaveIfNeeded::yes, nullptr);
|
||||
return;
|
||||
}
|
||||
|
||||
if (! goToPreviousFile())
|
||||
hideEditor();
|
||||
}
|
||||
|
||||
static void showSaveWarning (OpenDocumentManager::Document* currentDocument)
|
||||
{
|
||||
AlertWindow::showMessageBoxAsync (MessageBoxIconType::WarningIcon,
|
||||
TRANS("Save failed!"),
|
||||
TRANS("Couldn't save the file:")
|
||||
+ "\n" + currentDocument->getFile().getFullPathName());
|
||||
}
|
||||
|
||||
void ProjectContentComponent::saveDocumentAsync()
|
||||
{
|
||||
if (currentDocument != nullptr)
|
||||
{
|
||||
currentDocument->saveAsync ([parent = SafePointer<ProjectContentComponent> { this }] (bool savedSuccessfully)
|
||||
{
|
||||
if (parent == nullptr)
|
||||
return;
|
||||
|
||||
if (! savedSuccessfully)
|
||||
showSaveWarning (parent->currentDocument);
|
||||
|
||||
parent->refreshProjectTreeFileStatuses();
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
saveProjectAsync();
|
||||
}
|
||||
}
|
||||
|
||||
void ProjectContentComponent::saveAsAsync()
|
||||
{
|
||||
if (currentDocument != nullptr)
|
||||
{
|
||||
currentDocument->saveAsAsync ([parent = SafePointer<ProjectContentComponent> { this }] (bool savedSuccessfully)
|
||||
{
|
||||
if (parent == nullptr)
|
||||
return;
|
||||
|
||||
if (! savedSuccessfully)
|
||||
showSaveWarning (parent->currentDocument);
|
||||
|
||||
parent->refreshProjectTreeFileStatuses();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
bool ProjectContentComponent::goToPreviousFile()
|
||||
{
|
||||
auto* 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)
|
||||
{
|
||||
auto file = currentDocument->getCounterpartFile();
|
||||
|
||||
if (file.exists())
|
||||
return showEditorForFile (file, true);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void ProjectContentComponent::saveProjectAsync()
|
||||
{
|
||||
if (project != nullptr)
|
||||
project->saveAsync (true, true, nullptr);
|
||||
}
|
||||
|
||||
void ProjectContentComponent::closeProject()
|
||||
{
|
||||
if (auto* mw = findParentComponentOfClass<MainWindow>())
|
||||
mw->closeCurrentProject (OpenDocumentManager::SaveIfNeeded::yes, nullptr);
|
||||
}
|
||||
|
||||
void ProjectContentComponent::showProjectSettings()
|
||||
{
|
||||
setScrollableEditorComponent (std::make_unique<ProjectSettingsComponent> (*project));
|
||||
}
|
||||
|
||||
void ProjectContentComponent::showCurrentExporterSettings()
|
||||
{
|
||||
if (auto selected = headerComponent.getSelectedExporter())
|
||||
showExporterSettings (selected->getUniqueName());
|
||||
}
|
||||
|
||||
void ProjectContentComponent::showExporterSettings (const String& exporterName)
|
||||
{
|
||||
if (exporterName.isEmpty())
|
||||
return;
|
||||
|
||||
showExportersPanel();
|
||||
|
||||
if (sidebar == nullptr)
|
||||
return;
|
||||
|
||||
if (auto* exportersPanel = sidebar->getExportersTreePanel())
|
||||
{
|
||||
if (auto* exporters = dynamic_cast<TreeItemTypes::ExportersTreeRoot*> (exportersPanel->rootItem.get()))
|
||||
{
|
||||
for (auto i = exporters->getNumSubItems(); i >= 0; --i)
|
||||
{
|
||||
if (auto* e = dynamic_cast<TreeItemTypes::ExporterItem*> (exporters->getSubItem (i)))
|
||||
{
|
||||
if (e->getDisplayName() == exporterName)
|
||||
{
|
||||
if (e->isSelected())
|
||||
e->setSelected (false, true);
|
||||
|
||||
e->setSelected (true, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ProjectContentComponent::showModule (const String& moduleID)
|
||||
{
|
||||
showModulesPanel();
|
||||
|
||||
if (sidebar == nullptr)
|
||||
return;
|
||||
|
||||
if (auto* modsPanel = sidebar->getModuleTreePanel())
|
||||
{
|
||||
if (auto* mods = dynamic_cast<TreeItemTypes::EnabledModulesItem*> (modsPanel->rootItem.get()))
|
||||
{
|
||||
for (auto i = mods->getNumSubItems(); --i >= 0;)
|
||||
{
|
||||
if (auto* m = dynamic_cast<TreeItemTypes::ModuleItem*> (mods->getSubItem (i)))
|
||||
{
|
||||
if (m->moduleID == moduleID)
|
||||
{
|
||||
if (m->isSelected())
|
||||
m->setSelected (false, true);
|
||||
|
||||
m->setSelected (true, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
StringArray ProjectContentComponent::getExportersWhichCanLaunch() const
|
||||
{
|
||||
StringArray s;
|
||||
|
||||
if (project != nullptr)
|
||||
for (Project::ExporterIterator exporter (*project); exporter.next();)
|
||||
if (exporter->canLaunchProject())
|
||||
s.add (exporter->getUniqueName());
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
void ProjectContentComponent::openInSelectedIDE (bool saveFirst)
|
||||
{
|
||||
if (project == nullptr)
|
||||
return;
|
||||
|
||||
if (auto selectedExporter = headerComponent.getSelectedExporter())
|
||||
{
|
||||
if (saveFirst)
|
||||
{
|
||||
SafePointer<ProjectContentComponent> safeThis { this };
|
||||
project->saveAsync (true, true, [safeThis] (Project::SaveResult r)
|
||||
{
|
||||
if (safeThis != nullptr && r == Project::SaveResult::savedOk)
|
||||
safeThis->openInSelectedIDE (false);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
project->openProjectInIDE (*selectedExporter);
|
||||
}
|
||||
}
|
||||
|
||||
void ProjectContentComponent::showNewExporterMenu()
|
||||
{
|
||||
if (project != nullptr)
|
||||
{
|
||||
PopupMenu menu;
|
||||
|
||||
menu.addSectionHeader ("Create a new export target:");
|
||||
|
||||
SafePointer<ProjectContentComponent> safeThis (this);
|
||||
|
||||
for (auto& exporterInfo : ProjectExporter::getExporterTypeInfos())
|
||||
{
|
||||
PopupMenu::Item item;
|
||||
|
||||
item.itemID = -1;
|
||||
item.text = exporterInfo.displayName;
|
||||
|
||||
item.image = [exporterInfo]
|
||||
{
|
||||
auto drawableImage = std::make_unique<DrawableImage>();
|
||||
drawableImage->setImage (exporterInfo.icon);
|
||||
|
||||
return drawableImage;
|
||||
}();
|
||||
|
||||
item.action = [safeThis, exporterInfo]
|
||||
{
|
||||
if (safeThis != nullptr)
|
||||
if (auto* p = safeThis->getProject())
|
||||
p->addNewExporter (exporterInfo.identifier);
|
||||
};
|
||||
|
||||
menu.addItem (item);
|
||||
}
|
||||
|
||||
menu.showMenuAsync ({});
|
||||
}
|
||||
}
|
||||
|
||||
void ProjectContentComponent::deleteSelectedTreeItems()
|
||||
{
|
||||
if (sidebar != nullptr)
|
||||
if (auto* tree = sidebar->getTreeWithSelectedItems())
|
||||
tree->deleteSelectedItems();
|
||||
}
|
||||
|
||||
void ProjectContentComponent::showBubbleMessage (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, true,
|
||||
600, 700,
|
||||
600, 400, 10000, 10000);
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
struct AsyncCommandRetrier : public Timer
|
||||
{
|
||||
AsyncCommandRetrier (const ApplicationCommandTarget::InvocationInfo& i) : info (i)
|
||||
{
|
||||
info.originatingComponent = nullptr;
|
||||
startTimer (500);
|
||||
}
|
||||
|
||||
void timerCallback() override
|
||||
{
|
||||
stopTimer();
|
||||
ProjucerApplication::getCommandManager().invoke (info, true);
|
||||
delete this;
|
||||
}
|
||||
|
||||
ApplicationCommandTarget::InvocationInfo info;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE (AsyncCommandRetrier)
|
||||
};
|
||||
|
||||
static bool reinvokeCommandAfterCancellingModalComps (const ApplicationCommandTarget::InvocationInfo& info)
|
||||
{
|
||||
if (ModalComponentManager::getInstance()->cancelAllModalComponents())
|
||||
{
|
||||
new AsyncCommandRetrier (info);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
ApplicationCommandTarget* ProjectContentComponent::getNextCommandTarget()
|
||||
{
|
||||
return findFirstTargetParentComponent();
|
||||
}
|
||||
|
||||
void ProjectContentComponent::getAllCommands (Array <CommandID>& commands)
|
||||
{
|
||||
commands.addArray ({ CommandIDs::saveProject,
|
||||
CommandIDs::closeProject,
|
||||
CommandIDs::saveDocument,
|
||||
CommandIDs::saveDocumentAs,
|
||||
CommandIDs::closeDocument,
|
||||
CommandIDs::goToPreviousDoc,
|
||||
CommandIDs::goToNextDoc,
|
||||
CommandIDs::goToCounterpart,
|
||||
CommandIDs::showProjectSettings,
|
||||
CommandIDs::showFileExplorerPanel,
|
||||
CommandIDs::showModulesPanel,
|
||||
CommandIDs::showExportersPanel,
|
||||
CommandIDs::showExporterSettings,
|
||||
CommandIDs::openInIDE,
|
||||
CommandIDs::saveAndOpenInIDE,
|
||||
CommandIDs::createNewExporter,
|
||||
CommandIDs::deleteSelectedItem,
|
||||
CommandIDs::showTranslationTool,
|
||||
CommandIDs::addNewGUIFile });
|
||||
}
|
||||
|
||||
void ProjectContentComponent::getCommandInfo (const CommandID commandID, ApplicationCommandInfo& result)
|
||||
{
|
||||
String documentName;
|
||||
if (currentDocument != nullptr)
|
||||
documentName = " '" + currentDocument->getName().substring (0, 32) + "'";
|
||||
|
||||
#if JUCE_MAC
|
||||
auto cmdCtrl = (ModifierKeys::ctrlModifier | ModifierKeys::commandModifier);
|
||||
#else
|
||||
auto 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 && ! project->isSaveAndExportDisabled() && ! project->isCurrentlySaving());
|
||||
result.defaultKeypresses.add ({ 'p', ModifierKeys::commandModifier, 0 });
|
||||
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 && ! project->isCurrentlySaving()));
|
||||
result.defaultKeypresses.add ({ '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);
|
||||
result.defaultKeypresses.add ({ 's', ModifierKeys::commandModifier | ModifierKeys::shiftModifier, 0 });
|
||||
break;
|
||||
|
||||
case CommandIDs::closeDocument:
|
||||
result.setInfo ("Close" + documentName,
|
||||
"Closes the current document",
|
||||
CommandCategories::general, 0);
|
||||
result.setActive (currentDocument != nullptr);
|
||||
result.defaultKeypresses.add ({ '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::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::rightKey, cmdCtrl, 0 });
|
||||
break;
|
||||
|
||||
case CommandIDs::goToCounterpart:
|
||||
result.setInfo ("Open Counterpart File",
|
||||
"Open corresponding header or cpp file",
|
||||
CommandCategories::general, 0);
|
||||
result.setActive (canGoToCounterpart());
|
||||
result.defaultKeypresses.add ({ KeyPress::upKey, cmdCtrl, 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 ({ 'x', cmdCtrl, 0 });
|
||||
break;
|
||||
|
||||
case CommandIDs::showFileExplorerPanel:
|
||||
result.setInfo ("Show File Explorer Panel",
|
||||
"Shows the panel containing the tree of files for this project",
|
||||
CommandCategories::general, 0);
|
||||
result.setActive (project != nullptr);
|
||||
result.defaultKeypresses.add ({ 'f', cmdCtrl, 0 });
|
||||
break;
|
||||
|
||||
case CommandIDs::showModulesPanel:
|
||||
result.setInfo ("Show Modules Panel",
|
||||
"Shows the panel containing the project's list of modules",
|
||||
CommandCategories::general, 0);
|
||||
result.setActive (project != nullptr);
|
||||
result.defaultKeypresses.add ({ 'm', cmdCtrl, 0 });
|
||||
break;
|
||||
|
||||
case CommandIDs::showExportersPanel:
|
||||
result.setInfo ("Show Exporters Panel",
|
||||
"Shows the panel containing the project's list of exporters",
|
||||
CommandCategories::general, 0);
|
||||
result.setActive (project != nullptr);
|
||||
result.defaultKeypresses.add ({ 'e', cmdCtrl, 0 });
|
||||
break;
|
||||
|
||||
case CommandIDs::showExporterSettings:
|
||||
result.setInfo ("Show Exporter Settings",
|
||||
"Shows the settings page for the currently selected exporter",
|
||||
CommandCategories::general, 0);
|
||||
result.setActive (project != nullptr);
|
||||
result.defaultKeypresses.add ({ 'e', ModifierKeys::commandModifier | ModifierKeys::shiftModifier, 0 });
|
||||
break;
|
||||
|
||||
case CommandIDs::openInIDE:
|
||||
result.setInfo ("Open in IDE...",
|
||||
"Launches the project in an external IDE",
|
||||
CommandCategories::general, 0);
|
||||
result.setActive (ProjectExporter::canProjectBeLaunched (project) && ! project->isSaveAndExportDisabled());
|
||||
break;
|
||||
|
||||
case CommandIDs::saveAndOpenInIDE:
|
||||
result.setInfo ("Save Project and Open in IDE...",
|
||||
"Saves the project and launches it in an external IDE",
|
||||
CommandCategories::general, 0);
|
||||
result.setActive (ProjectExporter::canProjectBeLaunched (project) && ! project->isSaveAndExportDisabled() && ! project->isCurrentlySaving());
|
||||
result.defaultKeypresses.add ({ 'l', ModifierKeys::commandModifier | ModifierKeys::shiftModifier, 0 });
|
||||
break;
|
||||
|
||||
case CommandIDs::createNewExporter:
|
||||
result.setInfo ("Create New Exporter...",
|
||||
"Creates a new exporter for a compiler type",
|
||||
CommandCategories::general, 0);
|
||||
result.setActive (project != nullptr);
|
||||
break;
|
||||
|
||||
case CommandIDs::deleteSelectedItem:
|
||||
result.setInfo ("Delete Selected File",
|
||||
String(),
|
||||
CommandCategories::general, 0);
|
||||
result.defaultKeypresses.add ({ KeyPress::deleteKey, 0, 0 });
|
||||
result.defaultKeypresses.add ({ KeyPress::backspaceKey, 0, 0 });
|
||||
break;
|
||||
|
||||
case CommandIDs::showTranslationTool:
|
||||
result.setInfo ("Translation File Builder",
|
||||
"Shows the translation file helper tool",
|
||||
CommandCategories::general, 0);
|
||||
break;
|
||||
|
||||
case CommandIDs::addNewGUIFile:
|
||||
result.setInfo ("Add new GUI Component...",
|
||||
"Adds a new GUI Component file to the project",
|
||||
CommandCategories::general,
|
||||
(! ProjucerApplication::getApp().isGUIEditorEnabled() ? ApplicationCommandInfo::isDisabled : 0));
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool ProjectContentComponent::perform (const InvocationInfo& info)
|
||||
{
|
||||
// don't allow the project to be saved again if it's currently saving
|
||||
if (isSaveCommand (info.commandID) && project != nullptr && project->isCurrentlySaving())
|
||||
return false;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
if (isCurrentlyBlockedByAnotherModalComponent())
|
||||
return false;
|
||||
|
||||
switch (info.commandID)
|
||||
{
|
||||
case CommandIDs::saveProject: saveProjectAsync(); break;
|
||||
case CommandIDs::closeProject: closeProject(); break;
|
||||
case CommandIDs::saveDocument: saveDocumentAsync(); break;
|
||||
case CommandIDs::saveDocumentAs: saveAsAsync(); break;
|
||||
case CommandIDs::closeDocument: closeDocument(); break;
|
||||
case CommandIDs::goToPreviousDoc: goToPreviousFile(); break;
|
||||
case CommandIDs::goToNextDoc: goToNextFile(); break;
|
||||
case CommandIDs::goToCounterpart: goToCounterpart(); break;
|
||||
|
||||
case CommandIDs::showProjectSettings: showProjectSettings(); break;
|
||||
case CommandIDs::showFileExplorerPanel: showFilesPanel(); break;
|
||||
case CommandIDs::showModulesPanel: showModulesPanel(); break;
|
||||
case CommandIDs::showExportersPanel: showExportersPanel(); break;
|
||||
case CommandIDs::showExporterSettings: showCurrentExporterSettings(); break;
|
||||
|
||||
case CommandIDs::openInIDE: openInSelectedIDE (false); break;
|
||||
case CommandIDs::saveAndOpenInIDE: openInSelectedIDE (true); break;
|
||||
|
||||
case CommandIDs::createNewExporter: showNewExporterMenu(); break;
|
||||
|
||||
case CommandIDs::deleteSelectedItem: deleteSelectedTreeItems(); break;
|
||||
|
||||
case CommandIDs::showTranslationTool: showTranslationTool(); break;
|
||||
|
||||
case CommandIDs::addNewGUIFile: addNewGUIFile(); break;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ProjectContentComponent::isSaveCommand (const CommandID id)
|
||||
{
|
||||
return (id == CommandIDs::saveProject || id == CommandIDs::saveDocument || id == CommandIDs::saveAndOpenInIDE);
|
||||
}
|
||||
|
||||
void ProjectContentComponent::getSelectedProjectItemsBeingDragged (const DragAndDropTarget::SourceDetails& dragSourceDetails,
|
||||
OwnedArray<Project::Item>& selectedNodes)
|
||||
{
|
||||
TreeItemTypes::FileTreeItemBase::getSelectedProjectItemsBeingDragged (dragSourceDetails, selectedNodes);
|
||||
}
|
||||
|
||||
void ProjectContentComponent::addNewGUIFile()
|
||||
{
|
||||
if (project != nullptr)
|
||||
{
|
||||
wizardHolder = std::make_unique<WizardHolder>();
|
||||
wizardHolder->wizard.reset (createGUIComponentWizard (*project));
|
||||
wizardHolder->wizard->createNewFile (*project, project->getMainGroup());
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void ProjectContentComponent::showProjectPanel (const int index)
|
||||
{
|
||||
if (sidebar != nullptr)
|
||||
sidebar->showPanel (index);
|
||||
}
|
153
deps/juce/extras/Projucer/Source/Project/UI/jucer_ProjectContentComponent.h
vendored
Normal file
153
deps/juce/extras/Projucer/Source/Project/UI/jucer_ProjectContentComponent.h
vendored
Normal file
@ -0,0 +1,153 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../../CodeEditor/jucer_OpenDocumentManager.h"
|
||||
#include "jucer_HeaderComponent.h"
|
||||
#include "jucer_ProjectMessagesComponent.h"
|
||||
#include "jucer_ContentViewComponent.h"
|
||||
|
||||
class Sidebar;
|
||||
struct WizardHolder;
|
||||
|
||||
//==============================================================================
|
||||
class ProjectContentComponent : public Component,
|
||||
public ApplicationCommandTarget,
|
||||
private ChangeListener,
|
||||
private OpenDocumentManager::DocumentCloseListener
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
ProjectContentComponent();
|
||||
~ProjectContentComponent() override;
|
||||
|
||||
Project* getProject() const noexcept { return project; }
|
||||
void setProject (Project*);
|
||||
|
||||
void saveOpenDocumentList();
|
||||
void reloadLastOpenDocuments();
|
||||
|
||||
bool showEditorForFile (const File&, bool grabFocus);
|
||||
bool hasFileInRecentList (const File&) const;
|
||||
File getCurrentFile() const;
|
||||
|
||||
bool showDocument (OpenDocumentManager::Document*, bool grabFocus);
|
||||
void hideDocument (OpenDocumentManager::Document*);
|
||||
OpenDocumentManager::Document* getCurrentDocument() const { return currentDocument; }
|
||||
void closeDocument();
|
||||
void saveDocumentAsync();
|
||||
void saveAsAsync();
|
||||
|
||||
void hideEditor();
|
||||
void setScrollableEditorComponent (std::unique_ptr<Component> component);
|
||||
void setEditorDocument (std::unique_ptr<Component> component, OpenDocumentManager::Document* doc);
|
||||
Component* getEditorComponent();
|
||||
|
||||
Component& getSidebarComponent();
|
||||
|
||||
bool goToPreviousFile();
|
||||
bool goToNextFile();
|
||||
bool canGoToCounterpart() const;
|
||||
bool goToCounterpart();
|
||||
|
||||
void saveProjectAsync();
|
||||
void closeProject();
|
||||
void openInSelectedIDE (bool saveFirst);
|
||||
void showNewExporterMenu();
|
||||
|
||||
void showFilesPanel() { showProjectPanel (0); }
|
||||
void showModulesPanel() { showProjectPanel (1); }
|
||||
void showExportersPanel() { showProjectPanel (2); }
|
||||
|
||||
void showProjectSettings();
|
||||
void showCurrentExporterSettings();
|
||||
void showExporterSettings (const String& exporterName);
|
||||
void showModule (const String& moduleID);
|
||||
void showUserSettings();
|
||||
|
||||
void deleteSelectedTreeItems();
|
||||
|
||||
void refreshProjectTreeFileStatuses();
|
||||
void updateMissingFileStatuses();
|
||||
void addNewGUIFile();
|
||||
|
||||
void showBubbleMessage (Rectangle<int>, const String&);
|
||||
|
||||
StringArray getExportersWhichCanLaunch() const;
|
||||
|
||||
static void getSelectedProjectItemsBeingDragged (const DragAndDropTarget::SourceDetails&,
|
||||
OwnedArray<Project::Item>& selectedNodes);
|
||||
|
||||
//==============================================================================
|
||||
ApplicationCommandTarget* getNextCommandTarget() override;
|
||||
void getAllCommands (Array<CommandID>&) override;
|
||||
void getCommandInfo (CommandID, ApplicationCommandInfo&) override;
|
||||
bool perform (const InvocationInfo&) override;
|
||||
|
||||
bool isSaveCommand (const CommandID id);
|
||||
|
||||
void paint (Graphics&) override;
|
||||
void resized() override;
|
||||
void childBoundsChanged (Component*) override;
|
||||
void lookAndFeelChanged() override;
|
||||
|
||||
ProjectMessagesComponent& getProjectMessagesComponent() { return projectMessagesComponent; }
|
||||
|
||||
static String getProjectTabName() { return "Project"; }
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
bool documentAboutToClose (OpenDocumentManager::Document*) override;
|
||||
void changeListenerCallback (ChangeBroadcaster*) override;
|
||||
void showTranslationTool();
|
||||
|
||||
//==============================================================================
|
||||
void showProjectPanel (const int index);
|
||||
bool canSelectedProjectBeLaunch();
|
||||
|
||||
//==============================================================================
|
||||
Project* project = nullptr;
|
||||
OpenDocumentManager::Document* currentDocument = nullptr;
|
||||
RecentDocumentList recentDocumentList;
|
||||
|
||||
HeaderComponent headerComponent { this };
|
||||
std::unique_ptr<Sidebar> sidebar;
|
||||
ProjectMessagesComponent projectMessagesComponent;
|
||||
ContentViewComponent contentViewComponent;
|
||||
|
||||
std::unique_ptr<ResizableEdgeComponent> resizerBar;
|
||||
ComponentBoundsConstrainer sidebarSizeConstrainer;
|
||||
std::unique_ptr<Component> translationTool;
|
||||
BubbleMessageComponent bubbleMessage;
|
||||
|
||||
bool isForeground = false;
|
||||
int lastViewedTab = 0;
|
||||
|
||||
std::unique_ptr<WizardHolder> wizardHolder;
|
||||
|
||||
//==============================================================================
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ProjectContentComponent)
|
||||
};
|
581
deps/juce/extras/Projucer/Source/Project/UI/jucer_ProjectMessagesComponent.h
vendored
Normal file
581
deps/juce/extras/Projucer/Source/Project/UI/jucer_ProjectMessagesComponent.h
vendored
Normal file
@ -0,0 +1,581 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../../Application/jucer_CommonHeaders.h"
|
||||
#include "../../Application/jucer_Application.h"
|
||||
|
||||
//==============================================================================
|
||||
class MessagesPopupWindow : public Component,
|
||||
private ComponentMovementWatcher
|
||||
{
|
||||
public:
|
||||
MessagesPopupWindow (Component& target, Component& parent, Project& project)
|
||||
: ComponentMovementWatcher (&parent),
|
||||
targetComponent (target),
|
||||
parentComponent (parent),
|
||||
messagesListComponent (*this, project)
|
||||
{
|
||||
parentComponent.addAndMakeVisible (this);
|
||||
setAlwaysOnTop (true);
|
||||
|
||||
addAndMakeVisible (viewport);
|
||||
viewport.setScrollBarsShown (true, false);
|
||||
viewport.setViewedComponent (&messagesListComponent, false);
|
||||
viewport.setWantsKeyboardFocus (false);
|
||||
|
||||
setOpaque (true);
|
||||
}
|
||||
|
||||
void paint (Graphics& g) override
|
||||
{
|
||||
g.fillAll (findColour (secondaryBackgroundColourId));
|
||||
}
|
||||
|
||||
void resized() override
|
||||
{
|
||||
viewport.setBounds (getLocalBounds());
|
||||
}
|
||||
|
||||
bool isListShowing() const
|
||||
{
|
||||
return messagesListComponent.getRequiredHeight() > 0;
|
||||
}
|
||||
|
||||
void updateBounds (bool animate)
|
||||
{
|
||||
auto targetBounds = parentComponent.getLocalArea (&targetComponent, targetComponent.getLocalBounds());
|
||||
|
||||
auto height = jmin (messagesListComponent.getRequiredHeight(), maxHeight);
|
||||
auto yPos = jmax (indent, targetBounds.getY() - height);
|
||||
|
||||
Rectangle<int> bounds (targetBounds.getX(), yPos,
|
||||
jmin (width, parentComponent.getWidth() - targetBounds.getX() - indent), targetBounds.getY() - yPos);
|
||||
|
||||
auto& animator = Desktop::getInstance().getAnimator();
|
||||
|
||||
if (animate)
|
||||
{
|
||||
setBounds (bounds.withY (targetBounds.getY()));
|
||||
animator.animateComponent (this, bounds, 1.0f, 150, false, 1.0, 1.0);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (animator.isAnimating (this))
|
||||
animator.cancelAnimation (this, false);
|
||||
|
||||
setBounds (bounds);
|
||||
}
|
||||
|
||||
messagesListComponent.resized();
|
||||
}
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
class MessagesListComponent : public Component,
|
||||
private ValueTree::Listener,
|
||||
private AsyncUpdater
|
||||
{
|
||||
public:
|
||||
MessagesListComponent (MessagesPopupWindow& o, Project& currentProject)
|
||||
: owner (o),
|
||||
project (currentProject)
|
||||
{
|
||||
messagesTree = project.getProjectMessages();
|
||||
messagesTree.addListener (this);
|
||||
|
||||
setOpaque (true);
|
||||
|
||||
messagesChanged();
|
||||
}
|
||||
|
||||
void resized() override
|
||||
{
|
||||
auto bounds = getLocalBounds();
|
||||
auto numMessages = messages.size();
|
||||
|
||||
for (size_t i = 0; i < numMessages; ++i)
|
||||
{
|
||||
messages[i]->setBounds (bounds.removeFromTop (messageHeight));
|
||||
|
||||
if (numMessages > 1 && i != (numMessages - 1))
|
||||
bounds.removeFromTop (messageSpacing);
|
||||
}
|
||||
}
|
||||
|
||||
void paint (Graphics& g) override
|
||||
{
|
||||
g.fillAll (findColour (backgroundColourId).contrasting (0.2f));
|
||||
}
|
||||
|
||||
int getRequiredHeight() const
|
||||
{
|
||||
auto numMessages = (int) messages.size();
|
||||
|
||||
if (numMessages > 0)
|
||||
return (numMessages * messageHeight) + ((numMessages - 1) * messageSpacing);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void updateSize (int parentWidth)
|
||||
{
|
||||
setSize (parentWidth, getRequiredHeight());
|
||||
}
|
||||
|
||||
private:
|
||||
static constexpr int messageHeight = 65;
|
||||
static constexpr int messageSpacing = 2;
|
||||
|
||||
//==============================================================================
|
||||
struct MessageComponent : public Component
|
||||
{
|
||||
MessageComponent (MessagesListComponent& listComponent,
|
||||
const Identifier& messageToDisplay,
|
||||
std::vector<ProjectMessages::MessageAction> messageActions)
|
||||
: message (messageToDisplay)
|
||||
{
|
||||
for (auto& action : messageActions)
|
||||
{
|
||||
auto button = std::make_unique<TextButton> (action.first);
|
||||
addAndMakeVisible (*button);
|
||||
button->onClick = action.second;
|
||||
|
||||
buttons.push_back (std::move (button));
|
||||
}
|
||||
|
||||
icon = (ProjectMessages::getTypeForMessage (message) == ProjectMessages::Ids::warning ? getIcons().warning : getIcons().info);
|
||||
|
||||
messageTitleLabel.setText (ProjectMessages::getTitleForMessage (message), dontSendNotification);
|
||||
messageTitleLabel.setFont (Font (11.0f).boldened());
|
||||
addAndMakeVisible (messageTitleLabel);
|
||||
|
||||
messageDescriptionLabel.setText (ProjectMessages::getDescriptionForMessage (message), dontSendNotification);
|
||||
messageDescriptionLabel.setFont (Font (11.0f));
|
||||
messageDescriptionLabel.setJustificationType (Justification::topLeft);
|
||||
addAndMakeVisible (messageDescriptionLabel);
|
||||
|
||||
dismissButton.setShape (getLookAndFeel().getCrossShape (1.0f), false, true, false);
|
||||
addAndMakeVisible (dismissButton);
|
||||
|
||||
dismissButton.onClick = [this, &listComponent]
|
||||
{
|
||||
listComponent.messagesTree.getChildWithName (ProjectMessages::getTypeForMessage (message))
|
||||
.getChildWithName (message)
|
||||
.setProperty (ProjectMessages::Ids::isVisible, false, nullptr);
|
||||
};
|
||||
}
|
||||
|
||||
void paint (Graphics& g) override
|
||||
{
|
||||
g.fillAll (findColour (secondaryBackgroundColourId).contrasting (0.1f));
|
||||
|
||||
auto bounds = getLocalBounds().reduced (5);
|
||||
|
||||
g.setColour (findColour (defaultIconColourId));
|
||||
g.fillPath (icon, icon.getTransformToScaleToFit (bounds.removeFromTop (messageTitleHeight)
|
||||
.removeFromLeft (messageTitleHeight).toFloat(), true));
|
||||
}
|
||||
|
||||
void resized() override
|
||||
{
|
||||
auto bounds = getLocalBounds().reduced (5);
|
||||
|
||||
auto topSlice = bounds.removeFromTop (messageTitleHeight);
|
||||
|
||||
topSlice.removeFromLeft (messageTitleHeight + 5);
|
||||
topSlice.removeFromRight (5);
|
||||
|
||||
dismissButton.setBounds (topSlice.removeFromRight (messageTitleHeight));
|
||||
messageTitleLabel.setBounds (topSlice);
|
||||
bounds.removeFromTop (5);
|
||||
|
||||
auto numButtons = (int) buttons.size();
|
||||
|
||||
if (numButtons > 0)
|
||||
{
|
||||
auto buttonBounds = bounds.removeFromBottom (buttonHeight);
|
||||
|
||||
auto buttonWidth = roundToInt ((float) buttonBounds.getWidth() / 3.5f);
|
||||
auto requiredWidth = (numButtons * buttonWidth) + ((numButtons - 1) * buttonSpacing);
|
||||
buttonBounds.reduce ((buttonBounds.getWidth() - requiredWidth) / 2, 0);
|
||||
|
||||
for (auto& b : buttons)
|
||||
{
|
||||
b->setBounds (buttonBounds.removeFromLeft (buttonWidth));
|
||||
buttonBounds.removeFromLeft (buttonSpacing);
|
||||
}
|
||||
|
||||
bounds.removeFromBottom (5);
|
||||
}
|
||||
|
||||
messageDescriptionLabel.setBounds (bounds);
|
||||
}
|
||||
|
||||
static constexpr int messageTitleHeight = 11;
|
||||
static constexpr int buttonHeight = messageHeight / 4;
|
||||
static constexpr int buttonSpacing = 5;
|
||||
|
||||
Identifier message;
|
||||
|
||||
Path icon;
|
||||
Label messageTitleLabel, messageDescriptionLabel;
|
||||
std::vector<std::unique_ptr<TextButton>> buttons;
|
||||
ShapeButton dismissButton { {},
|
||||
findColour (treeIconColourId),
|
||||
findColour (treeIconColourId).overlaidWith (findColour (defaultHighlightedTextColourId).withAlpha (0.2f)),
|
||||
findColour (treeIconColourId).overlaidWith (findColour (defaultHighlightedTextColourId).withAlpha (0.4f)) };
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MessageComponent)
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
void valueTreePropertyChanged (ValueTree&, const Identifier&) override { messagesChanged(); }
|
||||
void valueTreeChildAdded (ValueTree&, ValueTree&) override { messagesChanged(); }
|
||||
void valueTreeChildRemoved (ValueTree&, ValueTree&, int) override { messagesChanged(); }
|
||||
void valueTreeChildOrderChanged (ValueTree&, int, int) override { messagesChanged(); }
|
||||
void valueTreeParentChanged (ValueTree&) override { messagesChanged(); }
|
||||
void valueTreeRedirected (ValueTree&) override { messagesChanged(); }
|
||||
|
||||
void handleAsyncUpdate() override
|
||||
{
|
||||
messagesChanged();
|
||||
}
|
||||
|
||||
void messagesChanged()
|
||||
{
|
||||
auto listWasShowing = (getHeight() > 0);
|
||||
|
||||
auto warningsTree = messagesTree.getChildWithName (ProjectMessages::Ids::warning);
|
||||
auto notificationsTree = messagesTree.getChildWithName (ProjectMessages::Ids::notification);
|
||||
|
||||
auto removePredicate = [warningsTree, notificationsTree] (std::unique_ptr<MessageComponent>& messageComponent)
|
||||
{
|
||||
for (int i = 0; i < warningsTree.getNumChildren(); ++i)
|
||||
{
|
||||
auto child = warningsTree.getChild (i);
|
||||
|
||||
if (child.getType() == messageComponent->message
|
||||
&& child.getProperty (ProjectMessages::Ids::isVisible))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < notificationsTree.getNumChildren(); ++i)
|
||||
{
|
||||
auto child = notificationsTree.getChild (i);
|
||||
|
||||
if (child.getType() == messageComponent->message
|
||||
&& child.getProperty (ProjectMessages::Ids::isVisible))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
messages.erase (std::remove_if (std::begin (messages), std::end (messages), removePredicate),
|
||||
std::end (messages));
|
||||
|
||||
for (int i = 0; i < warningsTree.getNumChildren(); ++i)
|
||||
{
|
||||
auto child = warningsTree.getChild (i);
|
||||
|
||||
if (! child.getProperty (ProjectMessages::Ids::isVisible))
|
||||
continue;
|
||||
|
||||
if (std::find_if (std::begin (messages), std::end (messages),
|
||||
[child] (const std::unique_ptr<MessageComponent>& messageComponent) { return messageComponent->message == child.getType(); })
|
||||
== std::end (messages))
|
||||
{
|
||||
messages.push_back (std::make_unique<MessageComponent> (*this, child.getType(), project.getMessageActions (child.getType())));
|
||||
addAndMakeVisible (*messages.back());
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < notificationsTree.getNumChildren(); ++i)
|
||||
{
|
||||
auto child = notificationsTree.getChild (i);
|
||||
|
||||
if (! child.getProperty (ProjectMessages::Ids::isVisible))
|
||||
continue;
|
||||
|
||||
if (std::find_if (std::begin (messages), std::end (messages),
|
||||
[child] (const std::unique_ptr<MessageComponent>& messageComponent) { return messageComponent->message == child.getType(); })
|
||||
== std::end (messages))
|
||||
{
|
||||
messages.push_back (std::make_unique<MessageComponent> (*this, child.getType(), project.getMessageActions (child.getType())));
|
||||
addAndMakeVisible (*messages.back());
|
||||
}
|
||||
}
|
||||
|
||||
auto isNowShowing = (messages.size() > 0);
|
||||
|
||||
owner.updateBounds (isNowShowing != listWasShowing);
|
||||
updateSize (owner.getWidth());
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
MessagesPopupWindow& owner;
|
||||
Project& project;
|
||||
|
||||
ValueTree messagesTree;
|
||||
std::vector<std::unique_ptr<MessageComponent>> messages;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MessagesListComponent)
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
void componentMovedOrResized (bool, bool) override
|
||||
{
|
||||
if (isListShowing())
|
||||
updateBounds (false);
|
||||
}
|
||||
|
||||
using ComponentMovementWatcher::componentMovedOrResized;
|
||||
|
||||
void componentPeerChanged() override
|
||||
{
|
||||
if (isListShowing())
|
||||
updateBounds (false);
|
||||
}
|
||||
|
||||
void componentVisibilityChanged() override
|
||||
{
|
||||
if (isListShowing())
|
||||
updateBounds (false);
|
||||
}
|
||||
|
||||
using ComponentMovementWatcher::componentVisibilityChanged;
|
||||
|
||||
//==============================================================================
|
||||
static constexpr int maxHeight = 500, width = 350, indent = 20;
|
||||
|
||||
Component& targetComponent;
|
||||
Component& parentComponent;
|
||||
|
||||
Viewport viewport;
|
||||
MessagesListComponent messagesListComponent;
|
||||
|
||||
//==============================================================================
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MessagesPopupWindow)
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
class ProjectMessagesComponent : public Component
|
||||
{
|
||||
public:
|
||||
ProjectMessagesComponent()
|
||||
{
|
||||
setFocusContainerType (FocusContainerType::focusContainer);
|
||||
setTitle ("Project Messages");
|
||||
|
||||
addAndMakeVisible (warningsComponent);
|
||||
addAndMakeVisible (notificationsComponent);
|
||||
|
||||
warningsComponent.addMouseListener (this, true);
|
||||
notificationsComponent.addMouseListener (this, true);
|
||||
|
||||
setOpaque (true);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void resized() override
|
||||
{
|
||||
auto b = getLocalBounds();
|
||||
|
||||
warningsComponent.setBounds (b.removeFromLeft (b.getWidth() / 2).reduced (5));
|
||||
notificationsComponent.setBounds (b.reduced (5));
|
||||
}
|
||||
|
||||
void paint (Graphics& g) override
|
||||
{
|
||||
auto backgroundColour = findColour (backgroundColourId);
|
||||
|
||||
if (isMouseDown || isMouseOver)
|
||||
backgroundColour = backgroundColour.overlaidWith (findColour (defaultHighlightColourId)
|
||||
.withAlpha (isMouseDown ? 1.0f : 0.8f));
|
||||
|
||||
g.fillAll (backgroundColour);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void mouseEnter (const MouseEvent&) override
|
||||
{
|
||||
isMouseOver = true;
|
||||
repaint();
|
||||
}
|
||||
|
||||
void mouseExit (const MouseEvent&) override
|
||||
{
|
||||
isMouseOver = false;
|
||||
repaint();
|
||||
}
|
||||
|
||||
void mouseDown (const MouseEvent&) override
|
||||
{
|
||||
isMouseDown = true;
|
||||
repaint();
|
||||
}
|
||||
|
||||
void mouseUp (const MouseEvent&) override
|
||||
{
|
||||
isMouseDown = false;
|
||||
repaint();
|
||||
|
||||
showOrHideMessagesWindow();
|
||||
}
|
||||
|
||||
std::unique_ptr<AccessibilityHandler> createAccessibilityHandler() override
|
||||
{
|
||||
return std::make_unique<AccessibilityHandler> (*this,
|
||||
AccessibilityRole::button,
|
||||
AccessibilityActions().addAction (AccessibilityActionType::press,
|
||||
[this] { showOrHideMessagesWindow(); }));
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void setProject (Project* newProject)
|
||||
{
|
||||
if (currentProject != newProject)
|
||||
{
|
||||
currentProject = newProject;
|
||||
|
||||
if (currentProject != nullptr)
|
||||
{
|
||||
if (auto* projectWindow = ProjucerApplication::getApp().mainWindowList.getMainWindowForFile (currentProject->getFile()))
|
||||
messagesWindow = std::make_unique<MessagesPopupWindow> (*this, *projectWindow, *currentProject);
|
||||
|
||||
auto projectMessagesTree = currentProject->getProjectMessages();
|
||||
|
||||
warningsComponent.setTree (projectMessagesTree.getChildWithName (ProjectMessages::Ids::warning));
|
||||
notificationsComponent.setTree (projectMessagesTree.getChildWithName (ProjectMessages::Ids::notification));
|
||||
}
|
||||
else
|
||||
{
|
||||
warningsComponent.setTree ({});
|
||||
notificationsComponent.setTree ({});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void numMessagesChanged()
|
||||
{
|
||||
const auto total = warningsComponent.getNumMessages()
|
||||
+ notificationsComponent.getNumMessages();
|
||||
|
||||
setHelpText (String (total) + (total == 1 ? " message" : " messages"));
|
||||
}
|
||||
|
||||
void showOrHideMessagesWindow()
|
||||
{
|
||||
if (messagesWindow != nullptr)
|
||||
showOrHideAllMessages (! messagesWindow->isListShowing());
|
||||
}
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
struct MessageCountComponent : public Component,
|
||||
private ValueTree::Listener
|
||||
{
|
||||
MessageCountComponent (ProjectMessagesComponent& o, Path pathToUse)
|
||||
: owner (o),
|
||||
path (pathToUse)
|
||||
{
|
||||
setInterceptsMouseClicks (false, false);
|
||||
}
|
||||
|
||||
void paint (Graphics& g) override
|
||||
{
|
||||
auto b = getLocalBounds().toFloat();
|
||||
|
||||
g.setColour (findColour ((owner.isMouseDown || owner.isMouseOver) ? defaultHighlightedTextColourId : treeIconColourId));
|
||||
g.fillPath (path, path.getTransformToScaleToFit (b.removeFromLeft (b.getWidth() / 2.0f), true));
|
||||
|
||||
b.removeFromLeft (5);
|
||||
g.drawFittedText (String (numMessages), b.getSmallestIntegerContainer(), Justification::centredLeft, 1);
|
||||
}
|
||||
|
||||
void setTree (ValueTree tree)
|
||||
{
|
||||
messagesTree = tree;
|
||||
|
||||
if (messagesTree.isValid())
|
||||
messagesTree.addListener (this);
|
||||
|
||||
updateNumMessages();
|
||||
}
|
||||
|
||||
void updateNumMessages()
|
||||
{
|
||||
numMessages = messagesTree.getNumChildren();
|
||||
owner.numMessagesChanged();
|
||||
repaint();
|
||||
}
|
||||
|
||||
int getNumMessages() const noexcept { return numMessages; }
|
||||
|
||||
private:
|
||||
void valueTreeChildAdded (ValueTree&, ValueTree&) override { updateNumMessages(); }
|
||||
void valueTreeChildRemoved (ValueTree&, ValueTree&, int) override { updateNumMessages(); }
|
||||
|
||||
ProjectMessagesComponent& owner;
|
||||
ValueTree messagesTree;
|
||||
|
||||
Path path;
|
||||
int numMessages = 0;
|
||||
};
|
||||
|
||||
void showOrHideAllMessages (bool shouldBeVisible)
|
||||
{
|
||||
if (currentProject != nullptr)
|
||||
{
|
||||
auto messagesTree = currentProject->getProjectMessages();
|
||||
|
||||
auto setVisible = [shouldBeVisible] (ValueTree subTree)
|
||||
{
|
||||
for (int i = 0; i < subTree.getNumChildren(); ++i)
|
||||
subTree.getChild (i).setProperty (ProjectMessages::Ids::isVisible, shouldBeVisible, nullptr);
|
||||
};
|
||||
|
||||
setVisible (messagesTree.getChildWithName (ProjectMessages::Ids::warning));
|
||||
setVisible (messagesTree.getChildWithName (ProjectMessages::Ids::notification));
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
Project* currentProject = nullptr;
|
||||
bool isMouseOver = false, isMouseDown = false;
|
||||
|
||||
MessageCountComponent warningsComponent { *this, getIcons().warning },
|
||||
notificationsComponent { *this, getIcons().info };
|
||||
|
||||
std::unique_ptr<MessagesPopupWindow> messagesWindow;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ProjectMessagesComponent)
|
||||
};
|
171
deps/juce/extras/Projucer/Source/Project/UI/jucer_UserAvatarComponent.h
vendored
Normal file
171
deps/juce/extras/Projucer/Source/Project/UI/jucer_UserAvatarComponent.h
vendored
Normal file
@ -0,0 +1,171 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../../Application/jucer_Application.h"
|
||||
|
||||
//==============================================================================
|
||||
class UserAvatarComponent : public Component,
|
||||
public SettableTooltipClient,
|
||||
public ChangeBroadcaster,
|
||||
private LicenseController::LicenseStateListener
|
||||
{
|
||||
public:
|
||||
UserAvatarComponent (bool isInteractive)
|
||||
: interactive (isInteractive)
|
||||
{
|
||||
ProjucerApplication::getApp().getLicenseController().addListener (this);
|
||||
lookAndFeelChanged();
|
||||
}
|
||||
|
||||
~UserAvatarComponent() override
|
||||
{
|
||||
ProjucerApplication::getApp().getLicenseController().removeListener (this);
|
||||
}
|
||||
|
||||
void paint (Graphics& g) override
|
||||
{
|
||||
auto bounds = getLocalBounds();
|
||||
|
||||
if (! isGPL)
|
||||
{
|
||||
bounds = bounds.removeFromRight (bounds.getHeight());
|
||||
|
||||
Path ellipse;
|
||||
ellipse.addEllipse (bounds.toFloat());
|
||||
|
||||
g.reduceClipRegion (ellipse);
|
||||
}
|
||||
|
||||
g.drawImage (currentAvatar, bounds.toFloat(), RectanglePlacement::fillDestination);
|
||||
}
|
||||
|
||||
void mouseUp (const MouseEvent&) override
|
||||
{
|
||||
triggerClick();
|
||||
}
|
||||
|
||||
void triggerClick()
|
||||
{
|
||||
if (interactive)
|
||||
{
|
||||
PopupMenu menu;
|
||||
menu.addCommandItem (ProjucerApplication::getApp().commandManager.get(), CommandIDs::loginLogout);
|
||||
|
||||
menu.showMenuAsync (PopupMenu::Options().withTargetComponent (this));
|
||||
}
|
||||
}
|
||||
|
||||
bool isDisplaingGPLLogo() const noexcept { return isGPL; }
|
||||
|
||||
std::unique_ptr<AccessibilityHandler> createAccessibilityHandler() override
|
||||
{
|
||||
return interactive ? std::make_unique<AccessibilityHandler> (*this,
|
||||
AccessibilityRole::button,
|
||||
AccessibilityActions().addAction (AccessibilityActionType::press,
|
||||
[this] { triggerClick(); }))
|
||||
: nullptr;
|
||||
}
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
static Image createGPLAvatarImage()
|
||||
{
|
||||
if (auto logo = Drawable::createFromImageData (BinaryData::gpl_logo_svg, BinaryData::gpl_logo_svgSize))
|
||||
{
|
||||
auto bounds = logo->getDrawableBounds();
|
||||
|
||||
Image image (Image::ARGB, roundToInt (bounds.getWidth()), roundToInt (bounds.getHeight()), true);
|
||||
Graphics g (image);
|
||||
logo->draw (g, 1.0f);
|
||||
|
||||
return image;
|
||||
}
|
||||
|
||||
jassertfalse;
|
||||
return {};
|
||||
}
|
||||
|
||||
Image createStandardAvatarImage()
|
||||
{
|
||||
Image image (Image::ARGB, 250, 250, true);
|
||||
Graphics g (image);
|
||||
|
||||
g.setColour (findColour (defaultButtonBackgroundColourId));
|
||||
g.fillAll();
|
||||
|
||||
g.setColour (findColour (defaultIconColourId));
|
||||
|
||||
auto path = getIcons().user;
|
||||
g.fillPath (path, RectanglePlacement (RectanglePlacement::centred)
|
||||
.getTransformToFit (path.getBounds(), image.getBounds().reduced (image.getHeight() / 5).toFloat()));
|
||||
|
||||
return image;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void licenseStateChanged() override
|
||||
{
|
||||
auto state = ProjucerApplication::getApp().getLicenseController().getCurrentState();
|
||||
|
||||
isGPL = ProjucerApplication::getApp().getLicenseController().getCurrentState().isGPL();
|
||||
|
||||
if (interactive)
|
||||
{
|
||||
auto formattedUserString = [state]() -> String
|
||||
{
|
||||
if (state.isSignedIn())
|
||||
return (state.isGPL() ? "" : (state.username + " - ")) + state.getLicenseTypeString();
|
||||
|
||||
return "Not logged in";
|
||||
}();
|
||||
|
||||
setTooltip (formattedUserString);
|
||||
}
|
||||
|
||||
currentAvatar = isGPL ? gplAvatarImage
|
||||
: state.isSignedIn() ? standardAvatarImage : signedOutAvatarImage;
|
||||
|
||||
repaint();
|
||||
sendChangeMessage();
|
||||
}
|
||||
|
||||
void lookAndFeelChanged() override
|
||||
{
|
||||
standardAvatarImage = createStandardAvatarImage();
|
||||
signedOutAvatarImage = createStandardAvatarImage();
|
||||
|
||||
if (interactive)
|
||||
signedOutAvatarImage.multiplyAllAlphas (0.4f);
|
||||
|
||||
licenseStateChanged();
|
||||
repaint();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
Image standardAvatarImage, signedOutAvatarImage, gplAvatarImage { createGPLAvatarImage() }, currentAvatar;
|
||||
bool isGPL = false, interactive = false;
|
||||
};
|
2669
deps/juce/extras/Projucer/Source/Project/jucer_Project.cpp
vendored
Normal file
2669
deps/juce/extras/Projucer/Source/Project/jucer_Project.cpp
vendored
Normal file
File diff suppressed because it is too large
Load Diff
635
deps/juce/extras/Projucer/Source/Project/jucer_Project.h
vendored
Normal file
635
deps/juce/extras/Projucer/Source/Project/jucer_Project.h
vendored
Normal file
@ -0,0 +1,635 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../Application/UserAccount/jucer_LicenseController.h"
|
||||
#include "Modules/jucer_AvailableModulesList.h"
|
||||
|
||||
class ProjectExporter;
|
||||
class LibraryModule;
|
||||
class EnabledModulesList;
|
||||
class ProjectSaver;
|
||||
|
||||
namespace ProjectMessages
|
||||
{
|
||||
namespace Ids
|
||||
{
|
||||
#define DECLARE_ID(name) static const Identifier name (#name)
|
||||
|
||||
DECLARE_ID (projectMessages);
|
||||
|
||||
DECLARE_ID (incompatibleLicense);
|
||||
DECLARE_ID (cppStandard);
|
||||
DECLARE_ID (moduleNotFound);
|
||||
DECLARE_ID (jucePath);
|
||||
DECLARE_ID (jucerFileModified);
|
||||
DECLARE_ID (missingModuleDependencies);
|
||||
DECLARE_ID (oldProjucer);
|
||||
DECLARE_ID (cLion);
|
||||
DECLARE_ID (newVersionAvailable);
|
||||
|
||||
DECLARE_ID (notification);
|
||||
DECLARE_ID (warning);
|
||||
|
||||
DECLARE_ID (isVisible);
|
||||
|
||||
#undef DECLARE_ID
|
||||
}
|
||||
|
||||
inline Identifier getTypeForMessage (const Identifier& message)
|
||||
{
|
||||
static Identifier warnings[] = { Ids::incompatibleLicense, Ids::cppStandard, Ids::moduleNotFound,
|
||||
Ids::jucePath, Ids::jucerFileModified, Ids::missingModuleDependencies,
|
||||
Ids::oldProjucer, Ids::cLion };
|
||||
|
||||
if (std::find (std::begin (warnings), std::end (warnings), message) != std::end (warnings))
|
||||
return Ids::warning;
|
||||
|
||||
if (message == Ids::newVersionAvailable)
|
||||
return Ids::notification;
|
||||
|
||||
jassertfalse;
|
||||
return {};
|
||||
}
|
||||
|
||||
inline String getTitleForMessage (const Identifier& message)
|
||||
{
|
||||
if (message == Ids::incompatibleLicense) return "Incompatible License and Splash Screen Setting";
|
||||
if (message == Ids::cppStandard) return "C++ Standard";
|
||||
if (message == Ids::moduleNotFound) return "Module Not Found";
|
||||
if (message == Ids::jucePath) return "JUCE Path";
|
||||
if (message == Ids::jucerFileModified) return "Project File Modified";
|
||||
if (message == Ids::missingModuleDependencies) return "Missing Module Dependencies";
|
||||
if (message == Ids::oldProjucer) return "Projucer Out of Date";
|
||||
if (message == Ids::newVersionAvailable) return "New Version Available";
|
||||
if (message == Ids::cLion) return "Deprecated Exporter";
|
||||
|
||||
jassertfalse;
|
||||
return {};
|
||||
}
|
||||
|
||||
inline String getDescriptionForMessage (const Identifier& message)
|
||||
{
|
||||
if (message == Ids::incompatibleLicense) return "Save and export is disabled.";
|
||||
if (message == Ids::cppStandard) return "Module(s) have a higher C++ standard requirement than the project.";
|
||||
if (message == Ids::moduleNotFound) return "Module(s) could not be found at the specified paths.";
|
||||
if (message == Ids::jucePath) return "The path to your JUCE folder is incorrect.";
|
||||
if (message == Ids::jucerFileModified) return "The .jucer file has been modified since the last save.";
|
||||
if (message == Ids::missingModuleDependencies) return "Module(s) have missing dependencies.";
|
||||
if (message == Ids::oldProjucer) return "The version of the Projucer you are using is out of date.";
|
||||
if (message == Ids::newVersionAvailable) return "A new version of JUCE is available to download.";
|
||||
if (message == Ids::cLion) return "The CLion exporter is deprecated. Use JUCE's CMake support instead.";
|
||||
|
||||
jassertfalse;
|
||||
return {};
|
||||
}
|
||||
|
||||
using MessageAction = std::pair<String, std::function<void()>>;
|
||||
}
|
||||
|
||||
enum class Async { no, yes };
|
||||
|
||||
//==============================================================================
|
||||
class Project : public FileBasedDocument,
|
||||
private ValueTree::Listener,
|
||||
private LicenseController::LicenseStateListener,
|
||||
private ChangeListener,
|
||||
private AvailableModulesList::Listener
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
Project (const File&);
|
||||
~Project() override;
|
||||
|
||||
//==============================================================================
|
||||
String getDocumentTitle() override;
|
||||
Result loadDocument (const File& file) override;
|
||||
Result saveDocument (const File& file) override;
|
||||
void saveDocumentAsync (const File& file, std::function<void (Result)> callback) override;
|
||||
|
||||
void saveProject (Async, ProjectExporter* exporterToSave, std::function<void (Result)> onCompletion);
|
||||
Result saveResourcesOnly();
|
||||
void openProjectInIDE (ProjectExporter& exporterToOpen);
|
||||
|
||||
File getLastDocumentOpened() override;
|
||||
void setLastDocumentOpened (const File& file) override;
|
||||
|
||||
void setTitle (const String& newTitle);
|
||||
|
||||
//==============================================================================
|
||||
File getProjectFolder() const { return getFile().getParentDirectory(); }
|
||||
File getGeneratedCodeFolder() const { return getFile().getSiblingFile ("JuceLibraryCode"); }
|
||||
File getSourceFilesFolder() const { return getProjectFolder().getChildFile ("Source"); }
|
||||
File getLocalModulesFolder() const { return getGeneratedCodeFolder().getChildFile ("modules"); }
|
||||
File getLocalModuleFolder (const String& moduleID) const { return getLocalModulesFolder().getChildFile (moduleID); }
|
||||
File getAppIncludeFile() const { return getGeneratedCodeFolder().getChildFile (getJuceSourceHFilename()); }
|
||||
|
||||
File getBinaryDataCppFile (int index) const;
|
||||
File getBinaryDataHeaderFile() const { return getBinaryDataCppFile (0).withFileExtension (".h"); }
|
||||
|
||||
static String getAppConfigFilename() { return "AppConfig.h"; }
|
||||
static String getPluginDefinesFilename() { return "JucePluginDefines.h"; }
|
||||
static String getJuceSourceHFilename() { return "JuceHeader.h"; }
|
||||
|
||||
//==============================================================================
|
||||
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&);
|
||||
|
||||
//==============================================================================
|
||||
ValueTree getProjectRoot() const { return projectRoot; }
|
||||
Value getProjectValue (const Identifier& name) { return projectRoot.getPropertyAsValue (name, getUndoManagerFor (projectRoot)); }
|
||||
var getProjectVar (const Identifier& name) const { return projectRoot.getProperty (name); }
|
||||
|
||||
const build_tools::ProjectType& getProjectType() const;
|
||||
String getProjectTypeString() const { return projectTypeValue.get(); }
|
||||
void setProjectType (const String& newProjectType) { projectTypeValue = newProjectType; }
|
||||
|
||||
String getProjectNameString() const { return projectNameValue.get(); }
|
||||
String getProjectFilenameRootString() { return File::createLegalFileName (getDocumentTitle()); }
|
||||
String getProjectUIDString() const { return projectUIDValue.get(); }
|
||||
|
||||
String getProjectLineFeed() const { return projectLineFeedValue.get(); }
|
||||
|
||||
String getVersionString() const { return versionValue.get(); }
|
||||
String getVersionAsHex() const { return build_tools::getVersionAsHex (getVersionString()); }
|
||||
int getVersionAsHexInteger() const { return build_tools::getVersionAsHexInteger (getVersionString()); }
|
||||
void setProjectVersion (const String& newVersion) { versionValue = newVersion; }
|
||||
|
||||
String getBundleIdentifierString() const { return bundleIdentifierValue.get(); }
|
||||
String getDefaultBundleIdentifierString() const;
|
||||
String getDefaultAAXIdentifierString() const { return getDefaultBundleIdentifierString(); }
|
||||
String getDefaultPluginManufacturerString() const;
|
||||
|
||||
String getCompanyNameString() const { return companyNameValue.get(); }
|
||||
String getCompanyCopyrightString() const { return companyCopyrightValue.get(); }
|
||||
String getCompanyWebsiteString() const { return companyWebsiteValue.get(); }
|
||||
String getCompanyEmailString() const { return companyEmailValue.get(); }
|
||||
|
||||
String getHeaderSearchPathsString() const { return headerSearchPathsValue.get(); }
|
||||
|
||||
StringPairArray getPreprocessorDefs() const { return parsedPreprocessorDefs; }
|
||||
|
||||
int getMaxBinaryFileSize() const { return maxBinaryFileSizeValue.get(); }
|
||||
bool shouldIncludeBinaryInJuceHeader() const { return includeBinaryDataInJuceHeaderValue.get(); }
|
||||
String getBinaryDataNamespaceString() const { return binaryDataNamespaceValue.get(); }
|
||||
|
||||
bool shouldDisplaySplashScreen() const { return displaySplashScreenValue.get(); }
|
||||
String getSplashScreenColourString() const { return splashScreenColourValue.get(); }
|
||||
|
||||
static StringArray getCppStandardStrings() { return { "C++14", "C++17", "C++20", "Use Latest" }; }
|
||||
static Array<var> getCppStandardVars() { return { "14", "17", "20", "latest" }; }
|
||||
|
||||
static String getLatestNumberedCppStandardString()
|
||||
{
|
||||
auto cppStandardVars = getCppStandardVars();
|
||||
return cppStandardVars[cppStandardVars.size() - 2];
|
||||
}
|
||||
|
||||
String getCppStandardString() const { return cppStandardValue.get(); }
|
||||
|
||||
StringArray getCompilerFlagSchemes() const;
|
||||
void addCompilerFlagScheme (const String&);
|
||||
void removeCompilerFlagScheme (const String&);
|
||||
|
||||
String getPostExportShellCommandPosixString() const { return postExportShellCommandPosixValue.get(); }
|
||||
String getPostExportShellCommandWinString() const { return postExportShellCommandWinValue.get(); }
|
||||
|
||||
bool shouldUseAppConfig() const { return useAppConfigValue.get(); }
|
||||
bool shouldAddUsingNamespaceToJuceHeader() const { return addUsingNamespaceToJuceHeader.get(); }
|
||||
|
||||
//==============================================================================
|
||||
String getPluginNameString() const { return pluginNameValue.get(); }
|
||||
String getPluginDescriptionString() const { return pluginDescriptionValue.get();}
|
||||
String getPluginManufacturerString() const { return pluginManufacturerValue.get(); }
|
||||
String getPluginManufacturerCodeString() const { return pluginManufacturerCodeValue.get(); }
|
||||
String getPluginCodeString() const { return pluginCodeValue.get(); }
|
||||
String getPluginChannelConfigsString() const { return pluginChannelConfigsValue.get(); }
|
||||
String getAAXIdentifierString() const { return pluginAAXIdentifierValue.get(); }
|
||||
String getPluginAUExportPrefixString() const { return pluginAUExportPrefixValue.get(); }
|
||||
String getVSTNumMIDIInputsString() const { return pluginVSTNumMidiInputsValue.get(); }
|
||||
String getVSTNumMIDIOutputsString() const { return pluginVSTNumMidiOutputsValue.get(); }
|
||||
|
||||
static bool checkMultiChoiceVar (const ValueWithDefault& valueToCheck, Identifier idToCheck) noexcept
|
||||
{
|
||||
if (! valueToCheck.get().isArray())
|
||||
return false;
|
||||
|
||||
auto v = valueToCheck.get();
|
||||
|
||||
if (auto* varArray = v.getArray())
|
||||
return varArray->contains (idToCheck.toString());
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool isAudioPluginProject() const { return getProjectType().isAudioPlugin(); }
|
||||
|
||||
bool shouldBuildVST() const { return isAudioPluginProject() && checkMultiChoiceVar (pluginFormatsValue, Ids::buildVST); }
|
||||
bool shouldBuildVST3() const { return isAudioPluginProject() && checkMultiChoiceVar (pluginFormatsValue, Ids::buildVST3); }
|
||||
bool shouldBuildAU() const { return isAudioPluginProject() && checkMultiChoiceVar (pluginFormatsValue, Ids::buildAU); }
|
||||
bool shouldBuildAUv3() const { return isAudioPluginProject() && checkMultiChoiceVar (pluginFormatsValue, Ids::buildAUv3); }
|
||||
bool shouldBuildRTAS() const { return isAudioPluginProject() && checkMultiChoiceVar (pluginFormatsValue, Ids::buildRTAS); }
|
||||
bool shouldBuildAAX() const { return isAudioPluginProject() && checkMultiChoiceVar (pluginFormatsValue, Ids::buildAAX); }
|
||||
bool shouldBuildStandalonePlugin() const { return isAudioPluginProject() && checkMultiChoiceVar (pluginFormatsValue, Ids::buildStandalone); }
|
||||
bool shouldBuildUnityPlugin() const { return isAudioPluginProject() && checkMultiChoiceVar (pluginFormatsValue, Ids::buildUnity); }
|
||||
bool shouldEnableIAA() const { return isAudioPluginProject() && checkMultiChoiceVar (pluginFormatsValue, Ids::enableIAA); }
|
||||
|
||||
bool isPluginSynth() const { return checkMultiChoiceVar (pluginCharacteristicsValue, Ids::pluginIsSynth); }
|
||||
bool pluginWantsMidiInput() const { return checkMultiChoiceVar (pluginCharacteristicsValue, Ids::pluginWantsMidiIn); }
|
||||
bool pluginProducesMidiOutput() const { return checkMultiChoiceVar (pluginCharacteristicsValue, Ids::pluginProducesMidiOut); }
|
||||
bool isPluginMidiEffect() const { return checkMultiChoiceVar (pluginCharacteristicsValue, Ids::pluginIsMidiEffectPlugin); }
|
||||
bool pluginEditorNeedsKeyFocus() const { return checkMultiChoiceVar (pluginCharacteristicsValue, Ids::pluginEditorRequiresKeys); }
|
||||
bool isPluginRTASBypassDisabled() const { return checkMultiChoiceVar (pluginCharacteristicsValue, Ids::pluginRTASDisableBypass); }
|
||||
bool isPluginRTASMultiMonoDisabled() const { return checkMultiChoiceVar (pluginCharacteristicsValue, Ids::pluginRTASDisableMultiMono); }
|
||||
bool isPluginAAXBypassDisabled() const { return checkMultiChoiceVar (pluginCharacteristicsValue, Ids::pluginAAXDisableBypass); }
|
||||
bool isPluginAAXMultiMonoDisabled() const { return checkMultiChoiceVar (pluginCharacteristicsValue, Ids::pluginAAXDisableMultiMono); }
|
||||
|
||||
static StringArray getAllAUMainTypeStrings() noexcept;
|
||||
static Array<var> getAllAUMainTypeVars() noexcept;
|
||||
Array<var> getDefaultAUMainTypes() const noexcept;
|
||||
|
||||
static StringArray getAllVSTCategoryStrings() noexcept;
|
||||
Array<var> getDefaultVSTCategories() const noexcept;
|
||||
|
||||
static StringArray getAllVST3CategoryStrings() noexcept;
|
||||
Array<var> getDefaultVST3Categories() const noexcept;
|
||||
|
||||
static StringArray getAllAAXCategoryStrings() noexcept;
|
||||
static Array<var> getAllAAXCategoryVars() noexcept;
|
||||
Array<var> getDefaultAAXCategories() const noexcept;
|
||||
|
||||
static StringArray getAllRTASCategoryStrings() noexcept;
|
||||
static Array<var> getAllRTASCategoryVars() noexcept;
|
||||
Array<var> getDefaultRTASCategories() const noexcept;
|
||||
|
||||
String getAUMainTypeString() const noexcept;
|
||||
bool isAUSandBoxSafe() const noexcept;
|
||||
String getVSTCategoryString() const noexcept;
|
||||
String getVST3CategoryString() const noexcept;
|
||||
int getAAXCategory() const noexcept;
|
||||
int getRTASCategory() const noexcept;
|
||||
|
||||
String getIAATypeCode() const;
|
||||
String getIAAPluginName() const;
|
||||
|
||||
String getUnityScriptName() const { return addUnityPluginPrefixIfNecessary (getProjectNameString()) + "_UnityScript.cs"; }
|
||||
static String addUnityPluginPrefixIfNecessary (const String& name)
|
||||
{
|
||||
if (! name.startsWithIgnoreCase ("audioplugin"))
|
||||
return "audioplugin_" + name;
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
bool isAUPluginHost();
|
||||
bool isVSTPluginHost();
|
||||
bool isVST3PluginHost();
|
||||
|
||||
//==============================================================================
|
||||
bool shouldBuildTargetType (build_tools::ProjectType::Target::Type targetType) const noexcept;
|
||||
static build_tools::ProjectType::Target::Type getTargetTypeFromFilePath (const File& file, bool returnSharedTargetIfNoValidSuffix);
|
||||
|
||||
//==============================================================================
|
||||
void updateDeprecatedProjectSettingsInteractively();
|
||||
|
||||
StringPairArray getAppConfigDefs();
|
||||
StringPairArray getAudioPluginFlags() const;
|
||||
|
||||
//==============================================================================
|
||||
class Item
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
Item (Project& project, const ValueTree& itemNode, bool isModuleCode);
|
||||
Item (const Item& other);
|
||||
|
||||
static Item createGroup (Project& project, const String& name, const String& uid, bool isModuleCode);
|
||||
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;
|
||||
bool isSourceFile() const;
|
||||
|
||||
String getID() const;
|
||||
void setID (const String& newID);
|
||||
Item findItemWithID (const String& targetId) const; // (recursive search)
|
||||
|
||||
String getImageFileID() const;
|
||||
std::unique_ptr<Drawable> loadAsImageFile() const;
|
||||
|
||||
//==============================================================================
|
||||
Value getNameValue();
|
||||
String getName() const;
|
||||
File getFile() const;
|
||||
String getFilePath() const;
|
||||
void setFile (const File& file);
|
||||
void setFile (const build_tools::RelativePath& file);
|
||||
File determineGroupFolder() const;
|
||||
bool renameFile (const File& newFile);
|
||||
|
||||
bool shouldBeAddedToTargetProject() const;
|
||||
bool shouldBeAddedToTargetExporter (const ProjectExporter&) const;
|
||||
bool shouldBeCompiled() const;
|
||||
Value getShouldCompileValue();
|
||||
|
||||
bool shouldBeAddedToBinaryResources() const;
|
||||
Value getShouldAddToBinaryResourcesValue();
|
||||
|
||||
bool shouldBeAddedToXcodeResources() const;
|
||||
Value getShouldAddToXcodeResourcesValue();
|
||||
|
||||
Value getShouldInhibitWarningsValue();
|
||||
bool shouldInhibitWarnings() const;
|
||||
|
||||
bool isModuleCode() const;
|
||||
|
||||
Value getShouldSkipPCHValue();
|
||||
bool shouldSkipPCH() const;
|
||||
|
||||
Value getCompilerFlagSchemeValue();
|
||||
String getCompilerFlagSchemeString() const;
|
||||
|
||||
void setCompilerFlagScheme (const String&);
|
||||
void clearCurrentCompilerFlagScheme();
|
||||
|
||||
//==============================================================================
|
||||
bool canContain (const Item& child) const;
|
||||
int getNumChildren() const { return state.getNumChildren(); }
|
||||
Item getChild (int index) const { return Item (project, state.getChild (index), belongsToModule); }
|
||||
|
||||
Item addNewSubGroup (const String& name, int insertIndex);
|
||||
Item getOrCreateSubGroup (const String& name);
|
||||
void addChild (const Item& newChild, int insertIndex);
|
||||
bool addFileAtIndex (const File& file, int insertIndex, bool shouldCompile);
|
||||
bool addFileRetainingSortOrder (const File& file, bool shouldCompile);
|
||||
void addFileUnchecked (const File& file, int insertIndex, bool shouldCompile);
|
||||
bool addRelativeFile (const build_tools::RelativePath& file, int insertIndex, bool shouldCompile);
|
||||
void removeItemFromProject();
|
||||
void sortAlphabetically (bool keepGroupsAtStart, bool recursive);
|
||||
Item findItemForFile (const File& file) const;
|
||||
bool containsChildForFile (const build_tools::RelativePath& file) const;
|
||||
|
||||
Item getParent() const;
|
||||
Item createCopy();
|
||||
|
||||
UndoManager* getUndoManager() const { return project.getUndoManagerFor (state); }
|
||||
|
||||
Icon getIcon (bool isOpen = false) const;
|
||||
bool isIconCrossedOut() const;
|
||||
|
||||
bool needsSaving() const noexcept;
|
||||
|
||||
Project& project;
|
||||
ValueTree state;
|
||||
|
||||
private:
|
||||
Item& operator= (const Item&);
|
||||
bool belongsToModule;
|
||||
};
|
||||
|
||||
Item getMainGroup();
|
||||
|
||||
void findAllImageItems (OwnedArray<Item>& items);
|
||||
|
||||
//==============================================================================
|
||||
ValueTree getExporters();
|
||||
int getNumExporters();
|
||||
std::unique_ptr<ProjectExporter> createExporter (int index);
|
||||
void addNewExporter (const Identifier& exporterIdentifier);
|
||||
void createExporterForCurrentPlatform();
|
||||
|
||||
struct ExporterIterator
|
||||
{
|
||||
ExporterIterator (Project& project);
|
||||
|
||||
bool next();
|
||||
|
||||
ProjectExporter& operator*() const { return *exporter; }
|
||||
ProjectExporter* operator->() const { return exporter.get(); }
|
||||
|
||||
std::unique_ptr<ProjectExporter> exporter;
|
||||
int index;
|
||||
|
||||
private:
|
||||
Project& project;
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ExporterIterator)
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
struct ConfigFlag
|
||||
{
|
||||
String symbol, description, sourceModuleID;
|
||||
ValueWithDefault value;
|
||||
};
|
||||
|
||||
ValueWithDefault getConfigFlag (const String& name);
|
||||
bool isConfigFlagEnabled (const String& name, bool defaultIsEnabled = false) const;
|
||||
|
||||
//==============================================================================
|
||||
EnabledModulesList& getEnabledModules();
|
||||
|
||||
AvailableModulesList& getExporterPathsModulesList() { return exporterPathsModulesList; }
|
||||
void rescanExporterPathModules (bool async = false);
|
||||
|
||||
std::pair<String, File> getModuleWithID (const String&);
|
||||
|
||||
//==============================================================================
|
||||
PropertiesFile& getStoredProperties() const;
|
||||
|
||||
//==============================================================================
|
||||
UndoManager* getUndoManagerFor (const ValueTree&) const { return nullptr; }
|
||||
UndoManager* getUndoManager() const { return nullptr; }
|
||||
|
||||
//==============================================================================
|
||||
static const char* projectFileExtension;
|
||||
|
||||
//==============================================================================
|
||||
bool updateCachedFileState();
|
||||
String getCachedFileStateContent() const noexcept { return cachedFileState.second; }
|
||||
|
||||
String serialiseProjectXml (std::unique_ptr<XmlElement>) const;
|
||||
|
||||
//==============================================================================
|
||||
String getUniqueTargetFolderSuffixForExporter (const Identifier& exporterIdentifier, const String& baseTargetFolder);
|
||||
|
||||
//==============================================================================
|
||||
bool isCurrentlySaving() const noexcept { return saver != nullptr; }
|
||||
|
||||
bool isTemporaryProject() const noexcept { return tempDirectory != File(); }
|
||||
File getTemporaryDirectory() const noexcept { return tempDirectory; }
|
||||
void setTemporaryDirectory (const File&) noexcept;
|
||||
|
||||
//==============================================================================
|
||||
ValueTree getProjectMessages() const { return projectMessages; }
|
||||
|
||||
void addProjectMessage (const Identifier& messageToAdd, std::vector<ProjectMessages::MessageAction>&& messageActions);
|
||||
void removeProjectMessage (const Identifier& messageToRemove);
|
||||
|
||||
std::vector<ProjectMessages::MessageAction> getMessageActions (const Identifier& message);
|
||||
|
||||
//==============================================================================
|
||||
bool hasIncompatibleLicenseTypeAndSplashScreenSetting() const;
|
||||
bool isFileModificationCheckPending() const;
|
||||
bool isSaveAndExportDisabled() const;
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
void valueTreePropertyChanged (ValueTree&, const Identifier&) override;
|
||||
void valueTreeChildAdded (ValueTree&, ValueTree&) override;
|
||||
void valueTreeChildRemoved (ValueTree&, ValueTree&, int) override;
|
||||
void valueTreeChildOrderChanged (ValueTree&, int, int) override;
|
||||
|
||||
//==============================================================================
|
||||
struct ProjectFileModificationPoller : private Timer
|
||||
{
|
||||
ProjectFileModificationPoller (Project& p);
|
||||
bool isCheckPending() const noexcept { return pending; }
|
||||
|
||||
private:
|
||||
void timerCallback() override;
|
||||
void reset();
|
||||
|
||||
void resaveProject();
|
||||
void reloadProjectFromDisk();
|
||||
|
||||
Project& project;
|
||||
bool pending = false;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
ValueTree projectRoot { Ids::JUCERPROJECT };
|
||||
|
||||
ValueWithDefault projectNameValue, projectUIDValue, projectLineFeedValue, projectTypeValue, versionValue, bundleIdentifierValue, companyNameValue,
|
||||
companyCopyrightValue, companyWebsiteValue, companyEmailValue, displaySplashScreenValue, splashScreenColourValue, cppStandardValue,
|
||||
headerSearchPathsValue, preprocessorDefsValue, userNotesValue, maxBinaryFileSizeValue, includeBinaryDataInJuceHeaderValue, binaryDataNamespaceValue,
|
||||
compilerFlagSchemesValue, postExportShellCommandPosixValue, postExportShellCommandWinValue, useAppConfigValue, addUsingNamespaceToJuceHeader;
|
||||
|
||||
ValueWithDefault pluginFormatsValue, pluginNameValue, pluginDescriptionValue, pluginManufacturerValue, pluginManufacturerCodeValue,
|
||||
pluginCodeValue, pluginChannelConfigsValue, pluginCharacteristicsValue, pluginAUExportPrefixValue, pluginAAXIdentifierValue,
|
||||
pluginAUMainTypeValue, pluginAUSandboxSafeValue, pluginRTASCategoryValue, pluginVSTCategoryValue, pluginVST3CategoryValue, pluginAAXCategoryValue,
|
||||
pluginVSTNumMidiInputsValue, pluginVSTNumMidiOutputsValue;
|
||||
|
||||
//==============================================================================
|
||||
std::unique_ptr<EnabledModulesList> enabledModulesList;
|
||||
|
||||
AvailableModulesList exporterPathsModulesList;
|
||||
|
||||
//==============================================================================
|
||||
void updateDeprecatedProjectSettings();
|
||||
|
||||
//==============================================================================
|
||||
bool shouldWriteLegacyPluginFormatSettings = false;
|
||||
bool shouldWriteLegacyPluginCharacteristicsSettings = false;
|
||||
|
||||
static Array<Identifier> getLegacyPluginFormatIdentifiers() noexcept;
|
||||
static Array<Identifier> getLegacyPluginCharacteristicsIdentifiers() noexcept;
|
||||
|
||||
void writeLegacyPluginFormatSettings();
|
||||
void writeLegacyPluginCharacteristicsSettings();
|
||||
|
||||
void coalescePluginFormatValues();
|
||||
void coalescePluginCharacteristicsValues();
|
||||
void updatePluginCategories();
|
||||
|
||||
//==============================================================================
|
||||
File tempDirectory;
|
||||
std::pair<Time, String> cachedFileState;
|
||||
|
||||
void saveAndMoveTemporaryProject (bool openInIDE);
|
||||
|
||||
//==============================================================================
|
||||
friend class Item;
|
||||
StringPairArray parsedPreprocessorDefs;
|
||||
|
||||
//==============================================================================
|
||||
void initialiseProjectValues();
|
||||
void initialiseMainGroup();
|
||||
void initialiseAudioPluginValues();
|
||||
|
||||
bool setCppVersionFromOldExporterSettings();
|
||||
|
||||
void createAudioPluginPropertyEditors (PropertyListBuilder& props);
|
||||
|
||||
//==============================================================================
|
||||
void updateTitleDependencies();
|
||||
void updateCompanyNameDependencies();
|
||||
void updateProjectSettings();
|
||||
ValueTree getConfigurations() const;
|
||||
ValueTree getConfigNode();
|
||||
|
||||
void updateOldStyleConfigList();
|
||||
void moveOldPropertyFromProjectToAllExporters (Identifier name);
|
||||
void removeDefunctExporters();
|
||||
void updateOldModulePaths();
|
||||
|
||||
//==============================================================================
|
||||
void licenseStateChanged() override;
|
||||
void changeListenerCallback (ChangeBroadcaster*) override;
|
||||
void availableModulesChanged (AvailableModulesList*) override;
|
||||
|
||||
void updateLicenseWarning();
|
||||
void updateJUCEPathWarning();
|
||||
|
||||
void updateModuleWarnings();
|
||||
void updateExporterWarnings();
|
||||
void updateCppStandardWarning (bool showWarning);
|
||||
void updateMissingModuleDependenciesWarning (bool showWarning);
|
||||
void updateOldProjucerWarning (bool showWarning);
|
||||
void updateCLionWarning (bool showWarning);
|
||||
void updateModuleNotFoundWarning (bool showWarning);
|
||||
|
||||
ValueTree projectMessages { ProjectMessages::Ids::projectMessages, {},
|
||||
{ { ProjectMessages::Ids::notification, {} }, { ProjectMessages::Ids::warning, {} } } };
|
||||
std::map<Identifier, std::vector<ProjectMessages::MessageAction>> messageActions;
|
||||
|
||||
ProjectFileModificationPoller fileModificationPoller { *this };
|
||||
|
||||
std::unique_ptr<FileChooser> chooser;
|
||||
std::unique_ptr<ProjectSaver> saver;
|
||||
|
||||
//==============================================================================
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Project)
|
||||
JUCE_DECLARE_WEAK_REFERENCEABLE (Project)
|
||||
};
|
Reference in New Issue
Block a user