1 /* 2 * Copyright (C) 2010-2012 Carl Hetherington <carl@carlh.net> 3 * Copyright (C) 2010-2016 Paul Davis <paul@linuxaudiosystems.com> 4 * Copyright (C) 2010 David Robillard <d@drobilla.net> 5 * Copyright (C) 2014-2015 Robin Gareus <robin@gareus.org> 6 * Copyright (C) 2015-2016 Tim Mayberry <mojofunk@gmail.com> 7 * 8 * This program is free software; you can redistribute it and/or modify 9 * it under the terms of the GNU General Public License as published by 10 * the Free Software Foundation; either version 2 of the License, or 11 * (at your option) any later version. 12 * 13 * This program is distributed in the hope that it will be useful, 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 * GNU General Public License for more details. 17 * 18 * You should have received a copy of the GNU General Public License along 19 * with this program; if not, write to the Free Software Foundation, Inc., 20 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 21 */ 22 23 #ifndef __pbd_properties_h__ 24 #define __pbd_properties_h__ 25 26 #include <string> 27 #include <list> 28 #include <set> 29 30 #include "pbd/libpbd_visibility.h" 31 #include "pbd/xml++.h" 32 #include "pbd/property_basics.h" 33 #include "pbd/property_list.h" 34 #include "pbd/enumwriter.h" 35 #include "pbd/stateful.h" 36 #include "pbd/string_convert.h" 37 38 namespace PBD { 39 40 /** Parent class for classes which represent a single scalar property in a Stateful object */ 41 template<class T> 42 class /*LIBPBD_API*/ PropertyTemplate : public PropertyBase 43 { 44 public: PropertyTemplate(PropertyDescriptor<T> p,T const & v)45 PropertyTemplate (PropertyDescriptor<T> p, T const& v) 46 : PropertyBase (p.property_id) 47 , _have_old (false) 48 , _current (v) 49 {} 50 PropertyTemplate(PropertyDescriptor<T> p,T const & o,T const & c)51 PropertyTemplate (PropertyDescriptor<T> p, T const& o, T const& c) 52 : PropertyBase (p.property_id) 53 , _have_old (true) 54 , _current (c) 55 , _old (o) 56 {} 57 PropertyTemplate(PropertyDescriptor<T> p,PropertyTemplate<T> const & s)58 PropertyTemplate (PropertyDescriptor<T> p, PropertyTemplate<T> const & s) 59 : PropertyBase (p.property_id) 60 , _have_old (false) 61 , _current (s._current) 62 {} 63 64 65 /* OPERATORS / ACCESSORS */ 66 67 T & operator=(T const& v) { 68 set (v); 69 return _current; 70 } 71 72 /* This will mean that, if fred and jim are both PropertyTemplates, 73 * fred = jim will result in fred taking on jim's current value, 74 * but NOT jim's property ID. 75 */ 76 PropertyTemplate<T> & operator= (PropertyTemplate<T> const & p) { 77 set (p._current); 78 return *this; 79 } 80 81 T & operator+=(T const& v) { 82 set (_current + v); 83 return _current; 84 } 85 86 bool operator==(const T& other) const { 87 return _current == other; 88 } 89 90 bool operator!=(const T& other) const { 91 return _current != other; 92 } 93 94 operator T const &() const { 95 return _current; 96 } 97 val()98 T const& val () const { 99 return _current; 100 } 101 102 103 /* MANAGEMENT OF Stateful State */ 104 set_value(XMLNode const & node)105 bool set_value (XMLNode const & node) { 106 107 XMLProperty const* p = node.property (property_name()); 108 109 if (p) { 110 T const v = from_string (p->value ()); 111 112 if (v != _current) { 113 set (v); 114 return true; 115 } 116 } 117 118 return false; 119 } 120 get_value(XMLNode & node)121 void get_value (XMLNode & node) const { 122 node.set_property (property_name (), _current); 123 } 124 125 126 /* MANAGEMENT OF HISTORY */ 127 clear_changes()128 void clear_changes () { 129 _have_old = false; 130 } 131 changed()132 bool changed () const { return _have_old; } 133 invert()134 void invert () { 135 T const tmp = _current; 136 _current = _old; 137 _old = tmp; 138 } 139 140 141 /* TRANSFERRING HISTORY TO / FROM A StatefulDiffCommand */ 142 get_changes_as_xml(XMLNode * history_node)143 void get_changes_as_xml (XMLNode* history_node) const { 144 XMLNode* node = history_node->add_child (property_name()); 145 node->set_property ("from", _old); 146 node->set_property ("to", _current); 147 } 148 get_changes_as_properties(PropertyList & changes,Command *)149 void get_changes_as_properties (PropertyList& changes, Command *) const { 150 if (this->_have_old) { 151 changes.add (clone ()); 152 } 153 } 154 155 156 /* VARIOUS */ 157 apply_changes(PropertyBase const * p)158 void apply_changes (PropertyBase const * p) { 159 T v = dynamic_cast<const PropertyTemplate<T>* > (p)->val (); 160 if (v != _current) { 161 set (v); 162 } 163 } 164 165 protected: 166 set(T const & v)167 void set (T const& v) { 168 if (v != _current) { 169 if (!_have_old) { 170 _old = _current; 171 _have_old = true; 172 } else { 173 if (v == _old) { 174 /* value has been reset to the value 175 at the start of a history transaction, 176 before clear_changes() is called. 177 thus there is effectively no apparent 178 history for this property. 179 */ 180 _have_old = false; 181 } 182 } 183 184 _current = v; 185 } 186 } 187 188 virtual std::string to_string (T const& v) const = 0; 189 virtual T from_string (std::string const& s) const = 0; 190 191 bool _have_old; 192 T _current; 193 T _old; 194 195 private: 196 /* disallow copy-construction; it's not obvious whether it should mean 197 a copy of just the value, or the value and property ID. 198 */ 199 PropertyTemplate (PropertyTemplate<T> const &); 200 }; 201 202 template<class T> /*LIBPBD_API*/ 203 std::ostream & operator<<(std::ostream& os, PropertyTemplate<T> const& s) 204 { 205 return os << s.val (); 206 } 207 208 /** Representation of a single piece of scalar state in a Stateful; for use 209 * with types that can be written to / read from stringstreams. 210 */ 211 template<class T> 212 class /*LIBPBD_API*/ Property : public PropertyTemplate<T> 213 { 214 public: Property(PropertyDescriptor<T> q,T const & v)215 Property (PropertyDescriptor<T> q, T const& v) 216 : PropertyTemplate<T> (q, v) 217 {} 218 Property(PropertyDescriptor<T> q,T const & o,T const & c)219 Property (PropertyDescriptor<T> q, T const& o, T const& c) 220 : PropertyTemplate<T> (q, o, c) 221 {} 222 Property(PropertyDescriptor<T> q,Property<T> const & v)223 Property (PropertyDescriptor<T> q, Property<T> const& v) 224 : PropertyTemplate<T> (q, v) 225 {} 226 clone()227 Property<T>* clone () const { 228 return new Property<T> (this->property_id(), this->_old, this->_current); 229 } 230 clone_from_xml(const XMLNode & node)231 Property<T>* clone_from_xml (const XMLNode& node) const { 232 XMLNodeList const & children = node.children (); 233 XMLNodeList::const_iterator i = children.begin(); 234 while (i != children.end() && (*i)->name() != this->property_name()) { 235 ++i; 236 } 237 238 if (i == children.end()) { 239 return 0; 240 } 241 XMLProperty const * from = (*i)->property ("from"); 242 XMLProperty const * to = (*i)->property ("to"); 243 244 if (!from || !to) { 245 return 0; 246 } 247 248 return new Property<T> (this->property_id(), from_string (from->value()), from_string (to->value ())); 249 } 250 251 T & operator=(T const& v) { 252 this->set (v); 253 return this->_current; 254 } 255 256 Property<T>& operator=(Property<T> const& v) { 257 this->set (v); 258 return *this; 259 } 260 261 private: 262 friend class PropertyFactory; 263 264 /* no copy-construction */ 265 Property (Property<T> const &); 266 267 /* Note that we do not set a locale for the streams used 268 * in to_string() or from_string(), because we want the 269 * format to be portable across locales (i.e. C or 270 * POSIX). Also, there is the small matter of 271 * std::locale aborting on OS X if used with anything 272 * other than C or POSIX locales. 273 */ to_string(T const & v)274 virtual std::string to_string (T const& v) const { 275 return PBD::to_string (v); 276 } 277 from_string(std::string const & s)278 virtual T from_string (std::string const& s) const { 279 return PBD::string_to<T>(s); 280 } 281 282 }; 283 284 /** Specialization, for std::string which is common and special (see to_string() and from_string() 285 * Using stringstream to read from a std::string is easy to get wrong because of whitespace 286 * separators, etc. 287 */ 288 template<> 289 class /*LIBPBD_API*/ Property<std::string> : public PropertyTemplate<std::string> 290 { 291 public: Property(PropertyDescriptor<std::string> d,std::string const & v)292 Property (PropertyDescriptor<std::string> d, std::string const & v) 293 : PropertyTemplate<std::string> (d, v) 294 {} 295 Property(PropertyDescriptor<std::string> d,std::string const & o,std::string const & c)296 Property (PropertyDescriptor<std::string> d, std::string const & o, std::string const & c) 297 : PropertyTemplate<std::string> (d, o, c) 298 {} 299 clone()300 Property<std::string>* clone () const { 301 return new Property<std::string> (this->property_id(), _old, _current); 302 } 303 304 std::string & operator= (std::string const& v) { 305 this->set (v); 306 return this->_current; 307 } 308 309 private: to_string(std::string const & v)310 std::string to_string (std::string const& v) const { 311 return v; 312 } 313 from_string(std::string const & s)314 std::string from_string (std::string const& s) const { 315 return s; 316 } 317 318 /* no copy-construction */ 319 Property (Property<std::string> const &); 320 }; 321 322 template<class T> 323 class /*LIBPBD_API*/ EnumProperty : public Property<T> 324 { 325 public: EnumProperty(PropertyDescriptor<T> q,T const & v)326 EnumProperty (PropertyDescriptor<T> q, T const& v) 327 : Property<T> (q, v) 328 {} 329 330 T & operator=(T const& v) { 331 this->set (v); 332 return this->_current; 333 } 334 335 private: to_string(T const & v)336 std::string to_string (T const & v) const { 337 return enum_2_string (v); 338 } 339 from_string(std::string const & s)340 T from_string (std::string const & s) const { 341 return static_cast<T> (string_2_enum (s, this->_current)); 342 } 343 344 /* no copy-construction */ 345 EnumProperty (EnumProperty const &); 346 EnumProperty& operator= (EnumProperty const &); 347 }; 348 349 /** A Property which holds a shared_ptr to a Stateful object, 350 * and handles undo using the somewhat inefficient approach 351 * of saving the complete XML state of its object before and 352 * after changes. A sort of half-way house between the old 353 * complete-state undo system and the new difference-based 354 * one. 355 */ 356 template <class T> 357 class /*LIBPBD_API*/ SharedStatefulProperty : public PropertyBase 358 { 359 public: 360 typedef boost::shared_ptr<T> Ptr; 361 SharedStatefulProperty(PropertyID d,Ptr p)362 SharedStatefulProperty (PropertyID d, Ptr p) 363 : PropertyBase (d) 364 , _current (p) 365 { 366 367 } 368 SharedStatefulProperty(PropertyID d,Ptr o,Ptr c)369 SharedStatefulProperty (PropertyID d, Ptr o, Ptr c) 370 : PropertyBase (d) 371 , _old (o) 372 , _current (c) 373 { 374 375 } 376 set_value(XMLNode const & node)377 bool set_value (XMLNode const & node) { 378 379 /* Look for our node */ 380 XMLNode* n = node.child (property_name ()); 381 if (!n) { 382 return false; 383 } 384 385 /* And there should be one child which is the state of our T */ 386 XMLNodeList const & children = n->children (); 387 if (children.size() != 1) { 388 return false; 389 } 390 391 _current->set_state (*children.front (), Stateful::current_state_version); 392 return true; 393 } 394 get_value(XMLNode & node)395 void get_value (XMLNode & node) const { 396 XMLNode* n = node.add_child (property_name ()); 397 n->add_child_nocopy (_current->get_state ()); 398 } 399 clear_changes()400 void clear_changes () { 401 /* We are starting to change things, so _old gets set up 402 with the current state. 403 */ 404 _old.reset (new T (*_current.get())); 405 } 406 changed()407 bool changed () const { 408 if (!_old) { 409 return false; 410 } 411 /* Expensive, but, hey; this requires operator!= in our T */ 412 return (*_old != *_current); 413 } 414 invert()415 void invert () { 416 _current.swap (_old); 417 } 418 get_changes_as_xml(XMLNode * history_node)419 void get_changes_as_xml (XMLNode* history_node) const { 420 /* We express the diff as before and after state, just 421 as MementoCommand does. 422 */ 423 XMLNode* p = history_node->add_child (property_name ()); 424 XMLNode* from = p->add_child ("from"); 425 from->add_child_nocopy (_old->get_state ()); 426 XMLNode* to = p->add_child ("to"); 427 to->add_child_nocopy (_current->get_state ()); 428 } 429 get_changes_as_properties(PropertyList & changes,Command *)430 void get_changes_as_properties (PropertyList& changes, Command *) const { 431 if (changed ()) { 432 changes.add (clone ()); 433 } 434 } 435 apply_changes(PropertyBase const * p)436 void apply_changes (PropertyBase const * p) { 437 *_current = *(dynamic_cast<SharedStatefulProperty const *> (p))->val (); 438 } 439 val()440 Ptr val () const { 441 return _current; 442 } 443 444 T* operator-> () const { 445 return _current.operator-> (); 446 } 447 448 operator bool () const { 449 return _current ? true : false; 450 } 451 452 protected: 453 454 Ptr _old; 455 Ptr _current; 456 457 private: 458 459 /* No copy-construction nor assignment */ 460 SharedStatefulProperty (SharedStatefulProperty<T> const &); 461 SharedStatefulProperty<T>& operator= (SharedStatefulProperty<T> const &); 462 }; 463 464 } /* namespace PBD */ 465 466 #include "pbd/property_list_impl.h" 467 #include "pbd/property_basics_impl.h" 468 469 #endif /* __pbd_properties_h__ */ 470