1 /*
2   ==============================================================================
3 
4    This file is part of the JUCE library.
5    Copyright (c) 2020 - Raw Material Software Limited
6 
7    JUCE is an open source library subject to commercial or open-source
8    licensing.
9 
10    By using JUCE, you agree to the terms of both the JUCE 6 End-User License
11    Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
12 
13    End User License Agreement: www.juce.com/juce-6-licence
14    Privacy Policy: www.juce.com/juce-privacy-policy
15 
16    Or: You may also use this code under the terms of the GPL v3 (see
17    www.gnu.org/licenses).
18 
19    JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
20    EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
21    DISCLAIMED.
22 
23   ==============================================================================
24 */
25 
26 namespace juce
27 {
28 
29 //==============================================================================
30 /**
31     This class acts as a typed wrapper around a property inside a ValueTree.
32 
33     A CachedValue provides an easy way to read and write a ValueTree property with
34     a chosen type. So for example a CachedValue<int> allows you to read or write the
35     property as an int, and a CachedValue<String> lets you work with it as a String.
36 
37     It also allows efficient access to the value, by caching a copy of it in the
38     type that is being used.
39 
40     You can give the CachedValue an optional UndoManager which it will use when writing
41     to the underlying ValueTree.
42 
43     If the property inside the ValueTree is missing, the CachedValue will automatically
44     return an optional default value, which can be specified when initialising the CachedValue.
45 
46     To create one, you can either use the constructor to attach the CachedValue to a
47     ValueTree, or can create an uninitialised CachedValue with its default constructor and
48     then attach it later with the referTo() methods.
49 
50     Common types like String, int, double which can be easily converted to a var should work
51     out-of-the-box, but if you want to use more complex custom types, you may need to implement
52     some template specialisations of VariantConverter which this class uses to convert between
53     the type and the ValueTree's internal var.
54 
55     @tags{DataStructures}
56 */
57 template <typename Type>
58 class CachedValue   : private ValueTree::Listener
59 {
60 public:
61     //==============================================================================
62     /** Default constructor.
63         Creates a default CachedValue not referring to any property. To initialise the
64         object, call one of the referTo() methods.
65     */
66     CachedValue();
67 
68     /** Constructor.
69 
70         Creates a CachedValue referring to a Value property inside a ValueTree.
71         If you use this constructor, the fallback value will be a default-constructed
72         instance of Type.
73 
74         @param tree          The ValueTree containing the property
75         @param propertyID    The identifier of the property
76         @param undoManager   The UndoManager to use when writing to the property
77     */
78     CachedValue (ValueTree& tree, const Identifier& propertyID,
79                  UndoManager* undoManager);
80 
81     /** Constructor.
82 
83         Creates a default Cached Value referring to a Value property inside a ValueTree,
84         and specifies a fallback value to use if the property does not exist.
85 
86         @param tree          The ValueTree containing the property
87         @param propertyID    The identifier of the property
88         @param undoManager   The UndoManager to use when writing to the property
89         @param defaultToUse  The fallback default value to use.
90     */
91     CachedValue (ValueTree& tree, const Identifier& propertyID,
92                  UndoManager* undoManager, const Type& defaultToUse);
93 
94     //==============================================================================
95     /** Returns the current value of the property. If the property does not exist,
96         returns the fallback default value.
97 
98         This is the same as calling get().
99     */
Type()100     operator Type() const noexcept                   { return cachedValue; }
101 
102     /** Returns the current value of the property. If the property does not exist,
103         returns the fallback default value.
104     */
get()105     Type get() const noexcept                        { return cachedValue; }
106 
107     /** Dereference operator. Provides direct access to the property.  */
108     Type& operator*() noexcept                       { return cachedValue; }
109 
110     /** Dereference operator. Provides direct access to members of the property
111         if it is of object type.
112     */
113     Type* operator->() noexcept                      { return &cachedValue; }
114 
115     /** Returns true if the current value of the property (or the fallback value)
116         is equal to other.
117     */
118     template <typename OtherType>
119     bool operator== (const OtherType& other) const   { return cachedValue == other; }
120 
121     /** Returns true if the current value of the property (or the fallback value)
122         is not equal to other.
123      */
124     template <typename OtherType>
125     bool operator!= (const OtherType& other) const   { return cachedValue != other; }
126 
127     //==============================================================================
128     /** Returns the current property as a Value object. */
129     Value getPropertyAsValue();
130 
131     /** Returns true if the current property does not exist and the CachedValue is using
132         the fallback default value instead.
133     */
134     bool isUsingDefault() const;
135 
136     /** Returns the current fallback default value. */
getDefault()137     Type getDefault() const                          { return defaultValue; }
138 
139     //==============================================================================
140     /** Sets the property. This will actually modify the property in the referenced ValueTree. */
141     CachedValue& operator= (const Type& newValue);
142 
143     /** Sets the property. This will actually modify the property in the referenced ValueTree. */
144     void setValue (const Type& newValue, UndoManager* undoManagerToUse);
145 
146     /** Removes the property from the referenced ValueTree and makes the CachedValue
147         return the fallback default value instead.
148      */
149     void resetToDefault();
150 
151     /** Removes the property from the referenced ValueTree and makes the CachedValue
152         return the fallback default value instead.
153      */
154     void resetToDefault (UndoManager* undoManagerToUse);
155 
156     /** Resets the fallback default value. */
setDefault(const Type & value)157     void setDefault (const Type& value)                { defaultValue = value; }
158 
159     //==============================================================================
160     /** Makes the CachedValue refer to the specified property inside the given ValueTree. */
161     void referTo (ValueTree& tree, const Identifier& property, UndoManager* um);
162 
163     /** Makes the CachedValue refer to the specified property inside the given ValueTree,
164         and specifies a fallback value to use if the property does not exist.
165     */
166     void referTo (ValueTree& tree, const Identifier& property, UndoManager* um, const Type& defaultVal);
167 
168     /** Force an update in case the referenced property has been changed from elsewhere.
169 
170         Note: The CachedValue is a ValueTree::Listener and therefore will be informed of
171         changes of the referenced property anyway (and update itself). But this may happen
172         asynchronously. forceUpdateOfCachedValue() forces an update immediately.
173     */
174     void forceUpdateOfCachedValue();
175 
176     //==============================================================================
177     /** Returns a reference to the ValueTree containing the referenced property. */
getValueTree()178     ValueTree& getValueTree() noexcept                      { return targetTree; }
179 
180     /** Returns the property ID of the referenced property. */
getPropertyID()181     const Identifier& getPropertyID() const noexcept        { return targetProperty; }
182 
183     /** Returns the UndoManager that is being used. */
getUndoManager()184     UndoManager* getUndoManager() noexcept                  { return undoManager; }
185 
186 private:
187     //==============================================================================
188     ValueTree targetTree;
189     Identifier targetProperty;
190     UndoManager* undoManager = nullptr;
191     Type defaultValue;
192     Type cachedValue;
193 
194     //==============================================================================
195     void referToWithDefault (ValueTree&, const Identifier&, UndoManager*, const Type&);
196     Type getTypedValue() const;
197 
198     void valueTreePropertyChanged (ValueTree& changedTree, const Identifier& changedProperty) override;
199 
200     //==============================================================================
201     JUCE_DECLARE_WEAK_REFERENCEABLE (CachedValue)
202     JUCE_DECLARE_NON_COPYABLE (CachedValue)
203 };
204 
205 
206 //==============================================================================
207 template <typename Type>
208 inline CachedValue<Type>::CachedValue() = default;
209 
210 template <typename Type>
CachedValue(ValueTree & v,const Identifier & i,UndoManager * um)211 inline CachedValue<Type>::CachedValue (ValueTree& v, const Identifier& i, UndoManager* um)
212     : targetTree (v), targetProperty (i), undoManager (um),
213       defaultValue(), cachedValue (getTypedValue())
214 {
215     targetTree.addListener (this);
216 }
217 
218 template <typename Type>
CachedValue(ValueTree & v,const Identifier & i,UndoManager * um,const Type & defaultToUse)219 inline CachedValue<Type>::CachedValue (ValueTree& v, const Identifier& i, UndoManager* um, const Type& defaultToUse)
220     : targetTree (v), targetProperty (i), undoManager (um),
221       defaultValue (defaultToUse), cachedValue (getTypedValue())
222 {
223     targetTree.addListener (this);
224 }
225 
226 template <typename Type>
getPropertyAsValue()227 inline Value CachedValue<Type>::getPropertyAsValue()
228 {
229     return targetTree.getPropertyAsValue (targetProperty, undoManager);
230 }
231 
232 template <typename Type>
isUsingDefault()233 inline bool CachedValue<Type>::isUsingDefault() const
234 {
235     return ! targetTree.hasProperty (targetProperty);
236 }
237 
238 template <typename Type>
239 inline CachedValue<Type>& CachedValue<Type>::operator= (const Type& newValue)
240 {
241     setValue (newValue, undoManager);
242     return *this;
243 }
244 
245 template <typename Type>
setValue(const Type & newValue,UndoManager * undoManagerToUse)246 inline void CachedValue<Type>::setValue (const Type& newValue, UndoManager* undoManagerToUse)
247 {
248     if (cachedValue != newValue || isUsingDefault())
249     {
250         cachedValue = newValue;
251         targetTree.setProperty (targetProperty, VariantConverter<Type>::toVar (newValue), undoManagerToUse);
252     }
253 }
254 
255 template <typename Type>
resetToDefault()256 inline void CachedValue<Type>::resetToDefault()
257 {
258     resetToDefault (undoManager);
259 }
260 
261 template <typename Type>
resetToDefault(UndoManager * undoManagerToUse)262 inline void CachedValue<Type>::resetToDefault (UndoManager* undoManagerToUse)
263 {
264     targetTree.removeProperty (targetProperty, undoManagerToUse);
265     forceUpdateOfCachedValue();
266 }
267 
268 template <typename Type>
referTo(ValueTree & v,const Identifier & i,UndoManager * um)269 inline void CachedValue<Type>::referTo (ValueTree& v, const Identifier& i, UndoManager* um)
270 {
271     referToWithDefault (v, i, um, Type());
272 }
273 
274 template <typename Type>
referTo(ValueTree & v,const Identifier & i,UndoManager * um,const Type & defaultVal)275 inline void CachedValue<Type>::referTo (ValueTree& v, const Identifier& i, UndoManager* um, const Type& defaultVal)
276 {
277     referToWithDefault (v, i, um, defaultVal);
278 }
279 
280 template <typename Type>
forceUpdateOfCachedValue()281 inline void CachedValue<Type>::forceUpdateOfCachedValue()
282 {
283     cachedValue = getTypedValue();
284 }
285 
286 template <typename Type>
referToWithDefault(ValueTree & v,const Identifier & i,UndoManager * um,const Type & defaultVal)287 inline void CachedValue<Type>::referToWithDefault (ValueTree& v, const Identifier& i, UndoManager* um, const Type& defaultVal)
288 {
289     targetTree.removeListener (this);
290     targetTree = v;
291     targetProperty = i;
292     undoManager = um;
293     defaultValue = defaultVal;
294     cachedValue = getTypedValue();
295     targetTree.addListener (this);
296 }
297 
298 template <typename Type>
getTypedValue()299 inline Type CachedValue<Type>::getTypedValue() const
300 {
301     if (const var* property = targetTree.getPropertyPointer (targetProperty))
302         return VariantConverter<Type>::fromVar (*property);
303 
304     return defaultValue;
305 }
306 
307 template <typename Type>
valueTreePropertyChanged(ValueTree & changedTree,const Identifier & changedProperty)308 inline void CachedValue<Type>::valueTreePropertyChanged (ValueTree& changedTree, const Identifier& changedProperty)
309 {
310     if (changedProperty == targetProperty && targetTree == changedTree)
311         forceUpdateOfCachedValue();
312 }
313 
314 } // namespace juce
315