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:
107
deps/juce/modules/juce_data_structures/app_properties/juce_ApplicationProperties.cpp
vendored
Normal file
107
deps/juce/modules/juce_data_structures/app_properties/juce_ApplicationProperties.cpp
vendored
Normal file
@ -0,0 +1,107 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
ApplicationProperties::ApplicationProperties()
|
||||
{
|
||||
}
|
||||
|
||||
ApplicationProperties::~ApplicationProperties()
|
||||
{
|
||||
closeFiles();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void ApplicationProperties::setStorageParameters (const PropertiesFile::Options& newOptions)
|
||||
{
|
||||
options = newOptions;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void ApplicationProperties::openFiles()
|
||||
{
|
||||
// You need to call setStorageParameters() before trying to get hold of the properties!
|
||||
jassert (options.applicationName.isNotEmpty());
|
||||
|
||||
if (options.applicationName.isNotEmpty())
|
||||
{
|
||||
PropertiesFile::Options o (options);
|
||||
|
||||
if (userProps == nullptr)
|
||||
{
|
||||
o.commonToAllUsers = false;
|
||||
userProps.reset (new PropertiesFile (o));
|
||||
}
|
||||
|
||||
if (commonProps == nullptr)
|
||||
{
|
||||
o.commonToAllUsers = true;
|
||||
commonProps.reset (new PropertiesFile (o));
|
||||
}
|
||||
|
||||
userProps->setFallbackPropertySet (commonProps.get());
|
||||
}
|
||||
}
|
||||
|
||||
PropertiesFile* ApplicationProperties::getUserSettings()
|
||||
{
|
||||
if (userProps == nullptr)
|
||||
openFiles();
|
||||
|
||||
return userProps.get();
|
||||
}
|
||||
|
||||
PropertiesFile* ApplicationProperties::getCommonSettings (const bool returnUserPropsIfReadOnly)
|
||||
{
|
||||
if (commonProps == nullptr)
|
||||
openFiles();
|
||||
|
||||
if (returnUserPropsIfReadOnly)
|
||||
{
|
||||
if (commonSettingsAreReadOnly == 0)
|
||||
commonSettingsAreReadOnly = commonProps->save() ? -1 : 1;
|
||||
|
||||
if (commonSettingsAreReadOnly > 0)
|
||||
return userProps.get();
|
||||
}
|
||||
|
||||
return commonProps.get();
|
||||
}
|
||||
|
||||
bool ApplicationProperties::saveIfNeeded()
|
||||
{
|
||||
return (userProps == nullptr || userProps->saveIfNeeded())
|
||||
&& (commonProps == nullptr || commonProps->saveIfNeeded());
|
||||
}
|
||||
|
||||
void ApplicationProperties::closeFiles()
|
||||
{
|
||||
userProps.reset();
|
||||
commonProps.reset();
|
||||
}
|
||||
|
||||
} // namespace juce
|
133
deps/juce/modules/juce_data_structures/app_properties/juce_ApplicationProperties.h
vendored
Normal file
133
deps/juce/modules/juce_data_structures/app_properties/juce_ApplicationProperties.h
vendored
Normal file
@ -0,0 +1,133 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Manages a collection of properties.
|
||||
|
||||
This is a slightly higher-level wrapper for managing PropertiesFile objects.
|
||||
|
||||
It holds two different PropertiesFile objects internally, one for user-specific
|
||||
settings (stored in your user directory), and one for settings that are common to
|
||||
all users (stored in a folder accessible to all users).
|
||||
|
||||
The class manages the creation of these files on-demand, allowing access via the
|
||||
getUserSettings() and getCommonSettings() methods.
|
||||
|
||||
After creating an instance of an ApplicationProperties object, you should first
|
||||
of all call setStorageParameters() to tell it the parameters to use to create
|
||||
its files.
|
||||
|
||||
@see PropertiesFile
|
||||
|
||||
@tags{DataStructures}
|
||||
*/
|
||||
class JUCE_API ApplicationProperties
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/**
|
||||
Creates an ApplicationProperties object.
|
||||
|
||||
Before using it, you must call setStorageParameters() to give it the info
|
||||
it needs to create the property files.
|
||||
*/
|
||||
ApplicationProperties();
|
||||
|
||||
/** Destructor. */
|
||||
~ApplicationProperties();
|
||||
|
||||
//==============================================================================
|
||||
/** Gives the object the information it needs to create the appropriate properties files.
|
||||
See the PropertiesFile::Options class for details about what options you need to set.
|
||||
*/
|
||||
void setStorageParameters (const PropertiesFile::Options& options);
|
||||
|
||||
/** Returns the current storage parameters.
|
||||
@see setStorageParameters
|
||||
*/
|
||||
const PropertiesFile::Options& getStorageParameters() const noexcept { return options; }
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the user settings file.
|
||||
|
||||
The first time this is called, it will create and load the properties file.
|
||||
|
||||
Note that when you search the user PropertiesFile for a value that it doesn't contain,
|
||||
the common settings are used as a second-chance place to look. This is done via the
|
||||
PropertySet::setFallbackPropertySet() method - by default the common settings are set
|
||||
to the fallback for the user settings.
|
||||
|
||||
@see getCommonSettings
|
||||
*/
|
||||
PropertiesFile* getUserSettings();
|
||||
|
||||
/** Returns the common settings file.
|
||||
|
||||
The first time this is called, it will create and load the properties file.
|
||||
|
||||
@param returnUserPropsIfReadOnly if this is true, and the common properties file is
|
||||
read-only (e.g. because the user doesn't have permission to write
|
||||
to shared files), then this will return the user settings instead,
|
||||
(like getUserSettings() would do). This is handy if you'd like to
|
||||
write a value to the common settings, but if that's no possible,
|
||||
then you'd rather write to the user settings than none at all.
|
||||
If returnUserPropsIfReadOnly is false, this method will always return
|
||||
the common settings, even if any changes to them can't be saved.
|
||||
@see getUserSettings
|
||||
*/
|
||||
PropertiesFile* getCommonSettings (bool returnUserPropsIfReadOnly);
|
||||
|
||||
//==============================================================================
|
||||
/** Saves both files if they need to be saved.
|
||||
|
||||
@see PropertiesFile::saveIfNeeded
|
||||
*/
|
||||
bool saveIfNeeded();
|
||||
|
||||
/** Flushes and closes both files if they are open.
|
||||
|
||||
This flushes any pending changes to disk with PropertiesFile::saveIfNeeded()
|
||||
and closes both files. They will then be re-opened the next time getUserSettings()
|
||||
or getCommonSettings() is called.
|
||||
*/
|
||||
void closeFiles();
|
||||
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
PropertiesFile::Options options;
|
||||
std::unique_ptr<PropertiesFile> userProps, commonProps;
|
||||
int commonSettingsAreReadOnly = 0;
|
||||
|
||||
void openFiles();
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ApplicationProperties)
|
||||
};
|
||||
|
||||
} // namespace juce
|
358
deps/juce/modules/juce_data_structures/app_properties/juce_PropertiesFile.cpp
vendored
Normal file
358
deps/juce/modules/juce_data_structures/app_properties/juce_PropertiesFile.cpp
vendored
Normal file
@ -0,0 +1,358 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
namespace PropertyFileConstants
|
||||
{
|
||||
constexpr static const int magicNumber = (int) ByteOrder::makeInt ('P', 'R', 'O', 'P');
|
||||
constexpr static const int magicNumberCompressed = (int) ByteOrder::makeInt ('C', 'P', 'R', 'P');
|
||||
|
||||
constexpr static const char* const fileTag = "PROPERTIES";
|
||||
constexpr static const char* const valueTag = "VALUE";
|
||||
constexpr static const char* const nameAttribute = "name";
|
||||
constexpr static const char* const valueAttribute = "val";
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
PropertiesFile::Options::Options()
|
||||
: commonToAllUsers (false),
|
||||
ignoreCaseOfKeyNames (false),
|
||||
doNotSave (false),
|
||||
millisecondsBeforeSaving (3000),
|
||||
storageFormat (PropertiesFile::storeAsXML),
|
||||
processLock (nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
File PropertiesFile::Options::getDefaultFile() const
|
||||
{
|
||||
// mustn't have illegal characters in this name..
|
||||
jassert (applicationName == File::createLegalFileName (applicationName));
|
||||
|
||||
#if JUCE_MAC || JUCE_IOS
|
||||
File dir (commonToAllUsers ? "/Library/"
|
||||
: "~/Library/");
|
||||
|
||||
if (osxLibrarySubFolder != "Preferences"
|
||||
&& ! osxLibrarySubFolder.startsWith ("Application Support")
|
||||
&& ! osxLibrarySubFolder.startsWith ("Containers"))
|
||||
{
|
||||
/* The PropertiesFile class always used to put its settings files in "Library/Preferences", but Apple
|
||||
have changed their advice, and now stipulate that settings should go in "Library/Application Support",
|
||||
or Library/Containers/[app_bundle_id] for a sandboxed app.
|
||||
|
||||
Because older apps would be broken by a silent change in this class's behaviour, you must now
|
||||
explicitly set the osxLibrarySubFolder value to indicate which path you want to use.
|
||||
|
||||
In newer apps, you should always set this to "Application Support"
|
||||
or "Application Support/YourSubFolderName".
|
||||
|
||||
If your app needs to load settings files that were created by older versions of juce and
|
||||
you want to maintain backwards-compatibility, then you can set this to "Preferences".
|
||||
But.. for better Apple-compliance, the recommended approach would be to write some code that
|
||||
finds your old settings files in ~/Library/Preferences, moves them to ~/Library/Application Support,
|
||||
and then uses the new path.
|
||||
*/
|
||||
jassertfalse;
|
||||
|
||||
dir = dir.getChildFile ("Application Support");
|
||||
}
|
||||
else
|
||||
{
|
||||
dir = dir.getChildFile (osxLibrarySubFolder);
|
||||
}
|
||||
|
||||
if (folderName.isNotEmpty())
|
||||
dir = dir.getChildFile (folderName);
|
||||
|
||||
#elif JUCE_LINUX || JUCE_BSD || JUCE_ANDROID
|
||||
auto dir = File (commonToAllUsers ? "/var" : "~")
|
||||
.getChildFile (folderName.isNotEmpty() ? folderName
|
||||
: ("." + applicationName));
|
||||
|
||||
#elif JUCE_WINDOWS
|
||||
auto dir = File::getSpecialLocation (commonToAllUsers ? File::commonApplicationDataDirectory
|
||||
: File::userApplicationDataDirectory);
|
||||
|
||||
if (dir == File())
|
||||
return {};
|
||||
|
||||
dir = dir.getChildFile (folderName.isNotEmpty() ? folderName
|
||||
: applicationName);
|
||||
#endif
|
||||
|
||||
return (filenameSuffix.startsWithChar (L'.')
|
||||
? dir.getChildFile (applicationName).withFileExtension (filenameSuffix)
|
||||
: dir.getChildFile (applicationName + "." + filenameSuffix));
|
||||
}
|
||||
|
||||
|
||||
//==============================================================================
|
||||
PropertiesFile::PropertiesFile (const File& f, const Options& o)
|
||||
: PropertySet (o.ignoreCaseOfKeyNames),
|
||||
file (f), options (o)
|
||||
{
|
||||
reload();
|
||||
}
|
||||
|
||||
PropertiesFile::PropertiesFile (const Options& o)
|
||||
: PropertySet (o.ignoreCaseOfKeyNames),
|
||||
file (o.getDefaultFile()), options (o)
|
||||
{
|
||||
reload();
|
||||
}
|
||||
|
||||
bool PropertiesFile::reload()
|
||||
{
|
||||
ProcessScopedLock pl (createProcessLock());
|
||||
|
||||
if (pl != nullptr && ! pl->isLocked())
|
||||
return false; // locking failure..
|
||||
|
||||
loadedOk = (! file.exists()) || loadAsBinary() || loadAsXml();
|
||||
return loadedOk;
|
||||
}
|
||||
|
||||
PropertiesFile::~PropertiesFile()
|
||||
{
|
||||
saveIfNeeded();
|
||||
}
|
||||
|
||||
InterProcessLock::ScopedLockType* PropertiesFile::createProcessLock() const
|
||||
{
|
||||
return options.processLock != nullptr ? new InterProcessLock::ScopedLockType (*options.processLock) : nullptr;
|
||||
}
|
||||
|
||||
bool PropertiesFile::saveIfNeeded()
|
||||
{
|
||||
const ScopedLock sl (getLock());
|
||||
return (! needsWriting) || save();
|
||||
}
|
||||
|
||||
bool PropertiesFile::needsToBeSaved() const
|
||||
{
|
||||
const ScopedLock sl (getLock());
|
||||
return needsWriting;
|
||||
}
|
||||
|
||||
void PropertiesFile::setNeedsToBeSaved (const bool needsToBeSaved_)
|
||||
{
|
||||
const ScopedLock sl (getLock());
|
||||
needsWriting = needsToBeSaved_;
|
||||
}
|
||||
|
||||
bool PropertiesFile::save()
|
||||
{
|
||||
const ScopedLock sl (getLock());
|
||||
|
||||
stopTimer();
|
||||
|
||||
if (options.doNotSave
|
||||
|| file == File()
|
||||
|| file.isDirectory()
|
||||
|| ! file.getParentDirectory().createDirectory())
|
||||
return false;
|
||||
|
||||
if (options.storageFormat == storeAsXML)
|
||||
return saveAsXml();
|
||||
|
||||
return saveAsBinary();
|
||||
}
|
||||
|
||||
bool PropertiesFile::loadAsXml()
|
||||
{
|
||||
if (auto doc = parseXMLIfTagMatches (file, PropertyFileConstants::fileTag))
|
||||
{
|
||||
for (auto* e : doc->getChildWithTagNameIterator (PropertyFileConstants::valueTag))
|
||||
{
|
||||
auto name = e->getStringAttribute (PropertyFileConstants::nameAttribute);
|
||||
|
||||
if (name.isNotEmpty())
|
||||
getAllProperties().set (name,
|
||||
e->getFirstChildElement() != nullptr
|
||||
? e->getFirstChildElement()->toString (XmlElement::TextFormat().singleLine().withoutHeader())
|
||||
: e->getStringAttribute (PropertyFileConstants::valueAttribute));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool PropertiesFile::saveAsXml()
|
||||
{
|
||||
XmlElement doc (PropertyFileConstants::fileTag);
|
||||
auto& props = getAllProperties();
|
||||
|
||||
for (int i = 0; i < props.size(); ++i)
|
||||
{
|
||||
auto* e = doc.createNewChildElement (PropertyFileConstants::valueTag);
|
||||
e->setAttribute (PropertyFileConstants::nameAttribute, props.getAllKeys() [i]);
|
||||
|
||||
// if the value seems to contain xml, store it as such..
|
||||
if (auto childElement = parseXML (props.getAllValues() [i]))
|
||||
e->addChildElement (childElement.release());
|
||||
else
|
||||
e->setAttribute (PropertyFileConstants::valueAttribute, props.getAllValues() [i]);
|
||||
}
|
||||
|
||||
ProcessScopedLock pl (createProcessLock());
|
||||
|
||||
if (pl != nullptr && ! pl->isLocked())
|
||||
return false; // locking failure..
|
||||
|
||||
if (doc.writeTo (file, {}))
|
||||
{
|
||||
needsWriting = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool PropertiesFile::loadAsBinary()
|
||||
{
|
||||
FileInputStream fileStream (file);
|
||||
|
||||
if (fileStream.openedOk())
|
||||
{
|
||||
auto magicNumber = fileStream.readInt();
|
||||
|
||||
if (magicNumber == PropertyFileConstants::magicNumberCompressed)
|
||||
{
|
||||
SubregionStream subStream (&fileStream, 4, -1, false);
|
||||
GZIPDecompressorInputStream gzip (subStream);
|
||||
return loadAsBinary (gzip);
|
||||
}
|
||||
|
||||
if (magicNumber == PropertyFileConstants::magicNumber)
|
||||
return loadAsBinary (fileStream);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool PropertiesFile::loadAsBinary (InputStream& input)
|
||||
{
|
||||
BufferedInputStream in (input, 2048);
|
||||
|
||||
int numValues = in.readInt();
|
||||
|
||||
while (--numValues >= 0 && ! in.isExhausted())
|
||||
{
|
||||
auto key = in.readString();
|
||||
auto value = in.readString();
|
||||
jassert (key.isNotEmpty());
|
||||
|
||||
if (key.isNotEmpty())
|
||||
getAllProperties().set (key, value);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PropertiesFile::saveAsBinary()
|
||||
{
|
||||
ProcessScopedLock pl (createProcessLock());
|
||||
|
||||
if (pl != nullptr && ! pl->isLocked())
|
||||
return false; // locking failure..
|
||||
|
||||
TemporaryFile tempFile (file);
|
||||
|
||||
{
|
||||
FileOutputStream out (tempFile.getFile());
|
||||
|
||||
if (! out.openedOk())
|
||||
return false;
|
||||
|
||||
if (options.storageFormat == storeAsCompressedBinary)
|
||||
{
|
||||
out.writeInt (PropertyFileConstants::magicNumberCompressed);
|
||||
out.flush();
|
||||
|
||||
GZIPCompressorOutputStream zipped (out, 9);
|
||||
|
||||
if (! writeToStream (zipped))
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
// have you set up the storage option flags correctly?
|
||||
jassert (options.storageFormat == storeAsBinary);
|
||||
|
||||
out.writeInt (PropertyFileConstants::magicNumber);
|
||||
|
||||
if (! writeToStream (out))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (! tempFile.overwriteTargetFileWithTemporary())
|
||||
return false;
|
||||
|
||||
needsWriting = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PropertiesFile::writeToStream (OutputStream& out)
|
||||
{
|
||||
auto& props = getAllProperties();
|
||||
auto& keys = props.getAllKeys();
|
||||
auto& values = props.getAllValues();
|
||||
auto numProperties = props.size();
|
||||
|
||||
if (! out.writeInt (numProperties))
|
||||
return false;
|
||||
|
||||
for (int i = 0; i < numProperties; ++i)
|
||||
{
|
||||
if (! out.writeString (keys[i])) return false;
|
||||
if (! out.writeString (values[i])) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void PropertiesFile::timerCallback()
|
||||
{
|
||||
saveIfNeeded();
|
||||
}
|
||||
|
||||
void PropertiesFile::propertyChanged()
|
||||
{
|
||||
sendChangeMessage();
|
||||
needsWriting = true;
|
||||
|
||||
if (options.millisecondsBeforeSaving > 0)
|
||||
startTimer (options.millisecondsBeforeSaving);
|
||||
else if (options.millisecondsBeforeSaving == 0)
|
||||
saveIfNeeded();
|
||||
}
|
||||
|
||||
} // namespace juce
|
255
deps/juce/modules/juce_data_structures/app_properties/juce_PropertiesFile.h
vendored
Normal file
255
deps/juce/modules/juce_data_structures/app_properties/juce_PropertiesFile.h
vendored
Normal file
@ -0,0 +1,255 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/** Wrapper on a file that stores a list of key/value data pairs.
|
||||
|
||||
Useful for storing application settings, etc. See the PropertySet class for
|
||||
the interfaces that read and write values.
|
||||
|
||||
Not designed for very large amounts of data, as it keeps all the values in
|
||||
memory and writes them out to disk lazily when they are changed.
|
||||
|
||||
Because this class derives from ChangeBroadcaster, ChangeListeners can be registered
|
||||
with it, and these will be signalled when a value changes.
|
||||
|
||||
@see PropertySet
|
||||
|
||||
@tags{DataStructures}
|
||||
*/
|
||||
class JUCE_API PropertiesFile : public PropertySet,
|
||||
public ChangeBroadcaster,
|
||||
private Timer
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
enum StorageFormat
|
||||
{
|
||||
storeAsBinary,
|
||||
storeAsCompressedBinary,
|
||||
storeAsXML
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/** Structure describing properties file options */
|
||||
struct JUCE_API Options
|
||||
{
|
||||
/** Creates an empty Options structure.
|
||||
You'll need to fill-in the data members appropriately before using this structure.
|
||||
*/
|
||||
Options();
|
||||
|
||||
/** The name of your application - this is used to help generate the path and filename
|
||||
at which the properties file will be stored. */
|
||||
String applicationName;
|
||||
|
||||
/** The suffix to use for your properties file.
|
||||
It doesn't really matter what this is - you may want to use ".settings" or
|
||||
".properties" or something. If the suffix includes the prefixing dot (for example
|
||||
".settings") then the suffix of applicationName will be replaced with your suffix
|
||||
("MyApp.exe" -> "MyApp.settings"). If your filenameSuffix does NOT include the dot,
|
||||
then the suffix will be appended to the applicationName ("MyApp.exe" ->
|
||||
"MyApp.exe.settings").
|
||||
*/
|
||||
String filenameSuffix;
|
||||
|
||||
/** The name of a subfolder in which you'd like your properties file to live.
|
||||
See the getDefaultFile() method for more details about how this is used.
|
||||
*/
|
||||
String folderName;
|
||||
|
||||
/** If you're using properties files on a Mac, you must set this value - failure to
|
||||
do so will cause a runtime assertion.
|
||||
|
||||
The PropertiesFile class always used to put its settings files in "Library/Preferences", but Apple
|
||||
have changed their advice, and now stipulate that settings should go in "Library/Application Support".
|
||||
|
||||
Because older apps would be broken by a silent change in this class's behaviour, you must now
|
||||
explicitly set the osxLibrarySubFolder value to indicate which path you want to use.
|
||||
|
||||
In newer apps, you should always set this to "Application Support" or
|
||||
"Application Support/YourSubFolderName".
|
||||
|
||||
If your app needs to load settings files that were created by older versions of juce and
|
||||
you want to maintain backwards-compatibility, then you can set this to "Preferences".
|
||||
But.. for better Apple-compliance, the recommended approach would be to write some code that
|
||||
finds your old settings files in ~/Library/Preferences, moves them to ~/Library/Application Support,
|
||||
and then uses the new path.
|
||||
*/
|
||||
String osxLibrarySubFolder;
|
||||
|
||||
/** If true, the file will be created in a location that's shared between users.
|
||||
The default constructor initialises this value to false.
|
||||
*/
|
||||
bool commonToAllUsers;
|
||||
|
||||
/** If true, this means that property names are matched in a case-insensitive manner.
|
||||
See the PropertySet constructor for more info.
|
||||
The default constructor initialises this value to false.
|
||||
*/
|
||||
bool ignoreCaseOfKeyNames;
|
||||
|
||||
/** If set to true, this prevents the file from being written to disk. */
|
||||
bool doNotSave;
|
||||
|
||||
/** If this is zero or greater, then after a value is changed, the object will wait
|
||||
for this amount of time and then save the file. If this zero, the file will be
|
||||
written to disk immediately on being changed (which might be slow, as it'll re-write
|
||||
synchronously each time a value-change method is called). If it is less than zero,
|
||||
the file won't be saved until save() or saveIfNeeded() are explicitly called.
|
||||
The default constructor sets this to a reasonable value of a few seconds, so you
|
||||
only need to change it if you need a special case.
|
||||
*/
|
||||
int millisecondsBeforeSaving;
|
||||
|
||||
/** Specifies whether the file should be written as XML, binary, etc.
|
||||
The default constructor sets this to storeAsXML, so you only need to set it explicitly
|
||||
if you want to use a different format.
|
||||
*/
|
||||
StorageFormat storageFormat;
|
||||
|
||||
/** An optional InterprocessLock object that will be used to prevent multiple threads or
|
||||
processes from writing to the file at the same time. The PropertiesFile will keep a
|
||||
pointer to this object but will not take ownership of it - the caller is responsible for
|
||||
making sure that the lock doesn't get deleted before the PropertiesFile has been deleted.
|
||||
The default constructor initialises this value to nullptr, so you don't need to touch it
|
||||
unless you want to use a lock.
|
||||
*/
|
||||
InterProcessLock* processLock;
|
||||
|
||||
/** This can be called to suggest a file that should be used, based on the values
|
||||
in this structure.
|
||||
|
||||
So on a Mac, this will return a file called:
|
||||
~/Library/[osxLibrarySubFolder]/[folderName]/[applicationName].[filenameSuffix]
|
||||
|
||||
On Windows it'll return something like:
|
||||
C:\\Documents and Settings\\username\\Application Data\\[folderName]\\[applicationName].[filenameSuffix]
|
||||
|
||||
On Linux it'll return
|
||||
~/[folderName]/[applicationName].[filenameSuffix]
|
||||
|
||||
If the folderName variable is empty, it'll use the app name for this (or omit the
|
||||
folder name on the Mac).
|
||||
|
||||
The paths will also vary depending on whether commonToAllUsers is true.
|
||||
*/
|
||||
File getDefaultFile() const;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/** Creates a PropertiesFile object.
|
||||
The file used will be chosen by calling PropertiesFile::Options::getDefaultFile()
|
||||
for the options provided. To set the file explicitly, use the other constructor.
|
||||
*/
|
||||
explicit PropertiesFile (const Options& options);
|
||||
|
||||
/** Creates a PropertiesFile object.
|
||||
Unlike the other constructor, this one allows you to explicitly set the file that you
|
||||
want to be used, rather than using the default one.
|
||||
*/
|
||||
PropertiesFile (const File& file,
|
||||
const Options& options);
|
||||
|
||||
/** Destructor.
|
||||
When deleted, the file will first call saveIfNeeded() to flush any changes to disk.
|
||||
*/
|
||||
~PropertiesFile() override;
|
||||
|
||||
//==============================================================================
|
||||
/** Returns true if this file was created from a valid (or non-existent) file.
|
||||
If the file failed to load correctly because it was corrupt or had insufficient
|
||||
access, this will be false.
|
||||
*/
|
||||
bool isValidFile() const noexcept { return loadedOk; }
|
||||
|
||||
//==============================================================================
|
||||
/** This will flush all the values to disk if they've changed since the last
|
||||
time they were saved.
|
||||
|
||||
Returns false if it fails to write to the file for some reason (maybe because
|
||||
it's read-only or the directory doesn't exist or something).
|
||||
|
||||
@see save
|
||||
*/
|
||||
bool saveIfNeeded();
|
||||
|
||||
/** This will force a write-to-disk of the current values, regardless of whether
|
||||
anything has changed since the last save.
|
||||
|
||||
Returns false if it fails to write to the file for some reason (maybe because
|
||||
it's read-only or the directory doesn't exist or something).
|
||||
|
||||
@see saveIfNeeded
|
||||
*/
|
||||
bool save();
|
||||
|
||||
/** Returns true if the properties have been altered since the last time they were saved.
|
||||
The file is flagged as needing to be saved when you change a value, but you can
|
||||
explicitly set this flag with setNeedsToBeSaved().
|
||||
*/
|
||||
bool needsToBeSaved() const;
|
||||
|
||||
/** Explicitly sets the flag to indicate whether the file needs saving or not.
|
||||
@see needsToBeSaved
|
||||
*/
|
||||
void setNeedsToBeSaved (bool needsToBeSaved);
|
||||
|
||||
/** Attempts to reload the settings from the file. */
|
||||
bool reload();
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the file that's being used. */
|
||||
const File& getFile() const noexcept { return file; }
|
||||
|
||||
|
||||
protected:
|
||||
/** @internal */
|
||||
void propertyChanged() override;
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
File file;
|
||||
Options options;
|
||||
bool loadedOk = false, needsWriting = false;
|
||||
|
||||
using ProcessScopedLock = const std::unique_ptr<InterProcessLock::ScopedLockType>;
|
||||
InterProcessLock::ScopedLockType* createProcessLock() const;
|
||||
|
||||
void timerCallback() override;
|
||||
bool saveAsXml();
|
||||
bool saveAsBinary();
|
||||
bool loadAsXml();
|
||||
bool loadAsBinary();
|
||||
bool loadAsBinary (InputStream&);
|
||||
bool writeToStream (OutputStream&);
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (PropertiesFile)
|
||||
};
|
||||
|
||||
} // namespace juce
|
44
deps/juce/modules/juce_data_structures/juce_data_structures.cpp
vendored
Normal file
44
deps/juce/modules/juce_data_structures/juce_data_structures.cpp
vendored
Normal file
@ -0,0 +1,44 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#ifdef JUCE_DATA_STRUCTURES_H_INCLUDED
|
||||
/* When you add this cpp file to your project, you mustn't include it in a file where you've
|
||||
already included any other headers - just put it inside a file on its own, possibly with your config
|
||||
flags preceding it, but don't include anything else. That also includes avoiding any automatic prefix
|
||||
header files that the compiler may be using.
|
||||
*/
|
||||
#error "Incorrect use of JUCE cpp file"
|
||||
#endif
|
||||
|
||||
#include "juce_data_structures.h"
|
||||
|
||||
#include "values/juce_Value.cpp"
|
||||
#include "values/juce_ValueTree.cpp"
|
||||
#include "values/juce_ValueTreeSynchroniser.cpp"
|
||||
#include "values/juce_CachedValue.cpp"
|
||||
#include "values/juce_ValueWithDefault.cpp"
|
||||
#include "undomanager/juce_UndoManager.cpp"
|
||||
#include "app_properties/juce_ApplicationProperties.cpp"
|
||||
#include "app_properties/juce_PropertiesFile.cpp"
|
66
deps/juce/modules/juce_data_structures/juce_data_structures.h
vendored
Normal file
66
deps/juce/modules/juce_data_structures/juce_data_structures.h
vendored
Normal file
@ -0,0 +1,66 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
The block below describes the properties of this module, and is read by
|
||||
the Projucer to automatically generate project code that uses it.
|
||||
For details about the syntax and how to create or use a module, see the
|
||||
JUCE Module Format.md file.
|
||||
|
||||
|
||||
BEGIN_JUCE_MODULE_DECLARATION
|
||||
|
||||
ID: juce_data_structures
|
||||
vendor: juce
|
||||
version: 6.1.2
|
||||
name: JUCE data model helper classes
|
||||
description: Classes for undo/redo management, and smart data structures.
|
||||
website: http://www.juce.com/juce
|
||||
license: GPL/Commercial
|
||||
minimumCppStandard: 14
|
||||
|
||||
dependencies: juce_events
|
||||
|
||||
END_JUCE_MODULE_DECLARATION
|
||||
|
||||
*******************************************************************************/
|
||||
|
||||
|
||||
#pragma once
|
||||
#define JUCE_DATA_STRUCTURES_H_INCLUDED
|
||||
|
||||
//==============================================================================
|
||||
#include <juce_events/juce_events.h>
|
||||
|
||||
#include "undomanager/juce_UndoableAction.h"
|
||||
#include "undomanager/juce_UndoManager.h"
|
||||
#include "values/juce_Value.h"
|
||||
#include "values/juce_ValueTree.h"
|
||||
#include "values/juce_ValueTreeSynchroniser.h"
|
||||
#include "values/juce_CachedValue.h"
|
||||
#include "values/juce_ValueWithDefault.h"
|
||||
#include "app_properties/juce_PropertiesFile.h"
|
||||
#include "app_properties/juce_ApplicationProperties.h"
|
26
deps/juce/modules/juce_data_structures/juce_data_structures.mm
vendored
Normal file
26
deps/juce/modules/juce_data_structures/juce_data_structures.mm
vendored
Normal file
@ -0,0 +1,26 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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 "juce_data_structures.cpp"
|
369
deps/juce/modules/juce_data_structures/undomanager/juce_UndoManager.cpp
vendored
Normal file
369
deps/juce/modules/juce_data_structures/undomanager/juce_UndoManager.cpp
vendored
Normal file
@ -0,0 +1,369 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
struct UndoManager::ActionSet
|
||||
{
|
||||
ActionSet (const String& transactionName) : name (transactionName)
|
||||
{}
|
||||
|
||||
bool perform() const
|
||||
{
|
||||
for (auto* a : actions)
|
||||
if (! a->perform())
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool undo() const
|
||||
{
|
||||
for (int i = actions.size(); --i >= 0;)
|
||||
if (! actions.getUnchecked(i)->undo())
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int getTotalSize() const
|
||||
{
|
||||
int total = 0;
|
||||
|
||||
for (auto* a : actions)
|
||||
total += a->getSizeInUnits();
|
||||
|
||||
return total;
|
||||
}
|
||||
|
||||
OwnedArray<UndoableAction> actions;
|
||||
String name;
|
||||
Time time { Time::getCurrentTime() };
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
UndoManager::UndoManager (int maxNumberOfUnitsToKeep, int minimumTransactions)
|
||||
{
|
||||
setMaxNumberOfStoredUnits (maxNumberOfUnitsToKeep, minimumTransactions);
|
||||
}
|
||||
|
||||
UndoManager::~UndoManager()
|
||||
{
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void UndoManager::clearUndoHistory()
|
||||
{
|
||||
transactions.clear();
|
||||
totalUnitsStored = 0;
|
||||
nextIndex = 0;
|
||||
sendChangeMessage();
|
||||
}
|
||||
|
||||
int UndoManager::getNumberOfUnitsTakenUpByStoredCommands() const
|
||||
{
|
||||
return totalUnitsStored;
|
||||
}
|
||||
|
||||
void UndoManager::setMaxNumberOfStoredUnits (int maxUnits, int minTransactions)
|
||||
{
|
||||
maxNumUnitsToKeep = jmax (1, maxUnits);
|
||||
minimumTransactionsToKeep = jmax (1, minTransactions);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
bool UndoManager::perform (UndoableAction* newAction, const String& actionName)
|
||||
{
|
||||
if (perform (newAction))
|
||||
{
|
||||
if (actionName.isNotEmpty())
|
||||
setCurrentTransactionName (actionName);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool UndoManager::perform (UndoableAction* newAction)
|
||||
{
|
||||
if (newAction != nullptr)
|
||||
{
|
||||
std::unique_ptr<UndoableAction> action (newAction);
|
||||
|
||||
if (isPerformingUndoRedo())
|
||||
{
|
||||
jassertfalse; // Don't call perform() recursively from the UndoableAction::perform()
|
||||
// or undo() methods, or else these actions will be discarded!
|
||||
return false;
|
||||
}
|
||||
|
||||
if (action->perform())
|
||||
{
|
||||
auto* actionSet = getCurrentSet();
|
||||
|
||||
if (actionSet != nullptr && ! newTransaction)
|
||||
{
|
||||
if (auto* lastAction = actionSet->actions.getLast())
|
||||
{
|
||||
if (auto coalescedAction = lastAction->createCoalescedAction (action.get()))
|
||||
{
|
||||
action.reset (coalescedAction);
|
||||
totalUnitsStored -= lastAction->getSizeInUnits();
|
||||
actionSet->actions.removeLast();
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
actionSet = new ActionSet (newTransactionName);
|
||||
transactions.insert (nextIndex, actionSet);
|
||||
++nextIndex;
|
||||
}
|
||||
|
||||
totalUnitsStored += action->getSizeInUnits();
|
||||
actionSet->actions.add (std::move (action));
|
||||
newTransaction = false;
|
||||
|
||||
moveFutureTransactionsToStash();
|
||||
dropOldTransactionsIfTooLarge();
|
||||
sendChangeMessage();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void UndoManager::moveFutureTransactionsToStash()
|
||||
{
|
||||
if (nextIndex < transactions.size())
|
||||
{
|
||||
stashedFutureTransactions.clear();
|
||||
|
||||
while (nextIndex < transactions.size())
|
||||
{
|
||||
auto* removed = transactions.removeAndReturn (nextIndex);
|
||||
stashedFutureTransactions.add (removed);
|
||||
totalUnitsStored -= removed->getTotalSize();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UndoManager::restoreStashedFutureTransactions()
|
||||
{
|
||||
while (nextIndex < transactions.size())
|
||||
{
|
||||
totalUnitsStored -= transactions.getUnchecked (nextIndex)->getTotalSize();
|
||||
transactions.remove (nextIndex);
|
||||
}
|
||||
|
||||
for (auto* stashed : stashedFutureTransactions)
|
||||
{
|
||||
transactions.add (stashed);
|
||||
totalUnitsStored += stashed->getTotalSize();
|
||||
}
|
||||
|
||||
stashedFutureTransactions.clearQuick (false);
|
||||
}
|
||||
|
||||
void UndoManager::dropOldTransactionsIfTooLarge()
|
||||
{
|
||||
while (nextIndex > 0
|
||||
&& totalUnitsStored > maxNumUnitsToKeep
|
||||
&& transactions.size() > minimumTransactionsToKeep)
|
||||
{
|
||||
totalUnitsStored -= transactions.getFirst()->getTotalSize();
|
||||
transactions.remove (0);
|
||||
--nextIndex;
|
||||
|
||||
// if this fails, then some actions may not be returning
|
||||
// consistent results from their getSizeInUnits() method
|
||||
jassert (totalUnitsStored >= 0);
|
||||
}
|
||||
}
|
||||
|
||||
void UndoManager::beginNewTransaction()
|
||||
{
|
||||
beginNewTransaction ({});
|
||||
}
|
||||
|
||||
void UndoManager::beginNewTransaction (const String& actionName)
|
||||
{
|
||||
newTransaction = true;
|
||||
newTransactionName = actionName;
|
||||
}
|
||||
|
||||
void UndoManager::setCurrentTransactionName (const String& newName)
|
||||
{
|
||||
if (newTransaction)
|
||||
newTransactionName = newName;
|
||||
else if (auto* action = getCurrentSet())
|
||||
action->name = newName;
|
||||
}
|
||||
|
||||
String UndoManager::getCurrentTransactionName() const
|
||||
{
|
||||
if (auto* action = getCurrentSet())
|
||||
return action->name;
|
||||
|
||||
return newTransactionName;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
UndoManager::ActionSet* UndoManager::getCurrentSet() const { return transactions[nextIndex - 1]; }
|
||||
UndoManager::ActionSet* UndoManager::getNextSet() const { return transactions[nextIndex]; }
|
||||
|
||||
bool UndoManager::isPerformingUndoRedo() const { return isInsideUndoRedoCall; }
|
||||
|
||||
bool UndoManager::canUndo() const { return getCurrentSet() != nullptr; }
|
||||
bool UndoManager::canRedo() const { return getNextSet() != nullptr; }
|
||||
|
||||
bool UndoManager::undo()
|
||||
{
|
||||
if (auto* s = getCurrentSet())
|
||||
{
|
||||
const ScopedValueSetter<bool> setter (isInsideUndoRedoCall, true);
|
||||
|
||||
if (s->undo())
|
||||
--nextIndex;
|
||||
else
|
||||
clearUndoHistory();
|
||||
|
||||
beginNewTransaction();
|
||||
sendChangeMessage();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool UndoManager::redo()
|
||||
{
|
||||
if (auto* s = getNextSet())
|
||||
{
|
||||
const ScopedValueSetter<bool> setter (isInsideUndoRedoCall, true);
|
||||
|
||||
if (s->perform())
|
||||
++nextIndex;
|
||||
else
|
||||
clearUndoHistory();
|
||||
|
||||
beginNewTransaction();
|
||||
sendChangeMessage();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
String UndoManager::getUndoDescription() const
|
||||
{
|
||||
if (auto* s = getCurrentSet())
|
||||
return s->name;
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
String UndoManager::getRedoDescription() const
|
||||
{
|
||||
if (auto* s = getNextSet())
|
||||
return s->name;
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
StringArray UndoManager::getUndoDescriptions() const
|
||||
{
|
||||
StringArray descriptions;
|
||||
|
||||
for (int i = nextIndex;;)
|
||||
{
|
||||
if (auto* t = transactions[--i])
|
||||
descriptions.add (t->name);
|
||||
else
|
||||
return descriptions;
|
||||
}
|
||||
}
|
||||
|
||||
StringArray UndoManager::getRedoDescriptions() const
|
||||
{
|
||||
StringArray descriptions;
|
||||
|
||||
for (int i = nextIndex;;)
|
||||
{
|
||||
if (auto* t = transactions[i++])
|
||||
descriptions.add (t->name);
|
||||
else
|
||||
return descriptions;
|
||||
}
|
||||
}
|
||||
|
||||
Time UndoManager::getTimeOfUndoTransaction() const
|
||||
{
|
||||
if (auto* s = getCurrentSet())
|
||||
return s->time;
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
Time UndoManager::getTimeOfRedoTransaction() const
|
||||
{
|
||||
if (auto* s = getNextSet())
|
||||
return s->time;
|
||||
|
||||
return Time::getCurrentTime();
|
||||
}
|
||||
|
||||
bool UndoManager::undoCurrentTransactionOnly()
|
||||
{
|
||||
if ((! newTransaction) && undo())
|
||||
{
|
||||
restoreStashedFutureTransactions();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void UndoManager::getActionsInCurrentTransaction (Array<const UndoableAction*>& actionsFound) const
|
||||
{
|
||||
if (! newTransaction)
|
||||
if (auto* s = getCurrentSet())
|
||||
for (auto* a : s->actions)
|
||||
actionsFound.add (a);
|
||||
}
|
||||
|
||||
int UndoManager::getNumActionsInCurrentTransaction() const
|
||||
{
|
||||
if (! newTransaction)
|
||||
if (auto* s = getCurrentSet())
|
||||
return s->actions.size();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace juce
|
264
deps/juce/modules/juce_data_structures/undomanager/juce_UndoManager.h
vendored
Normal file
264
deps/juce/modules/juce_data_structures/undomanager/juce_UndoManager.h
vendored
Normal file
@ -0,0 +1,264 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Manages a list of undo/redo commands.
|
||||
|
||||
An UndoManager object keeps a list of past actions and can use these actions
|
||||
to move backwards and forwards through an undo history.
|
||||
|
||||
To use it, create subclasses of UndoableAction which perform all the
|
||||
actions you need, then when you need to actually perform an action, create one
|
||||
and pass it to the UndoManager's perform() method.
|
||||
|
||||
The manager also uses the concept of 'transactions' to group the actions
|
||||
together - all actions performed between calls to beginNewTransaction() are
|
||||
grouped together and are all undone/redone as a group.
|
||||
|
||||
The UndoManager is a ChangeBroadcaster, so listeners can register to be told
|
||||
when actions are performed or undone.
|
||||
|
||||
@see UndoableAction
|
||||
|
||||
@tags{DataStructures}
|
||||
*/
|
||||
class JUCE_API UndoManager : public ChangeBroadcaster
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates an UndoManager.
|
||||
|
||||
@param maxNumberOfUnitsToKeep each UndoableAction object returns a value
|
||||
to indicate how much storage it takes up
|
||||
(UndoableAction::getSizeInUnits()), so this
|
||||
lets you specify the maximum total number of
|
||||
units that the undomanager is allowed to
|
||||
keep in memory before letting the older actions
|
||||
drop off the end of the list.
|
||||
@param minimumTransactionsToKeep this specifies the minimum number of transactions
|
||||
that will be kept, even if this involves exceeding
|
||||
the amount of space specified in maxNumberOfUnitsToKeep
|
||||
*/
|
||||
UndoManager (int maxNumberOfUnitsToKeep = 30000,
|
||||
int minimumTransactionsToKeep = 30);
|
||||
|
||||
/** Destructor. */
|
||||
~UndoManager() override;
|
||||
|
||||
//==============================================================================
|
||||
/** Deletes all stored actions in the list. */
|
||||
void clearUndoHistory();
|
||||
|
||||
/** Returns the current amount of space to use for storing UndoableAction objects.
|
||||
@see setMaxNumberOfStoredUnits
|
||||
*/
|
||||
int getNumberOfUnitsTakenUpByStoredCommands() const;
|
||||
|
||||
/** Sets the amount of space that can be used for storing UndoableAction objects.
|
||||
|
||||
@param maxNumberOfUnitsToKeep each UndoableAction object returns a value
|
||||
to indicate how much storage it takes up
|
||||
(UndoableAction::getSizeInUnits()), so this
|
||||
lets you specify the maximum total number of
|
||||
units that the undomanager is allowed to
|
||||
keep in memory before letting the older actions
|
||||
drop off the end of the list.
|
||||
@param minimumTransactionsToKeep this specifies the minimum number of transactions
|
||||
that will be kept, even if this involves exceeding
|
||||
the amount of space specified in maxNumberOfUnitsToKeep
|
||||
@see getNumberOfUnitsTakenUpByStoredCommands
|
||||
*/
|
||||
void setMaxNumberOfStoredUnits (int maxNumberOfUnitsToKeep,
|
||||
int minimumTransactionsToKeep);
|
||||
|
||||
//==============================================================================
|
||||
/** Performs an action and adds it to the undo history list.
|
||||
|
||||
@param action the action to perform - this object will be deleted by
|
||||
the UndoManager when no longer needed
|
||||
@returns true if the command succeeds - see UndoableAction::perform
|
||||
@see beginNewTransaction
|
||||
*/
|
||||
bool perform (UndoableAction* action);
|
||||
|
||||
/** Performs an action and also gives it a name.
|
||||
|
||||
@param action the action to perform - this object will be deleted by
|
||||
the UndoManager when no longer needed
|
||||
@param actionName if this string is non-empty, the current transaction will be
|
||||
given this name; if it's empty, the current transaction name will
|
||||
be left unchanged. See setCurrentTransactionName()
|
||||
@returns true if the command succeeds - see UndoableAction::perform
|
||||
@see beginNewTransaction
|
||||
*/
|
||||
bool perform (UndoableAction* action, const String& actionName);
|
||||
|
||||
/** Starts a new group of actions that together will be treated as a single transaction.
|
||||
|
||||
All actions that are passed to the perform() method between calls to this
|
||||
method are grouped together and undone/redone together by a single call to
|
||||
undo() or redo().
|
||||
*/
|
||||
void beginNewTransaction();
|
||||
|
||||
/** Starts a new group of actions that together will be treated as a single transaction.
|
||||
|
||||
All actions that are passed to the perform() method between calls to this
|
||||
method are grouped together and undone/redone together by a single call to
|
||||
undo() or redo().
|
||||
|
||||
@param actionName a description of the transaction that is about to be
|
||||
performed
|
||||
*/
|
||||
void beginNewTransaction (const String& actionName);
|
||||
|
||||
/** Changes the name stored for the current transaction.
|
||||
|
||||
Each transaction is given a name when the beginNewTransaction() method is
|
||||
called, but this can be used to change that name without starting a new
|
||||
transaction.
|
||||
*/
|
||||
void setCurrentTransactionName (const String& newName);
|
||||
|
||||
/** Returns the name of the current transaction.
|
||||
@see setCurrentTransactionName
|
||||
*/
|
||||
String getCurrentTransactionName() const;
|
||||
|
||||
//==============================================================================
|
||||
/** Returns true if there's at least one action in the list to undo.
|
||||
@see getUndoDescription, undo, canRedo
|
||||
*/
|
||||
bool canUndo() const;
|
||||
|
||||
/** Tries to roll-back the last transaction.
|
||||
@returns true if the transaction can be undone, and false if it fails, or
|
||||
if there aren't any transactions to undo
|
||||
@see undoCurrentTransactionOnly
|
||||
*/
|
||||
bool undo();
|
||||
|
||||
/** Tries to roll-back any actions that were added to the current transaction.
|
||||
|
||||
This will perform an undo() only if there are some actions in the undo list
|
||||
that were added after the last call to beginNewTransaction().
|
||||
|
||||
This is useful because it lets you call beginNewTransaction(), then
|
||||
perform an operation which may or may not actually perform some actions, and
|
||||
then call this method to get rid of any actions that might have been done
|
||||
without it rolling back the previous transaction if nothing was actually
|
||||
done.
|
||||
|
||||
@returns true if any actions were undone.
|
||||
*/
|
||||
bool undoCurrentTransactionOnly();
|
||||
|
||||
/** Returns the name of the transaction that will be rolled-back when undo() is called.
|
||||
@see undo, canUndo, getUndoDescriptions
|
||||
*/
|
||||
String getUndoDescription() const;
|
||||
|
||||
/** Returns the names of the sequence of transactions that will be performed if undo()
|
||||
is repeatedly called. Note that for transactions where no name was provided, the
|
||||
corresponding string will be empty.
|
||||
@see undo, canUndo, getUndoDescription
|
||||
*/
|
||||
StringArray getUndoDescriptions() const;
|
||||
|
||||
/** Returns the time to which the state would be restored if undo() was to be called.
|
||||
If an undo isn't currently possible, it'll return Time().
|
||||
*/
|
||||
Time getTimeOfUndoTransaction() const;
|
||||
|
||||
/** Returns a list of the UndoableAction objects that have been performed during the
|
||||
transaction that is currently open.
|
||||
|
||||
Effectively, this is the list of actions that would be undone if undoCurrentTransactionOnly()
|
||||
were to be called now.
|
||||
|
||||
The first item in the list is the earliest action performed.
|
||||
*/
|
||||
void getActionsInCurrentTransaction (Array<const UndoableAction*>& actionsFound) const;
|
||||
|
||||
/** Returns the number of UndoableAction objects that have been performed during the
|
||||
transaction that is currently open.
|
||||
@see getActionsInCurrentTransaction
|
||||
*/
|
||||
int getNumActionsInCurrentTransaction() const;
|
||||
|
||||
//==============================================================================
|
||||
/** Returns true if there's at least one action in the list to redo.
|
||||
@see getRedoDescription, redo, canUndo
|
||||
*/
|
||||
bool canRedo() const;
|
||||
|
||||
/** Tries to redo the last transaction that was undone.
|
||||
@returns true if the transaction can be redone, and false if it fails, or
|
||||
if there aren't any transactions to redo
|
||||
*/
|
||||
bool redo();
|
||||
|
||||
/** Returns the name of the transaction that will be redone when redo() is called.
|
||||
@see redo, canRedo, getRedoDescriptions
|
||||
*/
|
||||
String getRedoDescription() const;
|
||||
|
||||
/** Returns the names of the sequence of transactions that will be performed if redo()
|
||||
is repeatedly called. Note that for transactions where no name was provided, the
|
||||
corresponding string will be empty.
|
||||
@see redo, canRedo, getRedoDescription
|
||||
*/
|
||||
StringArray getRedoDescriptions() const;
|
||||
|
||||
/** Returns the time to which the state would be restored if redo() was to be called.
|
||||
If a redo isn't currently possible, it'll return Time::getCurrentTime().
|
||||
@see redo, canRedo
|
||||
*/
|
||||
Time getTimeOfRedoTransaction() const;
|
||||
|
||||
/** Returns true if the caller code is in the middle of an undo or redo action. */
|
||||
bool isPerformingUndoRedo() const;
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
struct ActionSet;
|
||||
OwnedArray<ActionSet> transactions, stashedFutureTransactions;
|
||||
String newTransactionName;
|
||||
int totalUnitsStored = 0, maxNumUnitsToKeep = 0, minimumTransactionsToKeep = 0, nextIndex = 0;
|
||||
bool newTransaction = true, isInsideUndoRedoCall = false;
|
||||
ActionSet* getCurrentSet() const;
|
||||
ActionSet* getNextSet() const;
|
||||
void moveFutureTransactionsToStash();
|
||||
void restoreStashedFutureTransactions();
|
||||
void dropOldTransactionsIfTooLarge();
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (UndoManager)
|
||||
};
|
||||
|
||||
} // namespace juce
|
100
deps/juce/modules/juce_data_structures/undomanager/juce_UndoableAction.h
vendored
Normal file
100
deps/juce/modules/juce_data_structures/undomanager/juce_UndoableAction.h
vendored
Normal file
@ -0,0 +1,100 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Used by the UndoManager class to store an action which can be done
|
||||
and undone.
|
||||
|
||||
@see UndoManager
|
||||
|
||||
@tags{DataStructures}
|
||||
*/
|
||||
class JUCE_API UndoableAction
|
||||
{
|
||||
protected:
|
||||
/** Creates an action. */
|
||||
UndoableAction() = default;
|
||||
|
||||
public:
|
||||
/** Destructor. */
|
||||
virtual ~UndoableAction() = default;
|
||||
|
||||
//==============================================================================
|
||||
/** Overridden by a subclass to perform the action.
|
||||
|
||||
This method is called by the UndoManager, and shouldn't be used directly by
|
||||
applications.
|
||||
|
||||
Be careful not to make any calls in a perform() method that could call
|
||||
recursively back into the UndoManager::perform() method
|
||||
|
||||
@returns true if the action could be performed.
|
||||
@see UndoManager::perform
|
||||
*/
|
||||
virtual bool perform() = 0;
|
||||
|
||||
/** Overridden by a subclass to undo the action.
|
||||
|
||||
This method is called by the UndoManager, and shouldn't be used directly by
|
||||
applications.
|
||||
|
||||
Be careful not to make any calls in an undo() method that could call
|
||||
recursively back into the UndoManager::perform() method
|
||||
|
||||
@returns true if the action could be undone without any errors.
|
||||
@see UndoManager::perform
|
||||
*/
|
||||
virtual bool undo() = 0;
|
||||
|
||||
//==============================================================================
|
||||
/** Returns a value to indicate how much memory this object takes up.
|
||||
|
||||
Because the UndoManager keeps a list of UndoableActions, this is used
|
||||
to work out how much space each one will take up, so that the UndoManager
|
||||
can work out how many to keep.
|
||||
|
||||
The default value returned here is 10 - units are arbitrary and
|
||||
don't have to be accurate.
|
||||
|
||||
@see UndoManager::getNumberOfUnitsTakenUpByStoredCommands,
|
||||
UndoManager::setMaxNumberOfStoredUnits
|
||||
*/
|
||||
virtual int getSizeInUnits() { return 10; }
|
||||
|
||||
/** Allows multiple actions to be coalesced into a single action object, to reduce storage space.
|
||||
|
||||
If possible, this method should create and return a single action that does the same job as
|
||||
this one followed by the supplied action.
|
||||
|
||||
If it's not possible to merge the two actions, the method should return a nullptr.
|
||||
*/
|
||||
virtual UndoableAction* createCoalescedAction (UndoableAction* nextAction) { ignoreUnused (nextAction); return nullptr; }
|
||||
};
|
||||
|
||||
} // namespace juce
|
155
deps/juce/modules/juce_data_structures/values/juce_CachedValue.cpp
vendored
Normal file
155
deps/juce/modules/juce_data_structures/values/juce_CachedValue.cpp
vendored
Normal file
@ -0,0 +1,155 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
#if JUCE_UNIT_TESTS
|
||||
|
||||
class CachedValueTests : public UnitTest
|
||||
{
|
||||
public:
|
||||
CachedValueTests()
|
||||
: UnitTest ("CachedValues", UnitTestCategories::values)
|
||||
{}
|
||||
|
||||
void runTest() override
|
||||
{
|
||||
beginTest ("default constructor");
|
||||
{
|
||||
CachedValue<String> cv;
|
||||
expect (cv.isUsingDefault());
|
||||
expect (cv.get() == String());
|
||||
}
|
||||
|
||||
beginTest ("without default value");
|
||||
{
|
||||
ValueTree t ("root");
|
||||
t.setProperty ("testkey", "testvalue", nullptr);
|
||||
|
||||
CachedValue<String> cv (t, "testkey", nullptr);
|
||||
|
||||
expect (! cv.isUsingDefault());
|
||||
expect (cv.get() == "testvalue");
|
||||
|
||||
cv.resetToDefault();
|
||||
|
||||
expect (cv.isUsingDefault());
|
||||
expect (cv.get() == String());
|
||||
}
|
||||
|
||||
beginTest ("with default value");
|
||||
{
|
||||
ValueTree t ("root");
|
||||
t.setProperty ("testkey", "testvalue", nullptr);
|
||||
|
||||
CachedValue<String> cv (t, "testkey", nullptr, "defaultvalue");
|
||||
|
||||
expect (! cv.isUsingDefault());
|
||||
expect (cv.get() == "testvalue");
|
||||
|
||||
cv.resetToDefault();
|
||||
|
||||
expect (cv.isUsingDefault());
|
||||
expect (cv.get() == "defaultvalue");
|
||||
}
|
||||
|
||||
beginTest ("with default value (int)");
|
||||
{
|
||||
ValueTree t ("root");
|
||||
t.setProperty ("testkey", 23, nullptr);
|
||||
|
||||
CachedValue<int> cv (t, "testkey", nullptr, 34);
|
||||
|
||||
expect (! cv.isUsingDefault());
|
||||
expect (cv == 23);
|
||||
expectEquals (cv.get(), 23);
|
||||
|
||||
cv.resetToDefault();
|
||||
|
||||
expect (cv.isUsingDefault());
|
||||
expect (cv == 34);
|
||||
}
|
||||
|
||||
beginTest ("with void value");
|
||||
{
|
||||
ValueTree t ("root");
|
||||
t.setProperty ("testkey", var(), nullptr);
|
||||
|
||||
CachedValue<String> cv (t, "testkey", nullptr, "defaultvalue");
|
||||
|
||||
expect (! cv.isUsingDefault());
|
||||
expect (cv == "");
|
||||
expectEquals (cv.get(), String());
|
||||
}
|
||||
|
||||
beginTest ("with non-existent value");
|
||||
{
|
||||
ValueTree t ("root");
|
||||
|
||||
CachedValue<String> cv (t, "testkey", nullptr, "defaultvalue");
|
||||
|
||||
expect (cv.isUsingDefault());
|
||||
expect (cv == "defaultvalue");
|
||||
expect (cv.get() == "defaultvalue");
|
||||
}
|
||||
|
||||
beginTest ("with value changing");
|
||||
{
|
||||
ValueTree t ("root");
|
||||
t.setProperty ("testkey", "oldvalue", nullptr);
|
||||
|
||||
CachedValue<String> cv (t, "testkey", nullptr, "defaultvalue");
|
||||
expect (cv == "oldvalue");
|
||||
|
||||
t.setProperty ("testkey", "newvalue", nullptr);
|
||||
expect (cv != "oldvalue");
|
||||
expect (cv == "newvalue");
|
||||
}
|
||||
|
||||
beginTest ("set value");
|
||||
{
|
||||
ValueTree t ("root");
|
||||
t.setProperty ("testkey", 23, nullptr);
|
||||
|
||||
CachedValue<int> cv (t, "testkey", nullptr, 45);
|
||||
cv = 34;
|
||||
|
||||
expectEquals ((int) t["testkey"], 34);
|
||||
|
||||
cv.resetToDefault();
|
||||
expect (cv == 45);
|
||||
expectEquals (cv.get(), 45);
|
||||
|
||||
expect (t["testkey"] == var());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
static CachedValueTests cachedValueTests;
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace juce
|
314
deps/juce/modules/juce_data_structures/values/juce_CachedValue.h
vendored
Normal file
314
deps/juce/modules/juce_data_structures/values/juce_CachedValue.h
vendored
Normal file
@ -0,0 +1,314 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
This class acts as a typed wrapper around a property inside a ValueTree.
|
||||
|
||||
A CachedValue provides an easy way to read and write a ValueTree property with
|
||||
a chosen type. So for example a CachedValue<int> allows you to read or write the
|
||||
property as an int, and a CachedValue<String> lets you work with it as a String.
|
||||
|
||||
It also allows efficient access to the value, by caching a copy of it in the
|
||||
type that is being used.
|
||||
|
||||
You can give the CachedValue an optional UndoManager which it will use when writing
|
||||
to the underlying ValueTree.
|
||||
|
||||
If the property inside the ValueTree is missing, the CachedValue will automatically
|
||||
return an optional default value, which can be specified when initialising the CachedValue.
|
||||
|
||||
To create one, you can either use the constructor to attach the CachedValue to a
|
||||
ValueTree, or can create an uninitialised CachedValue with its default constructor and
|
||||
then attach it later with the referTo() methods.
|
||||
|
||||
Common types like String, int, double which can be easily converted to a var should work
|
||||
out-of-the-box, but if you want to use more complex custom types, you may need to implement
|
||||
some template specialisations of VariantConverter which this class uses to convert between
|
||||
the type and the ValueTree's internal var.
|
||||
|
||||
@tags{DataStructures}
|
||||
*/
|
||||
template <typename Type>
|
||||
class CachedValue : private ValueTree::Listener
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Default constructor.
|
||||
Creates a default CachedValue not referring to any property. To initialise the
|
||||
object, call one of the referTo() methods.
|
||||
*/
|
||||
CachedValue();
|
||||
|
||||
/** Constructor.
|
||||
|
||||
Creates a CachedValue referring to a Value property inside a ValueTree.
|
||||
If you use this constructor, the fallback value will be a default-constructed
|
||||
instance of Type.
|
||||
|
||||
@param tree The ValueTree containing the property
|
||||
@param propertyID The identifier of the property
|
||||
@param undoManager The UndoManager to use when writing to the property
|
||||
*/
|
||||
CachedValue (ValueTree& tree, const Identifier& propertyID,
|
||||
UndoManager* undoManager);
|
||||
|
||||
/** Constructor.
|
||||
|
||||
Creates a default Cached Value referring to a Value property inside a ValueTree,
|
||||
and specifies a fallback value to use if the property does not exist.
|
||||
|
||||
@param tree The ValueTree containing the property
|
||||
@param propertyID The identifier of the property
|
||||
@param undoManager The UndoManager to use when writing to the property
|
||||
@param defaultToUse The fallback default value to use.
|
||||
*/
|
||||
CachedValue (ValueTree& tree, const Identifier& propertyID,
|
||||
UndoManager* undoManager, const Type& defaultToUse);
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the current value of the property. If the property does not exist,
|
||||
returns the fallback default value.
|
||||
|
||||
This is the same as calling get().
|
||||
*/
|
||||
operator Type() const noexcept { return cachedValue; }
|
||||
|
||||
/** Returns the current value of the property. If the property does not exist,
|
||||
returns the fallback default value.
|
||||
*/
|
||||
Type get() const noexcept { return cachedValue; }
|
||||
|
||||
/** Dereference operator. Provides direct access to the property. */
|
||||
Type& operator*() noexcept { return cachedValue; }
|
||||
|
||||
/** Dereference operator. Provides direct access to members of the property
|
||||
if it is of object type.
|
||||
*/
|
||||
Type* operator->() noexcept { return &cachedValue; }
|
||||
|
||||
/** Returns true if the current value of the property (or the fallback value)
|
||||
is equal to other.
|
||||
*/
|
||||
template <typename OtherType>
|
||||
bool operator== (const OtherType& other) const { return cachedValue == other; }
|
||||
|
||||
/** Returns true if the current value of the property (or the fallback value)
|
||||
is not equal to other.
|
||||
*/
|
||||
template <typename OtherType>
|
||||
bool operator!= (const OtherType& other) const { return cachedValue != other; }
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the current property as a Value object. */
|
||||
Value getPropertyAsValue();
|
||||
|
||||
/** Returns true if the current property does not exist and the CachedValue is using
|
||||
the fallback default value instead.
|
||||
*/
|
||||
bool isUsingDefault() const;
|
||||
|
||||
/** Returns the current fallback default value. */
|
||||
Type getDefault() const { return defaultValue; }
|
||||
|
||||
//==============================================================================
|
||||
/** Sets the property. This will actually modify the property in the referenced ValueTree. */
|
||||
CachedValue& operator= (const Type& newValue);
|
||||
|
||||
/** Sets the property. This will actually modify the property in the referenced ValueTree. */
|
||||
void setValue (const Type& newValue, UndoManager* undoManagerToUse);
|
||||
|
||||
/** Removes the property from the referenced ValueTree and makes the CachedValue
|
||||
return the fallback default value instead.
|
||||
*/
|
||||
void resetToDefault();
|
||||
|
||||
/** Removes the property from the referenced ValueTree and makes the CachedValue
|
||||
return the fallback default value instead.
|
||||
*/
|
||||
void resetToDefault (UndoManager* undoManagerToUse);
|
||||
|
||||
/** Resets the fallback default value. */
|
||||
void setDefault (const Type& value) { defaultValue = value; }
|
||||
|
||||
//==============================================================================
|
||||
/** Makes the CachedValue refer to the specified property inside the given ValueTree. */
|
||||
void referTo (ValueTree& tree, const Identifier& property, UndoManager* um);
|
||||
|
||||
/** Makes the CachedValue refer to the specified property inside the given ValueTree,
|
||||
and specifies a fallback value to use if the property does not exist.
|
||||
*/
|
||||
void referTo (ValueTree& tree, const Identifier& property, UndoManager* um, const Type& defaultVal);
|
||||
|
||||
/** Force an update in case the referenced property has been changed from elsewhere.
|
||||
|
||||
Note: The CachedValue is a ValueTree::Listener and therefore will be informed of
|
||||
changes of the referenced property anyway (and update itself). But this may happen
|
||||
asynchronously. forceUpdateOfCachedValue() forces an update immediately.
|
||||
*/
|
||||
void forceUpdateOfCachedValue();
|
||||
|
||||
//==============================================================================
|
||||
/** Returns a reference to the ValueTree containing the referenced property. */
|
||||
ValueTree& getValueTree() noexcept { return targetTree; }
|
||||
|
||||
/** Returns the property ID of the referenced property. */
|
||||
const Identifier& getPropertyID() const noexcept { return targetProperty; }
|
||||
|
||||
/** Returns the UndoManager that is being used. */
|
||||
UndoManager* getUndoManager() noexcept { return undoManager; }
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
ValueTree targetTree;
|
||||
Identifier targetProperty;
|
||||
UndoManager* undoManager = nullptr;
|
||||
Type defaultValue;
|
||||
Type cachedValue;
|
||||
|
||||
//==============================================================================
|
||||
void referToWithDefault (ValueTree&, const Identifier&, UndoManager*, const Type&);
|
||||
Type getTypedValue() const;
|
||||
|
||||
void valueTreePropertyChanged (ValueTree& changedTree, const Identifier& changedProperty) override;
|
||||
|
||||
//==============================================================================
|
||||
JUCE_DECLARE_WEAK_REFERENCEABLE (CachedValue)
|
||||
JUCE_DECLARE_NON_COPYABLE (CachedValue)
|
||||
};
|
||||
|
||||
|
||||
//==============================================================================
|
||||
template <typename Type>
|
||||
inline CachedValue<Type>::CachedValue() = default;
|
||||
|
||||
template <typename Type>
|
||||
inline CachedValue<Type>::CachedValue (ValueTree& v, const Identifier& i, UndoManager* um)
|
||||
: targetTree (v), targetProperty (i), undoManager (um),
|
||||
defaultValue(), cachedValue (getTypedValue())
|
||||
{
|
||||
targetTree.addListener (this);
|
||||
}
|
||||
|
||||
template <typename Type>
|
||||
inline CachedValue<Type>::CachedValue (ValueTree& v, const Identifier& i, UndoManager* um, const Type& defaultToUse)
|
||||
: targetTree (v), targetProperty (i), undoManager (um),
|
||||
defaultValue (defaultToUse), cachedValue (getTypedValue())
|
||||
{
|
||||
targetTree.addListener (this);
|
||||
}
|
||||
|
||||
template <typename Type>
|
||||
inline Value CachedValue<Type>::getPropertyAsValue()
|
||||
{
|
||||
return targetTree.getPropertyAsValue (targetProperty, undoManager);
|
||||
}
|
||||
|
||||
template <typename Type>
|
||||
inline bool CachedValue<Type>::isUsingDefault() const
|
||||
{
|
||||
return ! targetTree.hasProperty (targetProperty);
|
||||
}
|
||||
|
||||
template <typename Type>
|
||||
inline CachedValue<Type>& CachedValue<Type>::operator= (const Type& newValue)
|
||||
{
|
||||
setValue (newValue, undoManager);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename Type>
|
||||
inline void CachedValue<Type>::setValue (const Type& newValue, UndoManager* undoManagerToUse)
|
||||
{
|
||||
if (cachedValue != newValue || isUsingDefault())
|
||||
{
|
||||
cachedValue = newValue;
|
||||
targetTree.setProperty (targetProperty, VariantConverter<Type>::toVar (newValue), undoManagerToUse);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Type>
|
||||
inline void CachedValue<Type>::resetToDefault()
|
||||
{
|
||||
resetToDefault (undoManager);
|
||||
}
|
||||
|
||||
template <typename Type>
|
||||
inline void CachedValue<Type>::resetToDefault (UndoManager* undoManagerToUse)
|
||||
{
|
||||
targetTree.removeProperty (targetProperty, undoManagerToUse);
|
||||
forceUpdateOfCachedValue();
|
||||
}
|
||||
|
||||
template <typename Type>
|
||||
inline void CachedValue<Type>::referTo (ValueTree& v, const Identifier& i, UndoManager* um)
|
||||
{
|
||||
referToWithDefault (v, i, um, Type());
|
||||
}
|
||||
|
||||
template <typename Type>
|
||||
inline void CachedValue<Type>::referTo (ValueTree& v, const Identifier& i, UndoManager* um, const Type& defaultVal)
|
||||
{
|
||||
referToWithDefault (v, i, um, defaultVal);
|
||||
}
|
||||
|
||||
template <typename Type>
|
||||
inline void CachedValue<Type>::forceUpdateOfCachedValue()
|
||||
{
|
||||
cachedValue = getTypedValue();
|
||||
}
|
||||
|
||||
template <typename Type>
|
||||
inline void CachedValue<Type>::referToWithDefault (ValueTree& v, const Identifier& i, UndoManager* um, const Type& defaultVal)
|
||||
{
|
||||
targetTree.removeListener (this);
|
||||
targetTree = v;
|
||||
targetProperty = i;
|
||||
undoManager = um;
|
||||
defaultValue = defaultVal;
|
||||
cachedValue = getTypedValue();
|
||||
targetTree.addListener (this);
|
||||
}
|
||||
|
||||
template <typename Type>
|
||||
inline Type CachedValue<Type>::getTypedValue() const
|
||||
{
|
||||
if (const var* property = targetTree.getPropertyPointer (targetProperty))
|
||||
return VariantConverter<Type>::fromVar (*property);
|
||||
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
template <typename Type>
|
||||
inline void CachedValue<Type>::valueTreePropertyChanged (ValueTree& changedTree, const Identifier& changedProperty)
|
||||
{
|
||||
if (changedProperty == targetProperty && targetTree == changedTree)
|
||||
forceUpdateOfCachedValue();
|
||||
}
|
||||
|
||||
} // namespace juce
|
241
deps/juce/modules/juce_data_structures/values/juce_Value.cpp
vendored
Normal file
241
deps/juce/modules/juce_data_structures/values/juce_Value.cpp
vendored
Normal file
@ -0,0 +1,241 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
Value::ValueSource::ValueSource()
|
||||
{
|
||||
}
|
||||
|
||||
Value::ValueSource::~ValueSource()
|
||||
{
|
||||
cancelPendingUpdate();
|
||||
}
|
||||
|
||||
void Value::ValueSource::handleAsyncUpdate()
|
||||
{
|
||||
sendChangeMessage (true);
|
||||
}
|
||||
|
||||
void Value::ValueSource::sendChangeMessage (const bool synchronous)
|
||||
{
|
||||
const int numListeners = valuesWithListeners.size();
|
||||
|
||||
if (numListeners > 0)
|
||||
{
|
||||
if (synchronous)
|
||||
{
|
||||
const ReferenceCountedObjectPtr<ValueSource> localRef (this);
|
||||
|
||||
cancelPendingUpdate();
|
||||
|
||||
for (int i = numListeners; --i >= 0;)
|
||||
if (Value* const v = valuesWithListeners[i])
|
||||
v->callListeners();
|
||||
}
|
||||
else
|
||||
{
|
||||
triggerAsyncUpdate();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
class SimpleValueSource : public Value::ValueSource
|
||||
{
|
||||
public:
|
||||
SimpleValueSource()
|
||||
{
|
||||
}
|
||||
|
||||
SimpleValueSource (const var& initialValue)
|
||||
: value (initialValue)
|
||||
{
|
||||
}
|
||||
|
||||
var getValue() const override
|
||||
{
|
||||
return value;
|
||||
}
|
||||
|
||||
void setValue (const var& newValue) override
|
||||
{
|
||||
if (! newValue.equalsWithSameType (value))
|
||||
{
|
||||
value = newValue;
|
||||
sendChangeMessage (false);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
var value;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (SimpleValueSource)
|
||||
};
|
||||
|
||||
|
||||
//==============================================================================
|
||||
Value::Value() : value (new SimpleValueSource())
|
||||
{
|
||||
}
|
||||
|
||||
Value::Value (ValueSource* const v) : value (v)
|
||||
{
|
||||
jassert (v != nullptr);
|
||||
}
|
||||
|
||||
Value::Value (const var& initialValue) : value (new SimpleValueSource (initialValue))
|
||||
{
|
||||
}
|
||||
|
||||
Value::Value (const Value& other) : value (other.value)
|
||||
{
|
||||
}
|
||||
|
||||
Value::Value (Value&& other) noexcept
|
||||
{
|
||||
// moving a Value with listeners will lose those listeners, which
|
||||
// probably isn't what you wanted to happen!
|
||||
jassert (other.listeners.size() == 0);
|
||||
|
||||
other.removeFromListenerList();
|
||||
value = std::move (other.value);
|
||||
}
|
||||
|
||||
Value& Value::operator= (Value&& other) noexcept
|
||||
{
|
||||
// moving a Value with listeners will lose those listeners, which
|
||||
// probably isn't what you wanted to happen!
|
||||
jassert (other.listeners.size() == 0);
|
||||
|
||||
other.removeFromListenerList();
|
||||
value = std::move (other.value);
|
||||
return *this;
|
||||
}
|
||||
|
||||
Value::~Value()
|
||||
{
|
||||
removeFromListenerList();
|
||||
}
|
||||
|
||||
void Value::removeFromListenerList()
|
||||
{
|
||||
if (listeners.size() > 0 && value != nullptr) // may be nullptr after a move operation
|
||||
value->valuesWithListeners.removeValue (this);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
var Value::getValue() const
|
||||
{
|
||||
return value->getValue();
|
||||
}
|
||||
|
||||
Value::operator var() const
|
||||
{
|
||||
return value->getValue();
|
||||
}
|
||||
|
||||
void Value::setValue (const var& newValue)
|
||||
{
|
||||
value->setValue (newValue);
|
||||
}
|
||||
|
||||
String Value::toString() const
|
||||
{
|
||||
return value->getValue().toString();
|
||||
}
|
||||
|
||||
Value& Value::operator= (const var& newValue)
|
||||
{
|
||||
value->setValue (newValue);
|
||||
return *this;
|
||||
}
|
||||
|
||||
void Value::referTo (const Value& valueToReferTo)
|
||||
{
|
||||
if (valueToReferTo.value != value)
|
||||
{
|
||||
if (listeners.size() > 0)
|
||||
{
|
||||
value->valuesWithListeners.removeValue (this);
|
||||
valueToReferTo.value->valuesWithListeners.add (this);
|
||||
}
|
||||
|
||||
value = valueToReferTo.value;
|
||||
callListeners();
|
||||
}
|
||||
}
|
||||
|
||||
bool Value::refersToSameSourceAs (const Value& other) const
|
||||
{
|
||||
return value == other.value;
|
||||
}
|
||||
|
||||
bool Value::operator== (const Value& other) const
|
||||
{
|
||||
return value == other.value || value->getValue() == other.getValue();
|
||||
}
|
||||
|
||||
bool Value::operator!= (const Value& other) const
|
||||
{
|
||||
return value != other.value && value->getValue() != other.getValue();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void Value::addListener (Value::Listener* listener)
|
||||
{
|
||||
if (listener != nullptr)
|
||||
{
|
||||
if (listeners.size() == 0)
|
||||
value->valuesWithListeners.add (this);
|
||||
|
||||
listeners.add (listener);
|
||||
}
|
||||
}
|
||||
|
||||
void Value::removeListener (Value::Listener* listener)
|
||||
{
|
||||
listeners.remove (listener);
|
||||
|
||||
if (listeners.size() == 0)
|
||||
value->valuesWithListeners.removeValue (this);
|
||||
}
|
||||
|
||||
void Value::callListeners()
|
||||
{
|
||||
if (listeners.size() > 0)
|
||||
{
|
||||
Value v (*this); // (create a copy in case this gets deleted by a callback)
|
||||
listeners.call ([&] (Value::Listener& l) { l.valueChanged (v); });
|
||||
}
|
||||
}
|
||||
|
||||
OutputStream& JUCE_CALLTYPE operator<< (OutputStream& stream, const Value& value)
|
||||
{
|
||||
return stream << value.toString();
|
||||
}
|
||||
|
||||
} // namespace juce
|
242
deps/juce/modules/juce_data_structures/values/juce_Value.h
vendored
Normal file
242
deps/juce/modules/juce_data_structures/values/juce_Value.h
vendored
Normal file
@ -0,0 +1,242 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Represents a shared variant value.
|
||||
|
||||
A Value object contains a reference to a var object, and can get and set its value.
|
||||
Listeners can be attached to be told when the value is changed.
|
||||
|
||||
The Value class is a wrapper around a shared, reference-counted underlying data
|
||||
object - this means that multiple Value objects can all refer to the same piece of
|
||||
data, allowing all of them to be notified when any of them changes it.
|
||||
|
||||
When you create a Value with its default constructor, it acts as a wrapper around a
|
||||
simple var object, but by creating a Value that refers to a custom subclass of ValueSource,
|
||||
you can map the Value onto any kind of underlying data.
|
||||
|
||||
Important note! The Value class is not thread-safe! If you're accessing one from
|
||||
multiple threads, then you'll need to use your own synchronisation around any code
|
||||
that accesses it.
|
||||
|
||||
@tags{DataStructures}
|
||||
*/
|
||||
class JUCE_API Value final
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates an empty Value, containing a void var. */
|
||||
Value();
|
||||
|
||||
/** Creates a Value that refers to the same value as another one.
|
||||
|
||||
Note that this doesn't make a copy of the other value - both this and the other
|
||||
Value will share the same underlying value, so that when either one alters it, both
|
||||
will see it change.
|
||||
*/
|
||||
Value (const Value& other);
|
||||
|
||||
/** Creates a Value that is set to the specified value. */
|
||||
explicit Value (const var& initialValue);
|
||||
|
||||
/** Move constructor */
|
||||
Value (Value&&) noexcept;
|
||||
|
||||
/** Destructor. */
|
||||
~Value();
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the current value. */
|
||||
var getValue() const;
|
||||
|
||||
/** Returns the current value. */
|
||||
operator var() const;
|
||||
|
||||
/** Returns the value as a string.
|
||||
This is a shortcut for "myValue.getValue().toString()".
|
||||
*/
|
||||
String toString() const;
|
||||
|
||||
/** Sets the current value.
|
||||
|
||||
You can also use operator= to set the value.
|
||||
|
||||
If there are any listeners registered, they will be notified of the
|
||||
change asynchronously.
|
||||
*/
|
||||
void setValue (const var& newValue);
|
||||
|
||||
/** Sets the current value.
|
||||
|
||||
This is the same as calling setValue().
|
||||
|
||||
If there are any listeners registered, they will be notified of the
|
||||
change asynchronously.
|
||||
*/
|
||||
Value& operator= (const var& newValue);
|
||||
|
||||
/** Move assignment operator */
|
||||
Value& operator= (Value&&) noexcept;
|
||||
|
||||
/** Makes this object refer to the same underlying ValueSource as another one.
|
||||
|
||||
Once this object has been connected to another one, changing either one
|
||||
will update the other.
|
||||
|
||||
Existing listeners will still be registered after you call this method, and
|
||||
they'll continue to receive messages when the new value changes.
|
||||
*/
|
||||
void referTo (const Value& valueToReferTo);
|
||||
|
||||
/** Returns true if this value and the other one are references to the same value.
|
||||
*/
|
||||
bool refersToSameSourceAs (const Value& other) const;
|
||||
|
||||
/** Compares two values.
|
||||
This is a compare-by-value comparison, so is effectively the same as
|
||||
saying (this->getValue() == other.getValue()).
|
||||
*/
|
||||
bool operator== (const Value& other) const;
|
||||
|
||||
/** Compares two values.
|
||||
This is a compare-by-value comparison, so is effectively the same as
|
||||
saying (this->getValue() != other.getValue()).
|
||||
*/
|
||||
bool operator!= (const Value& other) const;
|
||||
|
||||
//==============================================================================
|
||||
/** Receives callbacks when a Value object changes.
|
||||
@see Value::addListener
|
||||
*/
|
||||
class JUCE_API Listener
|
||||
{
|
||||
public:
|
||||
Listener() = default;
|
||||
virtual ~Listener() = default;
|
||||
|
||||
/** Called when a Value object is changed.
|
||||
|
||||
Note that the Value object passed as a parameter may not be exactly the same
|
||||
object that you registered the listener with - it might be a copy that refers
|
||||
to the same underlying ValueSource. To find out, you can call Value::refersToSameSourceAs().
|
||||
*/
|
||||
virtual void valueChanged (Value& value) = 0;
|
||||
};
|
||||
|
||||
/** Adds a listener to receive callbacks when the value changes.
|
||||
|
||||
The listener is added to this specific Value object, and not to the shared
|
||||
object that it refers to. When this object is deleted, all the listeners will
|
||||
be lost, even if other references to the same Value still exist. So when you're
|
||||
adding a listener, make sure that you add it to a Value instance that will last
|
||||
for as long as you need the listener. In general, you'd never want to add a listener
|
||||
to a local stack-based Value, but more likely to one that's a member variable.
|
||||
|
||||
@see removeListener
|
||||
*/
|
||||
void addListener (Listener* listener);
|
||||
|
||||
/** Removes a listener that was previously added with addListener(). */
|
||||
void removeListener (Listener* listener);
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Used internally by the Value class as the base class for its shared value objects.
|
||||
|
||||
The Value class is essentially a reference-counted pointer to a shared instance
|
||||
of a ValueSource object. If you're feeling adventurous, you can create your own custom
|
||||
ValueSource classes to allow Value objects to represent your own custom data items.
|
||||
*/
|
||||
class JUCE_API ValueSource : public ReferenceCountedObject,
|
||||
private AsyncUpdater
|
||||
{
|
||||
public:
|
||||
ValueSource();
|
||||
~ValueSource() override;
|
||||
|
||||
/** Returns the current value of this object. */
|
||||
virtual var getValue() const = 0;
|
||||
|
||||
/** Changes the current value.
|
||||
This must also trigger a change message if the value actually changes.
|
||||
*/
|
||||
virtual void setValue (const var& newValue) = 0;
|
||||
|
||||
/** Delivers a change message to all the listeners that are registered with
|
||||
this value.
|
||||
|
||||
If dispatchSynchronously is true, the method will call all the listeners
|
||||
before returning; otherwise it'll dispatch a message and make the call later.
|
||||
*/
|
||||
void sendChangeMessage (bool dispatchSynchronously);
|
||||
|
||||
protected:
|
||||
//==============================================================================
|
||||
friend class Value;
|
||||
SortedSet<Value*> valuesWithListeners;
|
||||
|
||||
private:
|
||||
void handleAsyncUpdate() override;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ValueSource)
|
||||
};
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/** Creates a Value object that uses this valueSource object as its underlying data. */
|
||||
explicit Value (ValueSource* valueSource);
|
||||
|
||||
/** Returns the ValueSource that this value is referring to. */
|
||||
ValueSource& getValueSource() noexcept { return *value; }
|
||||
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
friend class ValueSource;
|
||||
ReferenceCountedObjectPtr<ValueSource> value;
|
||||
ListenerList<Listener> listeners;
|
||||
|
||||
void callListeners();
|
||||
void removeFromListenerList();
|
||||
|
||||
// This is disallowed to avoid confusion about whether it should
|
||||
// do a by-value or by-reference copy.
|
||||
Value& operator= (const Value&) = delete;
|
||||
|
||||
// This declaration prevents accidental construction from an integer of 0,
|
||||
// which is possible in some compilers via an implicit cast to a pointer.
|
||||
explicit Value (void*) = delete;
|
||||
};
|
||||
|
||||
/** Writes a Value to an OutputStream as a UTF8 string. */
|
||||
OutputStream& JUCE_CALLTYPE operator<< (OutputStream&, const Value&);
|
||||
|
||||
|
||||
} // namespace juce
|
1251
deps/juce/modules/juce_data_structures/values/juce_ValueTree.cpp
vendored
Normal file
1251
deps/juce/modules/juce_data_structures/values/juce_ValueTree.cpp
vendored
Normal file
File diff suppressed because it is too large
Load Diff
645
deps/juce/modules/juce_data_structures/values/juce_ValueTree.h
vendored
Normal file
645
deps/juce/modules/juce_data_structures/values/juce_ValueTree.h
vendored
Normal file
@ -0,0 +1,645 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A powerful tree structure that can be used to hold free-form data, and which can
|
||||
handle its own undo and redo behaviour.
|
||||
|
||||
A ValueTree contains a list of named properties as var objects, and also holds
|
||||
any number of sub-trees.
|
||||
|
||||
Create ValueTree objects on the stack, and don't be afraid to copy them around, as
|
||||
they're simply a lightweight reference to a shared data container. Creating a copy
|
||||
of another ValueTree simply creates a new reference to the same underlying object - to
|
||||
make a separate, deep copy of a tree you should explicitly call createCopy().
|
||||
|
||||
Each ValueTree has a type name, in much the same way as an XmlElement has a tag name,
|
||||
and much of the structure of a ValueTree is similar to an XmlElement tree.
|
||||
You can convert a ValueTree to and from an XmlElement, and as long as the XML doesn't
|
||||
contain text elements, the conversion works well and makes a good serialisation
|
||||
format. They can also be serialised to a binary format, which is very fast and compact.
|
||||
|
||||
All the methods that change data take an optional UndoManager, which will be used
|
||||
to track any changes to the object. For this to work, you have to be careful to
|
||||
consistently always use the same UndoManager for all operations to any sub-tree inside
|
||||
the tree.
|
||||
|
||||
A ValueTree can only be a child of one parent at a time, so if you're moving one from
|
||||
one tree to another, be careful to always remove it first, before adding it. This
|
||||
could also mess up your undo/redo chain, so be wary! In a debug build you should hit
|
||||
assertions if you try to do anything dangerous, but there are still plenty of ways it
|
||||
could go wrong.
|
||||
|
||||
Note that although the children in a tree have a fixed order, the properties are not
|
||||
guaranteed to be stored in any particular order, so don't expect that a property's index
|
||||
will correspond to the order in which the property was added, or that it will remain
|
||||
constant when other properties are added or removed.
|
||||
|
||||
Listeners can be added to a ValueTree to be told when properties change and when
|
||||
sub-trees are added or removed.
|
||||
|
||||
@see var, XmlElement
|
||||
|
||||
@tags{DataStructures}
|
||||
*/
|
||||
class JUCE_API ValueTree final
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates an empty, invalid ValueTree.
|
||||
|
||||
A ValueTree that is created with this constructor can't actually be used for anything,
|
||||
it's just a default 'null' ValueTree that can be returned to indicate some sort of failure.
|
||||
To create a real one, use the constructor that takes a string.
|
||||
*/
|
||||
ValueTree() noexcept;
|
||||
|
||||
/** Creates an empty ValueTree with the given type name.
|
||||
|
||||
Like an XmlElement, each ValueTree has a type, which you can access with
|
||||
getType() and hasType().
|
||||
*/
|
||||
explicit ValueTree (const Identifier& type);
|
||||
|
||||
/** Creates a value tree from nested lists of properties and ValueTrees.
|
||||
|
||||
This code,
|
||||
|
||||
@code
|
||||
ValueTree groups
|
||||
{ "ParameterGroups", {},
|
||||
{
|
||||
{ "Group", {{ "name", "Tone Controls" }},
|
||||
{
|
||||
{ "Parameter", {{ "id", "distortion" }, { "value", 0.5 }}},
|
||||
{ "Parameter", {{ "id", "reverb" }, { "value", 0.5 }}}
|
||||
}
|
||||
},
|
||||
{ "Group", {{ "name", "Other Controls" }},
|
||||
{
|
||||
{ "Parameter", {{ "id", "drywet" }, { "value", 0.5 }}},
|
||||
{ "Parameter", {{ "id", "gain" }, { "value", 0.5 }}}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
@endcode
|
||||
|
||||
produces this tree:
|
||||
|
||||
@verbatim
|
||||
<ParameterGroups>
|
||||
<Group name="Tone Controls">
|
||||
<Parameter id="distortion" value="0.5"/>
|
||||
<Parameter id="reverb" value="0.5"/>
|
||||
</Group>
|
||||
<Group name="Other Controls">
|
||||
<Parameter id="drywet" value="0.5"/>
|
||||
<Parameter id="gain" value="0.5"/>
|
||||
</Group>
|
||||
</ParameterGroups>
|
||||
@endverbatim
|
||||
*/
|
||||
ValueTree (const Identifier& type,
|
||||
std::initializer_list<NamedValueSet::NamedValue> properties,
|
||||
std::initializer_list<ValueTree> subTrees = {});
|
||||
|
||||
/** Creates a reference to another ValueTree. */
|
||||
ValueTree (const ValueTree&) noexcept;
|
||||
|
||||
/** Move constructor */
|
||||
ValueTree (ValueTree&&) noexcept;
|
||||
|
||||
/** Changes this object to be a reference to the given tree.
|
||||
Note that calling this just points this at the new object and invokes the
|
||||
Listener::valueTreeRedirected callback, but it's not an undoable operation. If
|
||||
you're trying to replace an entire tree in an undoable way, you probably want
|
||||
to use copyPropertiesAndChildrenFrom() instead.
|
||||
*/
|
||||
ValueTree& operator= (const ValueTree&);
|
||||
|
||||
/** Destructor. */
|
||||
~ValueTree();
|
||||
|
||||
/** Returns true if both this and the other tree refer to the same underlying structure.
|
||||
Note that this isn't a value comparison - two independently-created trees which
|
||||
contain identical data are NOT considered equal.
|
||||
*/
|
||||
bool operator== (const ValueTree&) const noexcept;
|
||||
|
||||
/** Returns true if this and the other tree refer to different underlying structures.
|
||||
Note that this isn't a value comparison - two independently-created trees which
|
||||
contain identical data are not considered equal.
|
||||
*/
|
||||
bool operator!= (const ValueTree&) const noexcept;
|
||||
|
||||
/** Performs a deep comparison between the properties and children of two trees.
|
||||
If all the properties and children of the two trees are the same (recursively), this
|
||||
returns true.
|
||||
The normal operator==() only checks whether two trees refer to the same shared data
|
||||
structure, so use this method if you need to do a proper value comparison.
|
||||
*/
|
||||
bool isEquivalentTo (const ValueTree&) const;
|
||||
|
||||
//==============================================================================
|
||||
/** Returns true if this tree refers to some valid data.
|
||||
An invalid tree is one that was created with the default constructor.
|
||||
*/
|
||||
bool isValid() const noexcept { return object != nullptr; }
|
||||
|
||||
/** Returns a deep copy of this tree and all its sub-trees. */
|
||||
ValueTree createCopy() const;
|
||||
|
||||
/** Overwrites all the properties in this tree with the properties of the source tree.
|
||||
Any properties that already exist will be updated; and new ones will be added, and
|
||||
any that are not present in the source tree will be removed.
|
||||
@see copyPropertiesAndChildrenFrom
|
||||
*/
|
||||
void copyPropertiesFrom (const ValueTree& source, UndoManager* undoManager);
|
||||
|
||||
/** Replaces all children and properties of this object with copies of those from
|
||||
the source object.
|
||||
@see copyPropertiesFrom
|
||||
*/
|
||||
void copyPropertiesAndChildrenFrom (const ValueTree& source, UndoManager* undoManager);
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the type of this tree.
|
||||
The type is specified when the ValueTree is created.
|
||||
@see hasType
|
||||
*/
|
||||
Identifier getType() const noexcept;
|
||||
|
||||
/** Returns true if the tree has this type.
|
||||
The comparison is case-sensitive.
|
||||
@see getType
|
||||
*/
|
||||
bool hasType (const Identifier& typeName) const noexcept;
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the value of a named property.
|
||||
If no such property has been set, this will return a void variant.
|
||||
You can also use operator[] to get a property.
|
||||
@see var, setProperty, getPropertyPointer, hasProperty
|
||||
*/
|
||||
const var& getProperty (const Identifier& name) const noexcept;
|
||||
|
||||
/** Returns the value of a named property, or the value of defaultReturnValue
|
||||
if the property doesn't exist.
|
||||
You can also use operator[] and getProperty to get a property.
|
||||
@see var, getProperty, getPropertyPointer, setProperty, hasProperty
|
||||
*/
|
||||
var getProperty (const Identifier& name, const var& defaultReturnValue) const;
|
||||
|
||||
/** Returns a pointer to the value of a named property, or nullptr if the property
|
||||
doesn't exist.
|
||||
@see var, getProperty, setProperty, hasProperty
|
||||
*/
|
||||
const var* getPropertyPointer (const Identifier& name) const noexcept;
|
||||
|
||||
/** Returns the value of a named property.
|
||||
If no such property has been set, this will return a void variant. This is the same as
|
||||
calling getProperty().
|
||||
@see getProperty
|
||||
*/
|
||||
const var& operator[] (const Identifier& name) const noexcept;
|
||||
|
||||
/** Changes a named property of the tree.
|
||||
The name identifier must not be an empty string.
|
||||
If the undoManager parameter is not nullptr, its UndoManager::perform() method will be used,
|
||||
so that this change can be undone. Be very careful not to mix undoable and non-undoable changes!
|
||||
@see var, getProperty, removeProperty
|
||||
@returns a reference to the value tree, so that you can daisy-chain calls to this method.
|
||||
*/
|
||||
ValueTree& setProperty (const Identifier& name, const var& newValue, UndoManager* undoManager);
|
||||
|
||||
/** Returns true if the tree contains a named property. */
|
||||
bool hasProperty (const Identifier& name) const noexcept;
|
||||
|
||||
/** Removes a property from the tree.
|
||||
If the undoManager parameter is not nullptr, its UndoManager::perform() method will be used,
|
||||
so that this change can be undone. Be very careful not to mix undoable and non-undoable changes!
|
||||
*/
|
||||
void removeProperty (const Identifier& name, UndoManager* undoManager);
|
||||
|
||||
/** Removes all properties from the tree.
|
||||
If the undoManager parameter is not nullptr, its UndoManager::perform() method will be used,
|
||||
so that this change can be undone. Be very careful not to mix undoable and non-undoable changes!
|
||||
*/
|
||||
void removeAllProperties (UndoManager* undoManager);
|
||||
|
||||
/** Returns the total number of properties that the tree contains.
|
||||
@see getProperty.
|
||||
*/
|
||||
int getNumProperties() const noexcept;
|
||||
|
||||
/** Returns the identifier of the property with a given index.
|
||||
Note that properties are not guaranteed to be stored in any particular order, so don't
|
||||
expect that the index will correspond to the order in which the property was added, or
|
||||
that it will remain constant when other properties are added or removed.
|
||||
@see getNumProperties
|
||||
*/
|
||||
Identifier getPropertyName (int index) const noexcept;
|
||||
|
||||
/** Returns a Value object that can be used to control and respond to one of the tree's properties.
|
||||
|
||||
The Value object will maintain a reference to this tree, and will use the undo manager when
|
||||
it needs to change the value. Attaching a Value::Listener to the value object will provide
|
||||
callbacks whenever the property changes.
|
||||
If shouldUpdateSynchronously is true the Value::Listener will be updated synchronously.
|
||||
@see ValueSource::sendChangeMessage (bool)
|
||||
*/
|
||||
Value getPropertyAsValue (const Identifier& name, UndoManager* undoManager,
|
||||
bool shouldUpdateSynchronously = false);
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the number of child trees inside this one.
|
||||
@see getChild
|
||||
*/
|
||||
int getNumChildren() const noexcept;
|
||||
|
||||
/** Returns one of this tree's sub-trees.
|
||||
If the index is out of range, it'll return an invalid tree. (You can use isValid() to
|
||||
check whether a tree is valid)
|
||||
*/
|
||||
ValueTree getChild (int index) const;
|
||||
|
||||
/** Returns the first sub-tree with the specified type name.
|
||||
If no such child tree exists, it'll return an invalid tree. (You can use isValid() to
|
||||
check whether a tree is valid)
|
||||
@see getOrCreateChildWithName
|
||||
*/
|
||||
ValueTree getChildWithName (const Identifier& type) const;
|
||||
|
||||
/** Returns the first sub-tree with the specified type name, creating and adding
|
||||
a child with this name if there wasn't already one there.
|
||||
The only time this will return an invalid object is when the object that you're calling
|
||||
the method on is itself invalid.
|
||||
@see getChildWithName
|
||||
*/
|
||||
ValueTree getOrCreateChildWithName (const Identifier& type, UndoManager* undoManager);
|
||||
|
||||
/** Looks for the first sub-tree that has the specified property value.
|
||||
This will scan the child trees in order, until it finds one that has property that matches
|
||||
the specified value.
|
||||
If no such tree is found, it'll return an invalid object. (You can use isValid() to
|
||||
check whether a tree is valid)
|
||||
*/
|
||||
ValueTree getChildWithProperty (const Identifier& propertyName, const var& propertyValue) const;
|
||||
|
||||
/** Adds a child to this tree.
|
||||
Make sure that the child being added has first been removed from any former parent before
|
||||
calling this, or else you'll hit an assertion.
|
||||
If the index is < 0 or greater than the current number of sub-trees, the new one will be
|
||||
added at the end of the list.
|
||||
If the undoManager parameter is not nullptr, its UndoManager::perform() method will be used,
|
||||
so that this change can be undone. Be very careful not to mix undoable and non-undoable changes!
|
||||
@see appendChild, removeChild
|
||||
*/
|
||||
void addChild (const ValueTree& child, int index, UndoManager* undoManager);
|
||||
|
||||
/** Appends a new child sub-tree to this tree.
|
||||
This is equivalent to calling addChild() with an index of -1. See addChild() for more details.
|
||||
@see addChild, removeChild
|
||||
*/
|
||||
void appendChild (const ValueTree& child, UndoManager* undoManager);
|
||||
|
||||
/** Removes the specified child from this tree's child-list.
|
||||
If the undoManager parameter is not nullptr, its UndoManager::perform() method will be used,
|
||||
so that this change can be undone. Be very careful not to mix undoable and non-undoable changes!
|
||||
*/
|
||||
void removeChild (const ValueTree& child, UndoManager* undoManager);
|
||||
|
||||
/** Removes a sub-tree from this tree.
|
||||
If the index is out-of-range, nothing will be changed.
|
||||
If the undoManager parameter is not nullptr, its UndoManager::perform() method will be used,
|
||||
so that this change can be undone. Be very careful not to mix undoable and non-undoable changes!
|
||||
*/
|
||||
void removeChild (int childIndex, UndoManager* undoManager);
|
||||
|
||||
/** Removes all child-trees.
|
||||
If the undoManager parameter is not nullptr, its UndoManager::perform() method will be used,
|
||||
so that this change can be undone. Be very careful not to mix undoable and non-undoable changes!
|
||||
*/
|
||||
void removeAllChildren (UndoManager* undoManager);
|
||||
|
||||
/** Moves one of the sub-trees to a different index.
|
||||
This will move the child to a specified index, shuffling along any intervening
|
||||
items as required. So for example, if you have a list of { 0, 1, 2, 3, 4, 5 }, then
|
||||
calling move (2, 4) would result in { 0, 1, 3, 4, 2, 5 }.
|
||||
|
||||
@param currentIndex the index of the item to be moved. If this isn't a
|
||||
valid index, then nothing will be done
|
||||
@param newIndex the index at which you'd like this item to end up. If this
|
||||
is less than zero, the value will be moved to the end
|
||||
of the list
|
||||
@param undoManager the optional UndoManager to use to store this transaction
|
||||
*/
|
||||
void moveChild (int currentIndex, int newIndex, UndoManager* undoManager);
|
||||
|
||||
/** Returns true if this tree is a sub-tree (at any depth) of the given parent.
|
||||
This searches recursively, so returns true if it's a sub-tree at any level below the parent.
|
||||
*/
|
||||
bool isAChildOf (const ValueTree& possibleParent) const noexcept;
|
||||
|
||||
/** Returns the index of a child item in this parent.
|
||||
If the child isn't found, this returns -1.
|
||||
*/
|
||||
int indexOf (const ValueTree& child) const noexcept;
|
||||
|
||||
/** Returns the parent tree that contains this one.
|
||||
If the tree has no parent, this will return an invalid object. (You can use isValid() to
|
||||
check whether a tree is valid)
|
||||
*/
|
||||
ValueTree getParent() const noexcept;
|
||||
|
||||
/** Recursively finds the highest-level parent tree that contains this one.
|
||||
If the tree has no parent, this will return itself.
|
||||
*/
|
||||
ValueTree getRoot() const noexcept;
|
||||
|
||||
/** Returns one of this tree's siblings in its parent's child list.
|
||||
The delta specifies how far to move through the list, so a value of 1 would return the tree
|
||||
that follows this one, -1 would return the tree before it, 0 will return this one, etc.
|
||||
If the requested position is beyond the start or end of the child list, this will return an
|
||||
invalid object.
|
||||
*/
|
||||
ValueTree getSibling (int delta) const noexcept;
|
||||
|
||||
//==============================================================================
|
||||
/** Iterator for a ValueTree.
|
||||
You shouldn't ever need to use this class directly - it's used internally by ValueTree::begin()
|
||||
and ValueTree::end() to allow range-based-for loops on a ValueTree.
|
||||
*/
|
||||
struct Iterator
|
||||
{
|
||||
Iterator (const ValueTree&, bool isEnd);
|
||||
Iterator& operator++();
|
||||
|
||||
bool operator== (const Iterator&) const;
|
||||
bool operator!= (const Iterator&) const;
|
||||
ValueTree operator*() const;
|
||||
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using value_type = ValueTree;
|
||||
using reference = ValueTree&;
|
||||
using pointer = ValueTree*;
|
||||
using iterator_category = std::forward_iterator_tag;
|
||||
|
||||
private:
|
||||
void* internal;
|
||||
};
|
||||
|
||||
/** Returns a start iterator for the children in this tree. */
|
||||
Iterator begin() const noexcept;
|
||||
|
||||
/** Returns an end iterator for the children in this tree. */
|
||||
Iterator end() const noexcept;
|
||||
|
||||
//==============================================================================
|
||||
/** Creates an XmlElement that holds a complete image of this tree and all its children.
|
||||
If this tree is invalid, this may return nullptr. Otherwise, the XML that is produced can
|
||||
be used to recreate a similar tree by calling ValueTree::fromXml().
|
||||
@see fromXml, toXmlString
|
||||
*/
|
||||
std::unique_ptr<XmlElement> createXml() const;
|
||||
|
||||
/** Tries to recreate a tree from its XML representation.
|
||||
This isn't designed to cope with random XML data - it should only be fed XML that was created
|
||||
by the createXml() method.
|
||||
*/
|
||||
static ValueTree fromXml (const XmlElement& xml);
|
||||
|
||||
/** Tries to recreate a tree from its XML representation.
|
||||
This isn't designed to cope with random XML data - it should only be fed XML that was created
|
||||
by the createXml() method.
|
||||
*/
|
||||
static ValueTree fromXml (const String& xmlText);
|
||||
|
||||
/** This returns a string containing an XML representation of the tree.
|
||||
This is quite handy for debugging purposes, as it provides a quick way to view a tree.
|
||||
@see createXml()
|
||||
*/
|
||||
String toXmlString (const XmlElement::TextFormat& format = {}) const;
|
||||
|
||||
//==============================================================================
|
||||
/** Stores this tree (and all its children) in a binary format.
|
||||
|
||||
Once written, the data can be read back with readFromStream().
|
||||
|
||||
It's much faster to load/save your tree in binary form than as XML, but
|
||||
obviously isn't human-readable.
|
||||
*/
|
||||
void writeToStream (OutputStream& output) const;
|
||||
|
||||
/** Reloads a tree from a stream that was written with writeToStream(). */
|
||||
static ValueTree readFromStream (InputStream& input);
|
||||
|
||||
/** Reloads a tree from a data block that was written with writeToStream(). */
|
||||
static ValueTree readFromData (const void* data, size_t numBytes);
|
||||
|
||||
/** Reloads a tree from a data block that was written with writeToStream() and
|
||||
then zipped using GZIPCompressorOutputStream.
|
||||
*/
|
||||
static ValueTree readFromGZIPData (const void* data, size_t numBytes);
|
||||
|
||||
//==============================================================================
|
||||
/** Listener class for events that happen to a ValueTree.
|
||||
|
||||
To get events from a ValueTree, make your class implement this interface, and use
|
||||
ValueTree::addListener() and ValueTree::removeListener() to register it.
|
||||
*/
|
||||
class JUCE_API Listener
|
||||
{
|
||||
public:
|
||||
/** Destructor. */
|
||||
virtual ~Listener() = default;
|
||||
|
||||
/** This method is called when a property of this tree (or of one of its sub-trees) is changed.
|
||||
Note that when you register a listener to a tree, it will receive this callback for
|
||||
property changes in that tree, and also for any of its children, (recursively, at any depth).
|
||||
If your tree has sub-trees but you only want to know about changes to the top level tree,
|
||||
simply check the tree parameter in this callback to make sure it's the tree you're interested in.
|
||||
*/
|
||||
virtual void valueTreePropertyChanged (ValueTree& treeWhosePropertyHasChanged,
|
||||
const Identifier& property);
|
||||
|
||||
/** This method is called when a child sub-tree is added.
|
||||
Note that when you register a listener to a tree, it will receive this callback for
|
||||
child changes in both that tree and any of its children, (recursively, at any depth).
|
||||
If your tree has sub-trees but you only want to know about changes to the top level tree,
|
||||
just check the parentTree parameter to make sure it's the one that you're interested in.
|
||||
*/
|
||||
virtual void valueTreeChildAdded (ValueTree& parentTree,
|
||||
ValueTree& childWhichHasBeenAdded);
|
||||
|
||||
/** This method is called when a child sub-tree is removed.
|
||||
|
||||
Note that when you register a listener to a tree, it will receive this callback for
|
||||
child changes in both that tree and any of its children, (recursively, at any depth).
|
||||
If your tree has sub-trees but you only want to know about changes to the top level tree,
|
||||
just check the parentTree parameter to make sure it's the one that you're interested in.
|
||||
*/
|
||||
virtual void valueTreeChildRemoved (ValueTree& parentTree,
|
||||
ValueTree& childWhichHasBeenRemoved,
|
||||
int indexFromWhichChildWasRemoved);
|
||||
|
||||
/** This method is called when a tree's children have been re-shuffled.
|
||||
|
||||
Note that when you register a listener to a tree, it will receive this callback for
|
||||
child changes in both that tree and any of its children, (recursively, at any depth).
|
||||
If your tree has sub-trees but you only want to know about changes to the top level tree,
|
||||
just check the parameter to make sure it's the tree that you're interested in.
|
||||
*/
|
||||
virtual void valueTreeChildOrderChanged (ValueTree& parentTreeWhoseChildrenHaveMoved,
|
||||
int oldIndex, int newIndex);
|
||||
|
||||
/** This method is called when a tree has been added or removed from a parent.
|
||||
|
||||
This callback happens when the tree to which the listener was registered is added or
|
||||
removed from a parent. Unlike the other callbacks, it applies only to the tree to which
|
||||
the listener is registered, and not to any of its children.
|
||||
*/
|
||||
virtual void valueTreeParentChanged (ValueTree& treeWhoseParentHasChanged);
|
||||
|
||||
/** This method is called when a tree is made to point to a different internal shared object.
|
||||
When operator= is used to make a ValueTree refer to a different object, this callback
|
||||
will be made.
|
||||
*/
|
||||
virtual void valueTreeRedirected (ValueTree& treeWhichHasBeenChanged);
|
||||
};
|
||||
|
||||
/** Adds a listener to receive callbacks when this tree is changed in some way.
|
||||
|
||||
The listener is added to this specific ValueTree object, and not to the shared
|
||||
object that it refers to. When this object is deleted, all the listeners will
|
||||
be lost, even if other references to the same ValueTree still exist. And if you
|
||||
use the operator= to make this refer to a different ValueTree, any listeners will
|
||||
begin listening to changes to the new tree instead of the old one.
|
||||
|
||||
When you're adding a listener, make sure that you add it to a ValueTree instance that
|
||||
will last for as long as you need the listener. In general, you'd never want to add a
|
||||
listener to a local stack-based ValueTree, and would usually add one to a member variable.
|
||||
|
||||
@see removeListener
|
||||
*/
|
||||
void addListener (Listener* listener);
|
||||
|
||||
/** Removes a listener that was previously added with addListener(). */
|
||||
void removeListener (Listener* listener);
|
||||
|
||||
/** Changes a named property of the tree, but will not notify a specified listener of the change.
|
||||
@see setProperty
|
||||
*/
|
||||
ValueTree& setPropertyExcludingListener (Listener* listenerToExclude,
|
||||
const Identifier& name, const var& newValue,
|
||||
UndoManager* undoManager);
|
||||
|
||||
/** Causes a property-change callback to be triggered for the specified property,
|
||||
calling any listeners that are registered.
|
||||
*/
|
||||
void sendPropertyChangeMessage (const Identifier& property);
|
||||
|
||||
//==============================================================================
|
||||
/** This method uses a comparator object to sort the tree's children into order.
|
||||
|
||||
The object provided must have a method of the form:
|
||||
@code
|
||||
int compareElements (const ValueTree& first, const ValueTree& second);
|
||||
@endcode
|
||||
|
||||
..and this method must return:
|
||||
- a value of < 0 if the first comes before the second
|
||||
- a value of 0 if the two objects are equivalent
|
||||
- a value of > 0 if the second comes before the first
|
||||
|
||||
To improve performance, the compareElements() method can be declared as static or const.
|
||||
|
||||
@param comparator the comparator to use for comparing elements.
|
||||
@param undoManager optional UndoManager for storing the changes
|
||||
@param retainOrderOfEquivalentItems if this is true, then items which the comparator says are
|
||||
equivalent will be kept in the order in which they currently appear in the array.
|
||||
This is slower to perform, but may be important in some cases. If it's false, a
|
||||
faster algorithm is used, but equivalent elements may be rearranged.
|
||||
*/
|
||||
template <typename ElementComparator>
|
||||
void sort (ElementComparator& comparator, UndoManager* undoManager, bool retainOrderOfEquivalentItems)
|
||||
{
|
||||
if (object != nullptr)
|
||||
{
|
||||
OwnedArray<ValueTree> sortedList;
|
||||
createListOfChildren (sortedList);
|
||||
ComparatorAdapter<ElementComparator> adapter (comparator);
|
||||
sortedList.sort (adapter, retainOrderOfEquivalentItems);
|
||||
reorderChildren (sortedList, undoManager);
|
||||
}
|
||||
}
|
||||
|
||||
/** Returns the total number of references to the shared underlying data structure that this
|
||||
ValueTree is using.
|
||||
*/
|
||||
int getReferenceCount() const noexcept;
|
||||
|
||||
#if JUCE_ALLOW_STATIC_NULL_VARIABLES && ! defined (DOXYGEN)
|
||||
/* An invalid ValueTree that can be used if you need to return one as an error condition, etc. */
|
||||
[[deprecated ("If you need an empty ValueTree object, just use ValueTree() or {}.")]]
|
||||
static const ValueTree invalid;
|
||||
#endif
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
JUCE_PUBLIC_IN_DLL_BUILD (class SharedObject)
|
||||
friend class SharedObject;
|
||||
|
||||
ReferenceCountedObjectPtr<SharedObject> object;
|
||||
ListenerList<Listener> listeners;
|
||||
|
||||
template <typename ElementComparator>
|
||||
struct ComparatorAdapter
|
||||
{
|
||||
ComparatorAdapter (ElementComparator& comp) noexcept : comparator (comp) {}
|
||||
|
||||
int compareElements (const ValueTree* const first, const ValueTree* const second)
|
||||
{
|
||||
return comparator.compareElements (*first, *second);
|
||||
}
|
||||
|
||||
private:
|
||||
ElementComparator& comparator;
|
||||
JUCE_DECLARE_NON_COPYABLE (ComparatorAdapter)
|
||||
};
|
||||
|
||||
void createListOfChildren (OwnedArray<ValueTree>&) const;
|
||||
void reorderChildren (const OwnedArray<ValueTree>&, UndoManager*);
|
||||
|
||||
explicit ValueTree (ReferenceCountedObjectPtr<SharedObject>) noexcept;
|
||||
explicit ValueTree (SharedObject&) noexcept;
|
||||
};
|
||||
|
||||
} // namespace juce
|
242
deps/juce/modules/juce_data_structures/values/juce_ValueTreeSynchroniser.cpp
vendored
Normal file
242
deps/juce/modules/juce_data_structures/values/juce_ValueTreeSynchroniser.cpp
vendored
Normal file
@ -0,0 +1,242 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
namespace ValueTreeSynchroniserHelpers
|
||||
{
|
||||
enum ChangeType
|
||||
{
|
||||
propertyChanged = 1,
|
||||
fullSync = 2,
|
||||
childAdded = 3,
|
||||
childRemoved = 4,
|
||||
childMoved = 5,
|
||||
propertyRemoved = 6
|
||||
};
|
||||
|
||||
static void getValueTreePath (ValueTree v, const ValueTree& topLevelTree, Array<int>& path)
|
||||
{
|
||||
while (v != topLevelTree)
|
||||
{
|
||||
ValueTree parent (v.getParent());
|
||||
|
||||
if (! parent.isValid())
|
||||
break;
|
||||
|
||||
path.add (parent.indexOf (v));
|
||||
v = parent;
|
||||
}
|
||||
}
|
||||
|
||||
static void writeHeader (MemoryOutputStream& stream, ChangeType type)
|
||||
{
|
||||
stream.writeByte ((char) type);
|
||||
}
|
||||
|
||||
static void writeHeader (ValueTreeSynchroniser& target, MemoryOutputStream& stream,
|
||||
ChangeType type, ValueTree v)
|
||||
{
|
||||
writeHeader (stream, type);
|
||||
|
||||
Array<int> path;
|
||||
getValueTreePath (v, target.getRoot(), path);
|
||||
|
||||
stream.writeCompressedInt (path.size());
|
||||
|
||||
for (int i = path.size(); --i >= 0;)
|
||||
stream.writeCompressedInt (path.getUnchecked(i));
|
||||
}
|
||||
|
||||
static ValueTree readSubTreeLocation (MemoryInputStream& input, ValueTree v)
|
||||
{
|
||||
const int numLevels = input.readCompressedInt();
|
||||
|
||||
if (! isPositiveAndBelow (numLevels, 65536)) // sanity-check
|
||||
return {};
|
||||
|
||||
for (int i = numLevels; --i >= 0;)
|
||||
{
|
||||
const int index = input.readCompressedInt();
|
||||
|
||||
if (! isPositiveAndBelow (index, v.getNumChildren()))
|
||||
return {};
|
||||
|
||||
v = v.getChild (index);
|
||||
}
|
||||
|
||||
return v;
|
||||
}
|
||||
}
|
||||
|
||||
ValueTreeSynchroniser::ValueTreeSynchroniser (const ValueTree& tree) : valueTree (tree)
|
||||
{
|
||||
valueTree.addListener (this);
|
||||
}
|
||||
|
||||
ValueTreeSynchroniser::~ValueTreeSynchroniser()
|
||||
{
|
||||
valueTree.removeListener (this);
|
||||
}
|
||||
|
||||
void ValueTreeSynchroniser::sendFullSyncCallback()
|
||||
{
|
||||
MemoryOutputStream m;
|
||||
writeHeader (m, ValueTreeSynchroniserHelpers::fullSync);
|
||||
valueTree.writeToStream (m);
|
||||
stateChanged (m.getData(), m.getDataSize());
|
||||
}
|
||||
|
||||
void ValueTreeSynchroniser::valueTreePropertyChanged (ValueTree& vt, const Identifier& property)
|
||||
{
|
||||
MemoryOutputStream m;
|
||||
|
||||
if (auto* value = vt.getPropertyPointer (property))
|
||||
{
|
||||
ValueTreeSynchroniserHelpers::writeHeader (*this, m, ValueTreeSynchroniserHelpers::propertyChanged, vt);
|
||||
m.writeString (property.toString());
|
||||
value->writeToStream (m);
|
||||
}
|
||||
else
|
||||
{
|
||||
ValueTreeSynchroniserHelpers::writeHeader (*this, m, ValueTreeSynchroniserHelpers::propertyRemoved, vt);
|
||||
m.writeString (property.toString());
|
||||
}
|
||||
|
||||
stateChanged (m.getData(), m.getDataSize());
|
||||
}
|
||||
|
||||
void ValueTreeSynchroniser::valueTreeChildAdded (ValueTree& parentTree, ValueTree& childTree)
|
||||
{
|
||||
const int index = parentTree.indexOf (childTree);
|
||||
jassert (index >= 0);
|
||||
|
||||
MemoryOutputStream m;
|
||||
ValueTreeSynchroniserHelpers::writeHeader (*this, m, ValueTreeSynchroniserHelpers::childAdded, parentTree);
|
||||
m.writeCompressedInt (index);
|
||||
childTree.writeToStream (m);
|
||||
stateChanged (m.getData(), m.getDataSize());
|
||||
}
|
||||
|
||||
void ValueTreeSynchroniser::valueTreeChildRemoved (ValueTree& parentTree, ValueTree&, int oldIndex)
|
||||
{
|
||||
MemoryOutputStream m;
|
||||
ValueTreeSynchroniserHelpers::writeHeader (*this, m, ValueTreeSynchroniserHelpers::childRemoved, parentTree);
|
||||
m.writeCompressedInt (oldIndex);
|
||||
stateChanged (m.getData(), m.getDataSize());
|
||||
}
|
||||
|
||||
void ValueTreeSynchroniser::valueTreeChildOrderChanged (ValueTree& parent, int oldIndex, int newIndex)
|
||||
{
|
||||
MemoryOutputStream m;
|
||||
ValueTreeSynchroniserHelpers::writeHeader (*this, m, ValueTreeSynchroniserHelpers::childMoved, parent);
|
||||
m.writeCompressedInt (oldIndex);
|
||||
m.writeCompressedInt (newIndex);
|
||||
stateChanged (m.getData(), m.getDataSize());
|
||||
}
|
||||
|
||||
bool ValueTreeSynchroniser::applyChange (ValueTree& root, const void* data, size_t dataSize, UndoManager* undoManager)
|
||||
{
|
||||
MemoryInputStream input (data, dataSize, false);
|
||||
|
||||
const ValueTreeSynchroniserHelpers::ChangeType type = (ValueTreeSynchroniserHelpers::ChangeType) input.readByte();
|
||||
|
||||
if (type == ValueTreeSynchroniserHelpers::fullSync)
|
||||
{
|
||||
root = ValueTree::readFromStream (input);
|
||||
return true;
|
||||
}
|
||||
|
||||
ValueTree v (ValueTreeSynchroniserHelpers::readSubTreeLocation (input, root));
|
||||
|
||||
if (! v.isValid())
|
||||
return false;
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case ValueTreeSynchroniserHelpers::propertyChanged:
|
||||
{
|
||||
Identifier property (input.readString());
|
||||
v.setProperty (property, var::readFromStream (input), undoManager);
|
||||
return true;
|
||||
}
|
||||
|
||||
case ValueTreeSynchroniserHelpers::propertyRemoved:
|
||||
{
|
||||
Identifier property (input.readString());
|
||||
v.removeProperty (property, undoManager);
|
||||
return true;
|
||||
}
|
||||
|
||||
case ValueTreeSynchroniserHelpers::childAdded:
|
||||
{
|
||||
const int index = input.readCompressedInt();
|
||||
v.addChild (ValueTree::readFromStream (input), index, undoManager);
|
||||
return true;
|
||||
}
|
||||
|
||||
case ValueTreeSynchroniserHelpers::childRemoved:
|
||||
{
|
||||
const int index = input.readCompressedInt();
|
||||
|
||||
if (isPositiveAndBelow (index, v.getNumChildren()))
|
||||
{
|
||||
v.removeChild (index, undoManager);
|
||||
return true;
|
||||
}
|
||||
|
||||
jassertfalse; // Either received some corrupt data, or the trees have drifted out of sync
|
||||
break;
|
||||
}
|
||||
|
||||
case ValueTreeSynchroniserHelpers::childMoved:
|
||||
{
|
||||
const int oldIndex = input.readCompressedInt();
|
||||
const int newIndex = input.readCompressedInt();
|
||||
|
||||
if (isPositiveAndBelow (oldIndex, v.getNumChildren())
|
||||
&& isPositiveAndBelow (newIndex, v.getNumChildren()))
|
||||
{
|
||||
v.moveChild (oldIndex, newIndex, undoManager);
|
||||
return true;
|
||||
}
|
||||
|
||||
jassertfalse; // Either received some corrupt data, or the trees have drifted out of sync
|
||||
break;
|
||||
}
|
||||
|
||||
case ValueTreeSynchroniserHelpers::fullSync:
|
||||
break;
|
||||
|
||||
default:
|
||||
jassertfalse; // Seem to have received some corrupt data?
|
||||
break;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace juce
|
97
deps/juce/modules/juce_data_structures/values/juce_ValueTreeSynchroniser.h
vendored
Normal file
97
deps/juce/modules/juce_data_structures/values/juce_ValueTreeSynchroniser.h
vendored
Normal file
@ -0,0 +1,97 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
This class can be used to watch for all changes to the state of a ValueTree,
|
||||
and to convert them to a transmittable binary encoding.
|
||||
|
||||
The purpose of this class is to allow two or more ValueTrees to be remotely
|
||||
synchronised by transmitting encoded changes over some kind of transport
|
||||
mechanism.
|
||||
|
||||
To use it, you'll need to implement a subclass of ValueTreeSynchroniser
|
||||
and implement the stateChanged() method to transmit the encoded change (maybe
|
||||
via a network or other means) to a remote destination, where it can be
|
||||
applied to a target tree.
|
||||
|
||||
@tags{DataStructures}
|
||||
*/
|
||||
class JUCE_API ValueTreeSynchroniser : private ValueTree::Listener
|
||||
{
|
||||
public:
|
||||
/** Creates a ValueTreeSynchroniser that watches the given tree.
|
||||
|
||||
After creating an instance of this class and somehow attaching it to
|
||||
a target tree, you probably want to call sendFullSyncCallback() to
|
||||
get them into a common starting state.
|
||||
*/
|
||||
ValueTreeSynchroniser (const ValueTree& tree);
|
||||
|
||||
/** Destructor. */
|
||||
~ValueTreeSynchroniser() override;
|
||||
|
||||
/** This callback happens when the ValueTree changes and the given state-change message
|
||||
needs to be applied to any other trees that need to stay in sync with it.
|
||||
The data is an opaque blob of binary that you should transmit to wherever your
|
||||
target tree lives, and use applyChange() to apply this to the target tree.
|
||||
*/
|
||||
virtual void stateChanged (const void* encodedChange, size_t encodedChangeSize) = 0;
|
||||
|
||||
/** Forces the sending of a full state message, which may be large, as it
|
||||
encodes the entire ValueTree.
|
||||
|
||||
This will internally invoke stateChanged() with the encoded version of the state.
|
||||
*/
|
||||
void sendFullSyncCallback();
|
||||
|
||||
/** Applies an encoded change to the given destination tree.
|
||||
|
||||
When you implement a receiver for changes that were sent by the stateChanged()
|
||||
message, this is the function that you'll need to call to apply them to the
|
||||
target tree that you want to be synced.
|
||||
*/
|
||||
static bool applyChange (ValueTree& target,
|
||||
const void* encodedChangeData, size_t encodedChangeDataSize,
|
||||
UndoManager* undoManager);
|
||||
|
||||
/** Returns the root ValueTree that is being observed. */
|
||||
const ValueTree& getRoot() noexcept { return valueTree; }
|
||||
|
||||
private:
|
||||
ValueTree valueTree;
|
||||
|
||||
void valueTreePropertyChanged (ValueTree&, const Identifier&) override;
|
||||
void valueTreeChildAdded (ValueTree&, ValueTree&) override;
|
||||
void valueTreeChildRemoved (ValueTree&, ValueTree&, int) override;
|
||||
void valueTreeChildOrderChanged (ValueTree&, int, int) override;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ValueTreeSynchroniser)
|
||||
};
|
||||
|
||||
} // namespace juce
|
101
deps/juce/modules/juce_data_structures/values/juce_ValueWithDefault.cpp
vendored
Normal file
101
deps/juce/modules/juce_data_structures/values/juce_ValueWithDefault.cpp
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.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
#if JUCE_UNIT_TESTS
|
||||
|
||||
class ValueWithDefaultTests : public UnitTest
|
||||
{
|
||||
public:
|
||||
ValueWithDefaultTests()
|
||||
: UnitTest ("ValueWithDefault", UnitTestCategories::values)
|
||||
{}
|
||||
|
||||
void runTest() override
|
||||
{
|
||||
beginTest ("default constructor");
|
||||
{
|
||||
ValueWithDefault vwd;
|
||||
expect (vwd.isUsingDefault());
|
||||
expect (vwd.get() == var());
|
||||
}
|
||||
|
||||
beginTest ("missing property");
|
||||
{
|
||||
ValueTree t ("root");
|
||||
ValueWithDefault vwd (t, "testKey", nullptr, "default");
|
||||
|
||||
expect (vwd.isUsingDefault());
|
||||
expectEquals (vwd.get().toString(), String ("default"));
|
||||
}
|
||||
|
||||
beginTest ("non-empty property");
|
||||
{
|
||||
ValueTree t ("root");
|
||||
t.setProperty ("testKey", "non-default", nullptr);
|
||||
|
||||
ValueWithDefault vwd (t, "testKey", nullptr, "default");
|
||||
|
||||
expect (! vwd.isUsingDefault());
|
||||
expectEquals (vwd.get().toString(), String ("non-default"));
|
||||
}
|
||||
|
||||
beginTest ("set default");
|
||||
{
|
||||
ValueTree t ("root");
|
||||
|
||||
ValueWithDefault vwd (t, "testkey", nullptr);
|
||||
vwd.setDefault ("default");
|
||||
|
||||
expect (vwd.isUsingDefault());
|
||||
expectEquals (vwd.get().toString(), String ("default"));
|
||||
}
|
||||
|
||||
beginTest ("set value");
|
||||
{
|
||||
ValueTree t ("root");
|
||||
t.setProperty ("testkey", "testvalue", nullptr);
|
||||
|
||||
ValueWithDefault vwd (t, "testkey", nullptr, "default");
|
||||
vwd = "newvalue";
|
||||
|
||||
expect (! vwd.isUsingDefault());
|
||||
expectEquals (t["testkey"].toString(), String ("newvalue"));
|
||||
|
||||
vwd.resetToDefault();
|
||||
|
||||
expect (vwd.isUsingDefault());
|
||||
expect (t["testkey"] == var());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
static ValueWithDefaultTests valueWithDefaultTests;
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace juce
|
244
deps/juce/modules/juce_data_structures/values/juce_ValueWithDefault.h
vendored
Normal file
244
deps/juce/modules/juce_data_structures/values/juce_ValueWithDefault.h
vendored
Normal file
@ -0,0 +1,244 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
This class acts as a wrapper around a property inside a ValueTree.
|
||||
|
||||
If the property inside the ValueTree is missing the ValueWithDefault will automatically
|
||||
return a default value, which can be specified when initialising the ValueWithDefault.
|
||||
|
||||
@tags{DataStructures}
|
||||
*/
|
||||
class ValueWithDefault
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates an unitialised ValueWithDefault. Initialise it using one of the referTo() methods. */
|
||||
ValueWithDefault() = default;
|
||||
|
||||
/** Creates an ValueWithDefault object. The default value will be an empty var. */
|
||||
ValueWithDefault (ValueTree& tree, const Identifier& propertyID, UndoManager* um)
|
||||
: targetTree (tree),
|
||||
targetProperty (propertyID),
|
||||
undoManager (um),
|
||||
defaultValue()
|
||||
{
|
||||
}
|
||||
|
||||
/** Creates an ValueWithDefault object. The default value will be defaultToUse. */
|
||||
ValueWithDefault (ValueTree& tree, const Identifier& propertyID, UndoManager* um,
|
||||
const var& defaultToUse)
|
||||
: targetTree (tree),
|
||||
targetProperty (propertyID),
|
||||
undoManager (um),
|
||||
defaultValue (defaultToUse)
|
||||
{
|
||||
}
|
||||
|
||||
/** Creates an ValueWithDefault object. The default value will be defaultToUse.
|
||||
|
||||
Use this constructor if the underlying var object being controlled is an array and
|
||||
it will handle the conversion to/from a delimited String that can be written to
|
||||
XML format.
|
||||
*/
|
||||
ValueWithDefault (ValueTree& tree, const Identifier& propertyID, UndoManager* um,
|
||||
const var& defaultToUse, StringRef arrayDelimiter)
|
||||
: targetTree (tree),
|
||||
targetProperty (propertyID),
|
||||
undoManager (um),
|
||||
defaultValue (defaultToUse),
|
||||
delimiter (arrayDelimiter)
|
||||
{
|
||||
}
|
||||
|
||||
/** Creates a ValueWithDefault object from another ValueWithDefault object. */
|
||||
ValueWithDefault (const ValueWithDefault& other)
|
||||
: targetTree (other.targetTree),
|
||||
targetProperty (other.targetProperty),
|
||||
undoManager (other.undoManager),
|
||||
defaultValue (other.defaultValue),
|
||||
delimiter (other.delimiter)
|
||||
{
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the current value of the property. If the property does not exist this
|
||||
returns the default value.
|
||||
*/
|
||||
var get() const noexcept
|
||||
{
|
||||
if (isUsingDefault())
|
||||
return defaultValue;
|
||||
|
||||
if (delimiter.isNotEmpty())
|
||||
return delimitedStringToVarArray (targetTree[targetProperty].toString());
|
||||
|
||||
return targetTree[targetProperty];
|
||||
}
|
||||
|
||||
/** Returns the current property as a Value object. */
|
||||
Value getPropertyAsValue() { return targetTree.getPropertyAsValue (targetProperty, undoManager); }
|
||||
|
||||
/** Returns the current default value. */
|
||||
var getDefault() const { return defaultValue; }
|
||||
|
||||
/** Sets the default value to a new var. */
|
||||
void setDefault (const var& newDefault)
|
||||
{
|
||||
if (defaultValue != newDefault)
|
||||
{
|
||||
defaultValue = newDefault;
|
||||
|
||||
if (onDefaultChange != nullptr)
|
||||
onDefaultChange();
|
||||
}
|
||||
}
|
||||
|
||||
/** Returns true if the property does not exist in the referenced ValueTree. */
|
||||
bool isUsingDefault() const
|
||||
{
|
||||
return ! targetTree.hasProperty (targetProperty);
|
||||
}
|
||||
|
||||
/** Removes the property from the referenced ValueTree. */
|
||||
void resetToDefault() noexcept
|
||||
{
|
||||
targetTree.removeProperty (targetProperty, nullptr);
|
||||
}
|
||||
|
||||
/** You can assign a lambda to this callback object to have it called when the default value is changed. */
|
||||
std::function<void()> onDefaultChange;
|
||||
|
||||
//==============================================================================
|
||||
/** Sets the property and returns the new ValueWithDefault. This will modify the property in the referenced ValueTree. */
|
||||
ValueWithDefault& operator= (const var& newValue)
|
||||
{
|
||||
setValue (newValue, undoManager);
|
||||
return *this;
|
||||
}
|
||||
|
||||
/** Sets the property. This will actually modify the property in the referenced ValueTree. */
|
||||
void setValue (const var& newValue, UndoManager* undoManagerToUse)
|
||||
{
|
||||
if (auto* array = newValue.getArray())
|
||||
targetTree.setProperty (targetProperty, varArrayToDelimitedString (*array), undoManagerToUse);
|
||||
else
|
||||
targetTree.setProperty (targetProperty, newValue, undoManagerToUse);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** Makes the ValueWithDefault refer to the specified property inside the given ValueTree. */
|
||||
void referTo (ValueTree& tree, const Identifier& property, UndoManager* um)
|
||||
{
|
||||
referToWithDefault (tree, property, um, var(), {});
|
||||
}
|
||||
|
||||
/** Makes the ValueWithDefault refer to the specified property inside the given ValueTree,
|
||||
and specifies a default value to use.
|
||||
*/
|
||||
void referTo (ValueTree& tree, const Identifier& property, UndoManager* um, const var& defaultVal)
|
||||
{
|
||||
referToWithDefault (tree, property, um, defaultVal, {});
|
||||
}
|
||||
|
||||
void referTo (ValueTree& tree, const Identifier& property, UndoManager* um,
|
||||
const var& defaultVal, StringRef arrayDelimiter)
|
||||
{
|
||||
referToWithDefault (tree, property, um, defaultVal, arrayDelimiter);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** Returns a reference to the ValueTree containing the referenced property. */
|
||||
ValueTree& getValueTree() noexcept { return targetTree; }
|
||||
|
||||
/** Returns the property ID of the referenced property. */
|
||||
Identifier& getPropertyID() noexcept { return targetProperty; }
|
||||
|
||||
/** Returns the UndoManager that is being used. */
|
||||
UndoManager* getUndoManager() noexcept { return undoManager; }
|
||||
|
||||
//==============================================================================
|
||||
ValueWithDefault& operator= (const ValueWithDefault& other)
|
||||
{
|
||||
referToWithDefault (other.targetTree, other.targetProperty, other.undoManager,
|
||||
other.defaultValue, other.delimiter);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
ValueTree targetTree;
|
||||
Identifier targetProperty;
|
||||
UndoManager* undoManager = nullptr;
|
||||
var defaultValue;
|
||||
|
||||
String delimiter;
|
||||
|
||||
//==============================================================================
|
||||
void referToWithDefault (const ValueTree& v, const Identifier& i, UndoManager* um,
|
||||
const var& defaultVal, StringRef del)
|
||||
{
|
||||
targetTree = v;
|
||||
targetProperty = i;
|
||||
undoManager = um;
|
||||
defaultValue = defaultVal;
|
||||
delimiter = del;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
String varArrayToDelimitedString (const Array<var>& input) const noexcept
|
||||
{
|
||||
// if you are trying to control a var that is an array then you need to
|
||||
// set a delimiter string that will be used when writing to XML!
|
||||
jassert (delimiter.isNotEmpty());
|
||||
|
||||
StringArray elements;
|
||||
|
||||
for (auto& v : input)
|
||||
elements.add (v.toString());
|
||||
|
||||
return elements.joinIntoString (delimiter);
|
||||
}
|
||||
|
||||
Array<var> delimitedStringToVarArray (StringRef input) const noexcept
|
||||
{
|
||||
Array<var> arr;
|
||||
|
||||
for (auto t : StringArray::fromTokens (input, delimiter, {}))
|
||||
arr.add (t);
|
||||
|
||||
return arr;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
JUCE_DECLARE_WEAK_REFERENCEABLE (ValueWithDefault)
|
||||
};
|
||||
|
||||
} // namespace juce
|
Reference in New Issue
Block a user