midway snapshot of work done on managing Region & Source lifetimes correctly. may...
[ardour.git] / libs / pbd / stateful_diff_command.cc
1 /*
2     Copyright (C) 2010 Paul Davis 
3
4     This program is free software; you can redistribute it and/or modify
5     it under the terms of the GNU General Public License as published by
6     the Free Software Foundation; either version 2 of the License, or
7     (at your option) any later version.
8
9     This program is distributed in the hope that it will be useful,
10     but WITHOUT ANY WARRANTY; without even the implied warranty of
11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12     GNU General Public License for more details.
13
14     You should have received a copy of the GNU General Public License
15     along with this program; if not, write to the Free Software
16     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17
18 */
19
20 #include <iostream>
21
22 #include "pbd/stateful_diff_command.h"
23 #include "pbd/property_list.h"
24 #include "pbd/demangle.h"
25 #include "i18n.h"
26
27 using namespace std;
28 using namespace PBD;
29
30 /** Create a new StatefulDiffCommand by examining the changes made to a Stateful
31  *  since the last time that clear_history was called on it.
32  *  @param s Stateful object.
33  */
34
35 StatefulDiffCommand::StatefulDiffCommand (boost::shared_ptr<StatefulDestructible> s)
36         : _object (s)
37         , _undo (new PropertyList)
38         , _redo (new PropertyList)
39 {
40         s->diff (*_undo, *_redo, this);
41
42         /* if the stateful object that this command refers to goes away,
43            be sure to notify owners of this command.
44         */
45
46         s->DropReferences.connect_same_thread (*this, boost::bind (&Destructible::drop_references, this));
47 }
48
49 StatefulDiffCommand::StatefulDiffCommand (boost::shared_ptr<StatefulDestructible> s, XMLNode const & n)
50         : _object (s)
51         , _undo (0)
52         , _redo (0)
53 {
54         const XMLNodeList& children (n.children());
55
56         for (XMLNodeList::const_iterator i = children.begin(); i != children.end(); ++i) {
57                 if ((*i)->name() == X_("Undo")) {
58                         _undo = s->property_factory (**i);
59                 } else if ((*i)->name() == X_("Do")) {
60                         _redo = s->property_factory (**i);
61                 }
62         }
63
64         assert (_undo != 0);
65         assert (_redo != 0);
66
67         /* if the stateful object that this command refers to goes away,
68            be sure to notify owners of this command.
69         */
70
71         s->DropReferences.connect_same_thread (*this, boost::bind (&Destructible::drop_references, this));
72 }
73
74 StatefulDiffCommand::~StatefulDiffCommand ()
75 {
76         drop_references ();
77
78         delete _undo;
79         delete _redo;
80 }
81
82 void
83 StatefulDiffCommand::operator() ()
84 {
85         boost::shared_ptr<Stateful> s (_object.lock());
86
87         if (s) {
88                 s->set_properties (*_redo);
89         }
90 }
91
92 void
93 StatefulDiffCommand::undo ()
94 {
95         boost::shared_ptr<Stateful> s (_object.lock());
96
97         if (s) {
98                 std::cerr << "Undoing a stateful diff command\n";
99                 s->set_properties (*_undo);
100         }
101 }
102
103 XMLNode&
104 StatefulDiffCommand::get_state ()
105 {
106         boost::shared_ptr<Stateful> s (_object.lock());
107
108         if (!s) {
109                 /* XXX should we throw? */
110                 return * new XMLNode("");
111         }
112
113         XMLNode* node = new XMLNode (X_("StatefulDiffCommand"));
114
115         node->add_property ("obj-id", s->id().to_s());
116         node->add_property ("type-name", demangled_name (*s.get()));
117
118         XMLNode* undo = new XMLNode (X_("Undo"));
119         XMLNode* redo = new XMLNode (X_("Do"));
120
121         _undo->add_history_state (undo);
122         _redo->add_history_state (redo);
123         
124         node->add_child_nocopy (*undo);
125         node->add_child_nocopy (*redo);
126
127         return *node;
128 }