The JUCE cross-platform C++ framework, with DISTRHO/KXStudio specific changes
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

910 lines
26KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library - "Jules' Utility Class Extensions"
  4. Copyright 2004-10 by Raw Material Software Ltd.
  5. ------------------------------------------------------------------------------
  6. JUCE can be redistributed and/or modified under the terms of the GNU General
  7. Public License (Version 2), as published by the Free Software Foundation.
  8. A copy of the license is included in the JUCE distribution, or can be found
  9. online at www.gnu.org/licenses.
  10. JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
  11. WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
  12. A PARTICULAR PURPOSE. See the GNU General Public License for more details.
  13. ------------------------------------------------------------------------------
  14. To release a closed-source product which uses JUCE, commercial licenses are
  15. available: visit www.rawmaterialsoftware.com/juce for more information.
  16. ==============================================================================
  17. */
  18. #include "../core/juce_StandardHeader.h"
  19. BEGIN_JUCE_NAMESPACE
  20. #include "juce_ValueTree.h"
  21. #include "../io/streams/juce_MemoryInputStream.h"
  22. //==============================================================================
  23. class ValueTree::SetPropertyAction : public UndoableAction
  24. {
  25. public:
  26. SetPropertyAction (const SharedObjectPtr& target_, const Identifier& name_,
  27. const var& newValue_, const var& oldValue_,
  28. const bool isAddingNewProperty_, const bool isDeletingProperty_)
  29. : target (target_), name (name_), newValue (newValue_), oldValue (oldValue_),
  30. isAddingNewProperty (isAddingNewProperty_), isDeletingProperty (isDeletingProperty_)
  31. {
  32. }
  33. ~SetPropertyAction() {}
  34. bool perform()
  35. {
  36. jassert (! (isAddingNewProperty && target->hasProperty (name)));
  37. if (isDeletingProperty)
  38. target->removeProperty (name, 0);
  39. else
  40. target->setProperty (name, newValue, 0);
  41. return true;
  42. }
  43. bool undo()
  44. {
  45. if (isAddingNewProperty)
  46. target->removeProperty (name, 0);
  47. else
  48. target->setProperty (name, oldValue, 0);
  49. return true;
  50. }
  51. int getSizeInUnits()
  52. {
  53. return (int) sizeof (*this); //xxx should be more accurate
  54. }
  55. UndoableAction* createCoalescedAction (UndoableAction* nextAction)
  56. {
  57. if (! (isAddingNewProperty || isDeletingProperty))
  58. {
  59. SetPropertyAction* next = dynamic_cast <SetPropertyAction*> (nextAction);
  60. if (next != 0 && next->target == target && next->name == name
  61. && ! (next->isAddingNewProperty || next->isDeletingProperty))
  62. {
  63. return new SetPropertyAction (target, name, next->newValue, oldValue, false, false);
  64. }
  65. }
  66. return 0;
  67. }
  68. private:
  69. const SharedObjectPtr target;
  70. const Identifier name;
  71. const var newValue;
  72. var oldValue;
  73. const bool isAddingNewProperty : 1, isDeletingProperty : 1;
  74. SetPropertyAction (const SetPropertyAction&);
  75. SetPropertyAction& operator= (const SetPropertyAction&);
  76. };
  77. //==============================================================================
  78. class ValueTree::AddOrRemoveChildAction : public UndoableAction
  79. {
  80. public:
  81. AddOrRemoveChildAction (const SharedObjectPtr& target_, const int childIndex_,
  82. const SharedObjectPtr& newChild_)
  83. : target (target_),
  84. child (newChild_ != 0 ? newChild_ : target_->children [childIndex_]),
  85. childIndex (childIndex_),
  86. isDeleting (newChild_ == 0)
  87. {
  88. jassert (child != 0);
  89. }
  90. ~AddOrRemoveChildAction() {}
  91. bool perform()
  92. {
  93. if (isDeleting)
  94. target->removeChild (childIndex, 0);
  95. else
  96. target->addChild (child, childIndex, 0);
  97. return true;
  98. }
  99. bool undo()
  100. {
  101. if (isDeleting)
  102. {
  103. target->addChild (child, childIndex, 0);
  104. }
  105. else
  106. {
  107. // If you hit this, it seems that your object's state is getting confused - probably
  108. // because you've interleaved some undoable and non-undoable operations?
  109. jassert (childIndex < target->children.size());
  110. target->removeChild (childIndex, 0);
  111. }
  112. return true;
  113. }
  114. int getSizeInUnits()
  115. {
  116. return (int) sizeof (*this); //xxx should be more accurate
  117. }
  118. private:
  119. const SharedObjectPtr target, child;
  120. const int childIndex;
  121. const bool isDeleting;
  122. AddOrRemoveChildAction (const AddOrRemoveChildAction&);
  123. AddOrRemoveChildAction& operator= (const AddOrRemoveChildAction&);
  124. };
  125. //==============================================================================
  126. class ValueTree::MoveChildAction : public UndoableAction
  127. {
  128. public:
  129. MoveChildAction (const SharedObjectPtr& target_,
  130. const int startIndex_, const int endIndex_)
  131. : target (target_),
  132. startIndex (startIndex_),
  133. endIndex (endIndex_)
  134. {
  135. }
  136. ~MoveChildAction() {}
  137. bool perform()
  138. {
  139. target->moveChild (startIndex, endIndex, 0);
  140. return true;
  141. }
  142. bool undo()
  143. {
  144. target->moveChild (endIndex, startIndex, 0);
  145. return true;
  146. }
  147. int getSizeInUnits()
  148. {
  149. return (int) sizeof (*this); //xxx should be more accurate
  150. }
  151. UndoableAction* createCoalescedAction (UndoableAction* nextAction)
  152. {
  153. MoveChildAction* next = dynamic_cast <MoveChildAction*> (nextAction);
  154. if (next != 0 && next->target == target && next->child == child)
  155. return new MoveChildAction (target, startIndex, next->endIndex);
  156. return 0;
  157. }
  158. private:
  159. const SharedObjectPtr target, child;
  160. const int startIndex, endIndex;
  161. MoveChildAction (const MoveChildAction&);
  162. MoveChildAction& operator= (const MoveChildAction&);
  163. };
  164. //==============================================================================
  165. ValueTree::SharedObject::SharedObject (const Identifier& type_)
  166. : type (type_), parent (0)
  167. {
  168. }
  169. ValueTree::SharedObject::SharedObject (const SharedObject& other)
  170. : type (other.type), properties (other.properties), parent (0)
  171. {
  172. for (int i = 0; i < other.children.size(); ++i)
  173. {
  174. SharedObject* const child = new SharedObject (*other.children.getUnchecked(i));
  175. child->parent = this;
  176. children.add (child);
  177. }
  178. }
  179. ValueTree::SharedObject::~SharedObject()
  180. {
  181. jassert (parent == 0); // this should never happen unless something isn't obeying the ref-counting!
  182. for (int i = children.size(); --i >= 0;)
  183. {
  184. const SharedObjectPtr c (children.getUnchecked(i));
  185. c->parent = 0;
  186. children.remove (i);
  187. c->sendParentChangeMessage();
  188. }
  189. }
  190. //==============================================================================
  191. void ValueTree::SharedObject::sendPropertyChangeMessage (ValueTree& tree, const Identifier& property)
  192. {
  193. for (int i = valueTreesWithListeners.size(); --i >= 0;)
  194. {
  195. ValueTree* const v = valueTreesWithListeners[i];
  196. if (v != 0)
  197. v->listeners.call (&ValueTree::Listener::valueTreePropertyChanged, tree, property);
  198. }
  199. }
  200. void ValueTree::SharedObject::sendPropertyChangeMessage (const Identifier& property)
  201. {
  202. ValueTree tree (this);
  203. ValueTree::SharedObject* t = this;
  204. while (t != 0)
  205. {
  206. t->sendPropertyChangeMessage (tree, property);
  207. t = t->parent;
  208. }
  209. }
  210. void ValueTree::SharedObject::sendChildChangeMessage (ValueTree& tree)
  211. {
  212. for (int i = valueTreesWithListeners.size(); --i >= 0;)
  213. {
  214. ValueTree* const v = valueTreesWithListeners[i];
  215. if (v != 0)
  216. v->listeners.call (&ValueTree::Listener::valueTreeChildrenChanged, tree);
  217. }
  218. }
  219. void ValueTree::SharedObject::sendChildChangeMessage()
  220. {
  221. ValueTree tree (this);
  222. ValueTree::SharedObject* t = this;
  223. while (t != 0)
  224. {
  225. t->sendChildChangeMessage (tree);
  226. t = t->parent;
  227. }
  228. }
  229. void ValueTree::SharedObject::sendParentChangeMessage()
  230. {
  231. ValueTree tree (this);
  232. int i;
  233. for (i = children.size(); --i >= 0;)
  234. {
  235. SharedObject* const t = children[i];
  236. if (t != 0)
  237. t->sendParentChangeMessage();
  238. }
  239. for (i = valueTreesWithListeners.size(); --i >= 0;)
  240. {
  241. ValueTree* const v = valueTreesWithListeners[i];
  242. if (v != 0)
  243. v->listeners.call (&ValueTree::Listener::valueTreeParentChanged, tree);
  244. }
  245. }
  246. //==============================================================================
  247. const var& ValueTree::SharedObject::getProperty (const Identifier& name) const
  248. {
  249. return properties [name];
  250. }
  251. const var ValueTree::SharedObject::getProperty (const Identifier& name, const var& defaultReturnValue) const
  252. {
  253. return properties.getWithDefault (name, defaultReturnValue);
  254. }
  255. void ValueTree::SharedObject::setProperty (const Identifier& name, const var& newValue, UndoManager* const undoManager)
  256. {
  257. if (undoManager == 0)
  258. {
  259. if (properties.set (name, newValue))
  260. sendPropertyChangeMessage (name);
  261. }
  262. else
  263. {
  264. var* const existingValue = properties.getItem (name);
  265. if (existingValue != 0)
  266. {
  267. if (*existingValue != newValue)
  268. undoManager->perform (new SetPropertyAction (this, name, newValue, properties [name], false, false));
  269. }
  270. else
  271. {
  272. undoManager->perform (new SetPropertyAction (this, name, newValue, var::null, true, false));
  273. }
  274. }
  275. }
  276. bool ValueTree::SharedObject::hasProperty (const Identifier& name) const
  277. {
  278. return properties.contains (name);
  279. }
  280. void ValueTree::SharedObject::removeProperty (const Identifier& name, UndoManager* const undoManager)
  281. {
  282. if (undoManager == 0)
  283. {
  284. if (properties.remove (name))
  285. sendPropertyChangeMessage (name);
  286. }
  287. else
  288. {
  289. if (properties.contains (name))
  290. undoManager->perform (new SetPropertyAction (this, name, var::null, properties [name], false, true));
  291. }
  292. }
  293. void ValueTree::SharedObject::removeAllProperties (UndoManager* const undoManager)
  294. {
  295. if (undoManager == 0)
  296. {
  297. while (properties.size() > 0)
  298. {
  299. const Identifier name (properties.getName (properties.size() - 1));
  300. properties.remove (name);
  301. sendPropertyChangeMessage (name);
  302. }
  303. }
  304. else
  305. {
  306. for (int i = properties.size(); --i >= 0;)
  307. undoManager->perform (new SetPropertyAction (this, properties.getName(i), var::null, properties.getValueAt(i), false, true));
  308. }
  309. }
  310. ValueTree ValueTree::SharedObject::getChildWithName (const Identifier& typeToMatch) const
  311. {
  312. for (int i = 0; i < children.size(); ++i)
  313. if (children.getUnchecked(i)->type == typeToMatch)
  314. return ValueTree (static_cast <SharedObject*> (children.getUnchecked(i)));
  315. return ValueTree::invalid;
  316. }
  317. ValueTree ValueTree::SharedObject::getOrCreateChildWithName (const Identifier& typeToMatch, UndoManager* undoManager)
  318. {
  319. for (int i = 0; i < children.size(); ++i)
  320. if (children.getUnchecked(i)->type == typeToMatch)
  321. return ValueTree (static_cast <SharedObject*> (children.getUnchecked(i)));
  322. SharedObject* const newObject = new SharedObject (typeToMatch);
  323. addChild (newObject, -1, undoManager);
  324. return ValueTree (newObject);
  325. }
  326. ValueTree ValueTree::SharedObject::getChildWithProperty (const Identifier& propertyName, const var& propertyValue) const
  327. {
  328. for (int i = 0; i < children.size(); ++i)
  329. if (children.getUnchecked(i)->getProperty (propertyName) == propertyValue)
  330. return ValueTree (static_cast <SharedObject*> (children.getUnchecked(i)));
  331. return ValueTree::invalid;
  332. }
  333. bool ValueTree::SharedObject::isAChildOf (const SharedObject* const possibleParent) const
  334. {
  335. const SharedObject* p = parent;
  336. while (p != 0)
  337. {
  338. if (p == possibleParent)
  339. return true;
  340. p = p->parent;
  341. }
  342. return false;
  343. }
  344. int ValueTree::SharedObject::indexOf (const ValueTree& child) const
  345. {
  346. return children.indexOf (child.object);
  347. }
  348. void ValueTree::SharedObject::addChild (SharedObject* child, int index, UndoManager* const undoManager)
  349. {
  350. if (child != 0 && child->parent != this)
  351. {
  352. if (child != this && ! isAChildOf (child))
  353. {
  354. // You should always make sure that a child is removed from its previous parent before
  355. // adding it somewhere else - otherwise, it's ambiguous as to whether a different
  356. // undomanager should be used when removing it from its current parent..
  357. jassert (child->parent == 0);
  358. if (child->parent != 0)
  359. {
  360. jassert (child->parent->children.indexOf (child) >= 0);
  361. child->parent->removeChild (child->parent->children.indexOf (child), undoManager);
  362. }
  363. if (undoManager == 0)
  364. {
  365. children.insert (index, child);
  366. child->parent = this;
  367. sendChildChangeMessage();
  368. child->sendParentChangeMessage();
  369. }
  370. else
  371. {
  372. if (index < 0)
  373. index = children.size();
  374. undoManager->perform (new AddOrRemoveChildAction (this, index, child));
  375. }
  376. }
  377. else
  378. {
  379. // You're attempting to create a recursive loop! A node
  380. // can't be a child of one of its own children!
  381. jassertfalse;
  382. }
  383. }
  384. }
  385. void ValueTree::SharedObject::removeChild (const int childIndex, UndoManager* const undoManager)
  386. {
  387. const SharedObjectPtr child (children [childIndex]);
  388. if (child != 0)
  389. {
  390. if (undoManager == 0)
  391. {
  392. children.remove (childIndex);
  393. child->parent = 0;
  394. sendChildChangeMessage();
  395. child->sendParentChangeMessage();
  396. }
  397. else
  398. {
  399. undoManager->perform (new AddOrRemoveChildAction (this, childIndex, 0));
  400. }
  401. }
  402. }
  403. void ValueTree::SharedObject::removeAllChildren (UndoManager* const undoManager)
  404. {
  405. while (children.size() > 0)
  406. removeChild (children.size() - 1, undoManager);
  407. }
  408. void ValueTree::SharedObject::moveChild (int currentIndex, int newIndex, UndoManager* undoManager)
  409. {
  410. // The source index must be a valid index!
  411. jassert (((unsigned int) currentIndex) < (unsigned int) children.size());
  412. if (currentIndex != newIndex
  413. && ((unsigned int) currentIndex) < (unsigned int) children.size())
  414. {
  415. if (undoManager == 0)
  416. {
  417. children.move (currentIndex, newIndex);
  418. sendChildChangeMessage();
  419. }
  420. else
  421. {
  422. if (((unsigned int) newIndex) >= (unsigned int) children.size())
  423. newIndex = children.size() - 1;
  424. undoManager->perform (new MoveChildAction (this, currentIndex, newIndex));
  425. }
  426. }
  427. }
  428. bool ValueTree::SharedObject::isEquivalentTo (const SharedObject& other) const
  429. {
  430. if (type != other.type
  431. || properties.size() != other.properties.size()
  432. || children.size() != other.children.size()
  433. || properties != other.properties)
  434. return false;
  435. for (int i = 0; i < children.size(); ++i)
  436. if (! children.getUnchecked(i)->isEquivalentTo (*other.children.getUnchecked(i)))
  437. return false;
  438. return true;
  439. }
  440. //==============================================================================
  441. ValueTree::ValueTree() throw()
  442. : object (0)
  443. {
  444. }
  445. const ValueTree ValueTree::invalid;
  446. ValueTree::ValueTree (const Identifier& type_)
  447. : object (new ValueTree::SharedObject (type_))
  448. {
  449. jassert (type_.toString().isNotEmpty()); // All objects should be given a sensible type name!
  450. }
  451. ValueTree::ValueTree (SharedObject* const object_)
  452. : object (object_)
  453. {
  454. }
  455. ValueTree::ValueTree (const ValueTree& other)
  456. : object (other.object)
  457. {
  458. }
  459. ValueTree& ValueTree::operator= (const ValueTree& other)
  460. {
  461. if (listeners.size() > 0)
  462. {
  463. if (object != 0)
  464. object->valueTreesWithListeners.removeValue (this);
  465. if (other.object != 0)
  466. other.object->valueTreesWithListeners.add (this);
  467. }
  468. object = other.object;
  469. return *this;
  470. }
  471. ValueTree::~ValueTree()
  472. {
  473. if (listeners.size() > 0 && object != 0)
  474. object->valueTreesWithListeners.removeValue (this);
  475. }
  476. bool ValueTree::operator== (const ValueTree& other) const throw()
  477. {
  478. return object == other.object;
  479. }
  480. bool ValueTree::operator!= (const ValueTree& other) const throw()
  481. {
  482. return object != other.object;
  483. }
  484. bool ValueTree::isEquivalentTo (const ValueTree& other) const
  485. {
  486. return object == other.object
  487. || (object != 0 && other.object != 0 && object->isEquivalentTo (*other.object));
  488. }
  489. ValueTree ValueTree::createCopy() const
  490. {
  491. return ValueTree (object != 0 ? new SharedObject (*object) : 0);
  492. }
  493. bool ValueTree::hasType (const Identifier& typeName) const
  494. {
  495. return object != 0 && object->type == typeName;
  496. }
  497. const Identifier ValueTree::getType() const
  498. {
  499. return object != 0 ? object->type : Identifier();
  500. }
  501. ValueTree ValueTree::getParent() const
  502. {
  503. return ValueTree (object != 0 ? object->parent : (SharedObject*) 0);
  504. }
  505. const var& ValueTree::operator[] (const Identifier& name) const
  506. {
  507. return object == 0 ? var::null : object->getProperty (name);
  508. }
  509. const var& ValueTree::getProperty (const Identifier& name) const
  510. {
  511. return object == 0 ? var::null : object->getProperty (name);
  512. }
  513. const var ValueTree::getProperty (const Identifier& name, const var& defaultReturnValue) const
  514. {
  515. return object == 0 ? defaultReturnValue : object->getProperty (name, defaultReturnValue);
  516. }
  517. void ValueTree::setProperty (const Identifier& name, const var& newValue, UndoManager* const undoManager)
  518. {
  519. jassert (name.toString().isNotEmpty());
  520. if (object != 0 && name.toString().isNotEmpty())
  521. object->setProperty (name, newValue, undoManager);
  522. }
  523. bool ValueTree::hasProperty (const Identifier& name) const
  524. {
  525. return object != 0 && object->hasProperty (name);
  526. }
  527. void ValueTree::removeProperty (const Identifier& name, UndoManager* const undoManager)
  528. {
  529. if (object != 0)
  530. object->removeProperty (name, undoManager);
  531. }
  532. void ValueTree::removeAllProperties (UndoManager* const undoManager)
  533. {
  534. if (object != 0)
  535. object->removeAllProperties (undoManager);
  536. }
  537. int ValueTree::getNumProperties() const
  538. {
  539. return object == 0 ? 0 : object->properties.size();
  540. }
  541. const Identifier ValueTree::getPropertyName (const int index) const
  542. {
  543. return object == 0 ? Identifier()
  544. : object->properties.getName (index);
  545. }
  546. //==============================================================================
  547. class ValueTreePropertyValueSource : public Value::ValueSource,
  548. public ValueTree::Listener
  549. {
  550. public:
  551. ValueTreePropertyValueSource (const ValueTree& tree_,
  552. const Identifier& property_,
  553. UndoManager* const undoManager_)
  554. : tree (tree_),
  555. property (property_),
  556. undoManager (undoManager_)
  557. {
  558. tree.addListener (this);
  559. }
  560. ~ValueTreePropertyValueSource()
  561. {
  562. tree.removeListener (this);
  563. }
  564. const var getValue() const
  565. {
  566. return tree [property];
  567. }
  568. void setValue (const var& newValue)
  569. {
  570. tree.setProperty (property, newValue, undoManager);
  571. }
  572. void valueTreePropertyChanged (ValueTree& treeWhosePropertyHasChanged, const Identifier& changedProperty)
  573. {
  574. if (tree == treeWhosePropertyHasChanged && property == changedProperty)
  575. sendChangeMessage (false);
  576. }
  577. void valueTreeChildrenChanged (ValueTree&) {}
  578. void valueTreeParentChanged (ValueTree&) {}
  579. private:
  580. ValueTree tree;
  581. const Identifier property;
  582. UndoManager* const undoManager;
  583. ValueTreePropertyValueSource& operator= (const ValueTreePropertyValueSource&);
  584. };
  585. Value ValueTree::getPropertyAsValue (const Identifier& name, UndoManager* const undoManager) const
  586. {
  587. return Value (new ValueTreePropertyValueSource (*this, name, undoManager));
  588. }
  589. //==============================================================================
  590. int ValueTree::getNumChildren() const
  591. {
  592. return object == 0 ? 0 : object->children.size();
  593. }
  594. ValueTree ValueTree::getChild (int index) const
  595. {
  596. return ValueTree (object != 0 ? (SharedObject*) object->children [index] : (SharedObject*) 0);
  597. }
  598. ValueTree ValueTree::getChildWithName (const Identifier& type) const
  599. {
  600. return object != 0 ? object->getChildWithName (type) : ValueTree::invalid;
  601. }
  602. ValueTree ValueTree::getOrCreateChildWithName (const Identifier& type, UndoManager* undoManager)
  603. {
  604. return object != 0 ? object->getOrCreateChildWithName (type, undoManager) : ValueTree::invalid;
  605. }
  606. ValueTree ValueTree::getChildWithProperty (const Identifier& propertyName, const var& propertyValue) const
  607. {
  608. return object != 0 ? object->getChildWithProperty (propertyName, propertyValue) : ValueTree::invalid;
  609. }
  610. bool ValueTree::isAChildOf (const ValueTree& possibleParent) const
  611. {
  612. return object != 0 && object->isAChildOf (possibleParent.object);
  613. }
  614. int ValueTree::indexOf (const ValueTree& child) const
  615. {
  616. return object != 0 ? object->indexOf (child) : -1;
  617. }
  618. void ValueTree::addChild (const ValueTree& child, int index, UndoManager* const undoManager)
  619. {
  620. if (object != 0)
  621. object->addChild (child.object, index, undoManager);
  622. }
  623. void ValueTree::removeChild (const int childIndex, UndoManager* const undoManager)
  624. {
  625. if (object != 0)
  626. object->removeChild (childIndex, undoManager);
  627. }
  628. void ValueTree::removeChild (const ValueTree& child, UndoManager* const undoManager)
  629. {
  630. if (object != 0)
  631. object->removeChild (object->children.indexOf (child.object), undoManager);
  632. }
  633. void ValueTree::removeAllChildren (UndoManager* const undoManager)
  634. {
  635. if (object != 0)
  636. object->removeAllChildren (undoManager);
  637. }
  638. void ValueTree::moveChild (int currentIndex, int newIndex, UndoManager* undoManager)
  639. {
  640. if (object != 0)
  641. object->moveChild (currentIndex, newIndex, undoManager);
  642. }
  643. //==============================================================================
  644. void ValueTree::addListener (Listener* listener)
  645. {
  646. if (listener != 0)
  647. {
  648. if (listeners.size() == 0 && object != 0)
  649. object->valueTreesWithListeners.add (this);
  650. listeners.add (listener);
  651. }
  652. }
  653. void ValueTree::removeListener (Listener* listener)
  654. {
  655. listeners.remove (listener);
  656. if (listeners.size() == 0 && object != 0)
  657. object->valueTreesWithListeners.removeValue (this);
  658. }
  659. //==============================================================================
  660. XmlElement* ValueTree::SharedObject::createXml() const
  661. {
  662. XmlElement* xml = new XmlElement (type.toString());
  663. int i;
  664. for (i = 0; i < properties.size(); ++i)
  665. {
  666. Identifier name (properties.getName(i));
  667. const var& v = properties [name];
  668. jassert (! v.isObject()); // DynamicObjects can't be stored as XML!
  669. xml->setAttribute (name.toString(), v.toString());
  670. }
  671. for (i = 0; i < children.size(); ++i)
  672. xml->addChildElement (children.getUnchecked(i)->createXml());
  673. return xml;
  674. }
  675. XmlElement* ValueTree::createXml() const
  676. {
  677. return object != 0 ? object->createXml() : 0;
  678. }
  679. ValueTree ValueTree::fromXml (const XmlElement& xml)
  680. {
  681. ValueTree v (xml.getTagName());
  682. const int numAtts = xml.getNumAttributes(); // xxx inefficient - should write an att iterator..
  683. for (int i = 0; i < numAtts; ++i)
  684. v.setProperty (xml.getAttributeName (i), var (xml.getAttributeValue (i)), 0);
  685. forEachXmlChildElement (xml, e)
  686. {
  687. v.addChild (fromXml (*e), -1, 0);
  688. }
  689. return v;
  690. }
  691. //==============================================================================
  692. void ValueTree::writeToStream (OutputStream& output)
  693. {
  694. output.writeString (getType().toString());
  695. const int numProps = getNumProperties();
  696. output.writeCompressedInt (numProps);
  697. int i;
  698. for (i = 0; i < numProps; ++i)
  699. {
  700. const Identifier name (getPropertyName(i));
  701. output.writeString (name.toString());
  702. getProperty(name).writeToStream (output);
  703. }
  704. const int numChildren = getNumChildren();
  705. output.writeCompressedInt (numChildren);
  706. for (i = 0; i < numChildren; ++i)
  707. getChild (i).writeToStream (output);
  708. }
  709. ValueTree ValueTree::readFromStream (InputStream& input)
  710. {
  711. const String type (input.readString());
  712. if (type.isEmpty())
  713. return ValueTree::invalid;
  714. ValueTree v (type);
  715. const int numProps = input.readCompressedInt();
  716. if (numProps < 0)
  717. {
  718. jassertfalse; // trying to read corrupted data!
  719. return v;
  720. }
  721. int i;
  722. for (i = 0; i < numProps; ++i)
  723. {
  724. const String name (input.readString());
  725. jassert (name.isNotEmpty());
  726. const var value (var::readFromStream (input));
  727. v.setProperty (name, value, 0);
  728. }
  729. const int numChildren = input.readCompressedInt();
  730. for (i = 0; i < numChildren; ++i)
  731. v.addChild (readFromStream (input), -1, 0);
  732. return v;
  733. }
  734. ValueTree ValueTree::readFromData (const void* const data, const size_t numBytes)
  735. {
  736. MemoryInputStream in (data, numBytes, false);
  737. return readFromStream (in);
  738. }
  739. END_JUCE_NAMESPACE