| @@ -38,7 +38,10 @@ | |||
| #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" | |||
| #if JUCE_UNIT_TESTS | |||
| #include "values/juce_ValueTreePropertyWithDefault_test.cpp" | |||
| #endif | |||
| @@ -61,6 +61,6 @@ | |||
| #include "values/juce_ValueTree.h" | |||
| #include "values/juce_ValueTreeSynchroniser.h" | |||
| #include "values/juce_CachedValue.h" | |||
| #include "values/juce_ValueWithDefault.h" | |||
| #include "values/juce_ValueTreePropertyWithDefault.h" | |||
| #include "app_properties/juce_PropertiesFile.h" | |||
| #include "app_properties/juce_ApplicationProperties.h" | |||
| @@ -0,0 +1,293 @@ | |||
| /* | |||
| ============================================================================== | |||
| 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 it will return a default value, | |||
| which can be specified in the constructor or by calling setDefault(). | |||
| @tags{DataStructures} | |||
| */ | |||
| class JUCE_API ValueTreePropertyWithDefault : private Value::Listener | |||
| { | |||
| public: | |||
| //============================================================================== | |||
| /** Creates an uninitialised ValueTreePropertyWithDefault object. | |||
| Initialise it using one of the referTo() methods. | |||
| */ | |||
| ValueTreePropertyWithDefault() = default; | |||
| /** Creates a ValueTreePropertyWithDefault object for the specified property. | |||
| The default value will be an empty var. | |||
| */ | |||
| ValueTreePropertyWithDefault (ValueTree& tree, | |||
| const Identifier& propertyID, | |||
| UndoManager* um) | |||
| { | |||
| referTo (tree, propertyID, um); | |||
| } | |||
| /** Creates an ValueTreePropertyWithDefault object for the specified property. | |||
| The default value will be defaultToUse. | |||
| */ | |||
| ValueTreePropertyWithDefault (ValueTree& tree, | |||
| const Identifier& propertyID, | |||
| UndoManager* um, | |||
| var defaultToUse) | |||
| { | |||
| referTo (tree, propertyID, um, defaultToUse); | |||
| } | |||
| /** Creates a ValueTreePropertyWithDefault object for the specified property. | |||
| 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. | |||
| */ | |||
| ValueTreePropertyWithDefault (ValueTree& tree, | |||
| const Identifier& propertyID, | |||
| UndoManager* um, | |||
| var defaultToUse, | |||
| StringRef arrayDelimiter) | |||
| { | |||
| referTo (tree, propertyID, um, defaultToUse, arrayDelimiter); | |||
| } | |||
| /** Creates a ValueTreePropertyWithDefault object from another ValueTreePropertyWithDefault object. */ | |||
| ValueTreePropertyWithDefault (const ValueTreePropertyWithDefault& other) | |||
| { | |||
| referToWithDefault (other.targetTree, | |||
| other.targetProperty, | |||
| other.undoManager, | |||
| other.defaultValue, | |||
| other.delimiter); | |||
| } | |||
| /** Destructor. */ | |||
| ~ValueTreePropertyWithDefault() override | |||
| { | |||
| defaultValue.removeListener (this); | |||
| } | |||
| //============================================================================== | |||
| /** 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) { defaultValue = newDefault; } | |||
| /** 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 and it will called when the default | |||
| value is changed. | |||
| @see setDefault | |||
| */ | |||
| std::function<void()> onDefaultChange; | |||
| //============================================================================== | |||
| /** Sets the property and returns the new ValueTreePropertyWithDefault. | |||
| This will modify the property in the referenced ValueTree. | |||
| */ | |||
| ValueTreePropertyWithDefault& operator= (const var& newValue) | |||
| { | |||
| setValue (newValue, undoManager); | |||
| return *this; | |||
| } | |||
| /** Sets the property. | |||
| This will 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 ValueTreePropertyWithDefault refer to the specified property inside | |||
| the given ValueTree. | |||
| The default value will be an empty var. | |||
| */ | |||
| void referTo (ValueTree tree, | |||
| const Identifier& property, | |||
| UndoManager* um) | |||
| { | |||
| referToWithDefault (tree, property, um, {}, {}); | |||
| } | |||
| /** Makes the ValueTreePropertyWithDefault refer to the specified property inside | |||
| the given ValueTree. | |||
| The default value will be defaultVal. | |||
| */ | |||
| void referTo (ValueTree tree, | |||
| const Identifier& property, | |||
| UndoManager* um, | |||
| var defaultVal) | |||
| { | |||
| referToWithDefault (tree, property, um, Value (defaultVal), {}); | |||
| } | |||
| /** Makes the ValueTreePropertyWithDefault refer to the specified property inside | |||
| the given ValueTree. | |||
| The default value will be defaultVal. | |||
| */ | |||
| void referTo (ValueTree tree, | |||
| const Identifier& property, | |||
| UndoManager* um, | |||
| var defaultVal, | |||
| StringRef arrayDelimiter) | |||
| { | |||
| referToWithDefault (tree, property, um, Value (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; } | |||
| //============================================================================== | |||
| ValueTreePropertyWithDefault& operator= (const ValueTreePropertyWithDefault& other) | |||
| { | |||
| referToWithDefault (other.targetTree, | |||
| other.targetProperty, | |||
| other.undoManager, | |||
| other.defaultValue, | |||
| other.delimiter); | |||
| return *this; | |||
| } | |||
| private: | |||
| //============================================================================== | |||
| ValueTree targetTree; | |||
| Identifier targetProperty; | |||
| UndoManager* undoManager = nullptr; | |||
| Value defaultValue; | |||
| String delimiter; | |||
| //============================================================================== | |||
| void valueChanged (Value&) override | |||
| { | |||
| if (onDefaultChange != nullptr) | |||
| onDefaultChange(); | |||
| } | |||
| //============================================================================== | |||
| void referToWithDefault (ValueTree v, | |||
| const Identifier& i, | |||
| UndoManager* um, | |||
| const Value& defaultVal, | |||
| StringRef del) | |||
| { | |||
| targetTree = v; | |||
| targetProperty = i; | |||
| undoManager = um; | |||
| defaultValue.referTo (defaultVal); | |||
| delimiter = del; | |||
| defaultValue.addListener (this); | |||
| } | |||
| //============================================================================== | |||
| 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; | |||
| } | |||
| }; | |||
| //============================================================================== | |||
| #ifndef DOXYGEN | |||
| using ValueWithDefault [[deprecated ("This class has been renamed to better describe what is does. " | |||
| "This declaration is here for backwards compatibility and new " | |||
| "code should use the new class name.")]] | |||
| = ValueTreePropertyWithDefault; | |||
| #endif | |||
| } // namespace juce | |||
| @@ -26,31 +26,29 @@ | |||
| namespace juce | |||
| { | |||
| #if JUCE_UNIT_TESTS | |||
| class ValueWithDefaultTests : public UnitTest | |||
| class ValueTreePropertyWithDefaultTests : public UnitTest | |||
| { | |||
| public: | |||
| ValueWithDefaultTests() | |||
| : UnitTest ("ValueWithDefault", UnitTestCategories::values) | |||
| ValueTreePropertyWithDefaultTests() | |||
| : UnitTest ("ValueTreePropertyWithDefault", UnitTestCategories::values) | |||
| {} | |||
| void runTest() override | |||
| { | |||
| beginTest ("default constructor"); | |||
| { | |||
| ValueWithDefault vwd; | |||
| expect (vwd.isUsingDefault()); | |||
| expect (vwd.get() == var()); | |||
| ValueTreePropertyWithDefault value; | |||
| expect (value.isUsingDefault()); | |||
| expect (value.get() == var()); | |||
| } | |||
| beginTest ("missing property"); | |||
| { | |||
| ValueTree t ("root"); | |||
| ValueWithDefault vwd (t, "testKey", nullptr, "default"); | |||
| ValueTreePropertyWithDefault value (t, "testKey", nullptr, "default"); | |||
| expect (vwd.isUsingDefault()); | |||
| expectEquals (vwd.get().toString(), String ("default")); | |||
| expect (value.isUsingDefault()); | |||
| expectEquals (value.get().toString(), String ("default")); | |||
| } | |||
| beginTest ("non-empty property"); | |||
| @@ -58,21 +56,21 @@ public: | |||
| ValueTree t ("root"); | |||
| t.setProperty ("testKey", "non-default", nullptr); | |||
| ValueWithDefault vwd (t, "testKey", nullptr, "default"); | |||
| ValueTreePropertyWithDefault value (t, "testKey", nullptr, "default"); | |||
| expect (! vwd.isUsingDefault()); | |||
| expectEquals (vwd.get().toString(), String ("non-default")); | |||
| expect (! value.isUsingDefault()); | |||
| expectEquals (value.get().toString(), String ("non-default")); | |||
| } | |||
| beginTest ("set default"); | |||
| { | |||
| ValueTree t ("root"); | |||
| ValueWithDefault vwd (t, "testkey", nullptr); | |||
| vwd.setDefault ("default"); | |||
| ValueTreePropertyWithDefault value (t, "testkey", nullptr); | |||
| value.setDefault ("default"); | |||
| expect (vwd.isUsingDefault()); | |||
| expectEquals (vwd.get().toString(), String ("default")); | |||
| expect (value.isUsingDefault()); | |||
| expectEquals (value.get().toString(), String ("default")); | |||
| } | |||
| beginTest ("set value"); | |||
| @@ -80,22 +78,20 @@ public: | |||
| ValueTree t ("root"); | |||
| t.setProperty ("testkey", "testvalue", nullptr); | |||
| ValueWithDefault vwd (t, "testkey", nullptr, "default"); | |||
| vwd = "newvalue"; | |||
| ValueTreePropertyWithDefault value (t, "testkey", nullptr, "default"); | |||
| value = "newvalue"; | |||
| expect (! vwd.isUsingDefault()); | |||
| expect (! value.isUsingDefault()); | |||
| expectEquals (t["testkey"].toString(), String ("newvalue")); | |||
| vwd.resetToDefault(); | |||
| value.resetToDefault(); | |||
| expect (vwd.isUsingDefault()); | |||
| expect (value.isUsingDefault()); | |||
| expect (t["testkey"] == var()); | |||
| } | |||
| } | |||
| }; | |||
| static ValueWithDefaultTests valueWithDefaultTests; | |||
| #endif | |||
| static ValueTreePropertyWithDefaultTests valueTreePropertyWithDefaultTests; | |||
| } // namespace juce | |||
| @@ -1,244 +0,0 @@ | |||
| /* | |||
| ============================================================================== | |||
| 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 | |||