merged with 1697 revision of trunk (which is post-rc1 but pre-rc2
[ardour.git] / libs / ardour / session_command.cc
1 #include <ardour/session.h>
2 #include <ardour/route.h>
3 #include <pbd/memento_command.h>
4 #include <ardour/diskstream.h>
5 #include <ardour/playlist.h>
6 #include <ardour/audioplaylist.h>
7 #include <ardour/audio_track.h>
8 #include <ardour/tempo.h>
9 #include <ardour/audiosource.h>
10 #include <ardour/audioregion.h>
11 #include <pbd/error.h>
12 #include <pbd/id.h>
13 #include <pbd/statefuldestructible.h>
14 #include <pbd/failed_constructor.h>
15
16 using namespace PBD;
17 using namespace ARDOUR;
18
19 #include "i18n.h"
20
21 void Session::register_with_memento_command_factory(PBD::ID id, PBD::StatefulThingWithGoingAway *ptr)
22 {
23     registry[id] = ptr;
24 }
25     
26 Command *
27 Session::memento_command_factory(XMLNode *n)
28 {
29     PBD::ID id;
30     XMLNode *before = 0, *after = 0;
31     XMLNode *child = 0;
32
33     /* get id */
34     id = PBD::ID(n->property("obj_id")->value());
35
36     /* get before/after */
37
38     if (n->name() == "MementoCommand") {
39             before = new XMLNode(*n->children().front());
40             after = new XMLNode(*n->children().back());
41             child = before;
42     } else if (n->name() == "MementoUndoCommand") {
43             before = new XMLNode(*n->children().front());
44             child = before;
45     } else if (n->name() == "MementoRedoCommand") {
46             after = new XMLNode(*n->children().front());
47             child = after;
48     } else if (n->name() == "PlaylistCommand") {
49             before = new XMLNode(*n->children().front());
50             after = new XMLNode(*n->children().back());
51             child = before;
52     }
53                     
54     if (!child)
55     {
56         error << _("Tried to reconstitute a MementoCommand with no contents, failing. id=") << id.to_s() << endmsg;
57         return 0;
58     }
59
60     /* create command */
61     string obj_T = n->property ("type_name")->value();
62     if (obj_T == typeid (AudioRegion).name() || obj_T == typeid (Region).name()) {
63             if (audio_regions.count(id))
64                     return new MementoCommand<AudioRegion>(*audio_regions[id], before, after);
65     } else if (obj_T == typeid (AudioSource).name()) {
66             if (audio_sources.count(id))
67                     return new MementoCommand<AudioSource>(*audio_sources[id], before, after);
68     } else if (obj_T == typeid (Location).name()) {
69             return new MementoCommand<Location>(*_locations.get_location_by_id(id), before, after);
70     } else if (obj_T == typeid (Locations).name()) {
71             return new MementoCommand<Locations>(_locations, before, after);
72     } else if (obj_T == typeid (TempoMap).name()) {
73             return new MementoCommand<TempoMap>(*_tempo_map, before, after);
74     } else if (obj_T == typeid (Playlist).name() || obj_T == typeid (AudioPlaylist).name()) {
75             if (boost::shared_ptr<Playlist> pl = playlist_by_name(child->property("name")->value())) {
76                     return new MementoCommand<Playlist>(*(pl.get()), before, after);
77             }
78     } else if (obj_T == typeid (Route).name() || obj_T == typeid (AudioTrack).name()) { 
79             return new MementoCommand<Route>(*route_by_id(id), before, after);
80     } else if (obj_T == typeid (Curve).name() || obj_T == typeid (AutomationList).name()) {
81             if (automation_lists.count(id))
82                     return new MementoCommand<AutomationList>(*automation_lists[id], before, after);
83     } else if (registry.count(id)) { // For Editor and AutomationLine which are off-limits here
84             return new MementoCommand<PBD::StatefulThingWithGoingAway>(*registry[id], before, after);
85     }
86
87     /* we failed */
88     error << string_compose (_("could not reconstitute MementoCommand from XMLNode. object type = %1 id = %2"), obj_T, id.to_s()) << endmsg;
89     return 0 ;
90 }
91
92 Command *
93 Session::global_state_command_factory (const XMLNode& node)
94 {
95         const XMLProperty* prop;
96         Command* command = 0;
97
98         if ((prop = node.property ("type")) == 0) {
99                 error << _("GlobalRouteStateCommand has no \"type\" node, ignoring") << endmsg;
100                 return 0;
101         }
102         
103         try {
104
105                 if (prop->value() == "solo") {
106                         command = new GlobalSoloStateCommand (*this, node);
107                 } else if (prop->value() == "mute") {
108                         command = new GlobalMuteStateCommand (*this, node);
109                 } else if (prop->value() == "rec-enable") {
110                         command = new GlobalRecordEnableStateCommand (*this, node);
111                 } else if (prop->value() == "metering") {
112                         command = new GlobalMeteringStateCommand (*this, node);
113                 } else {
114                         error << string_compose (_("unknown type of GlobalRouteStateCommand (%1), ignored"), prop->value()) << endmsg;
115                 }
116         }
117
118         catch (failed_constructor& err) {
119                 return 0;
120         }
121
122         return command;
123 }
124
125 Session::GlobalRouteStateCommand::GlobalRouteStateCommand (Session& s, void* p)
126         : sess (s), src (p)
127 {
128 }
129
130 Session::GlobalRouteStateCommand::GlobalRouteStateCommand (Session& s, const XMLNode& node)
131         : sess (s), src (this)
132 {
133         if (set_state (node)) {
134                 throw failed_constructor ();
135         }
136 }
137
138 int
139 Session::GlobalRouteStateCommand::set_state (const XMLNode& node)
140 {
141         GlobalRouteBooleanState states;
142         XMLNodeList nlist;
143         const XMLProperty* prop;
144         XMLNode* child;
145         XMLNodeConstIterator niter;
146         int loop;
147
148         before.clear ();
149         after.clear ();
150         
151         for (loop = 0; loop < 2; ++loop) {
152
153                 const char *str;
154
155                 if (loop) {
156                         str = "after";
157                 } else {
158                         str = "before";
159                 }
160                 
161                 if ((child = node.child (str)) == 0) {
162                         warning << string_compose (_("global route state command has no \"%1\" node, ignoring entire command"), str) << endmsg;
163                         return -1;
164                 }
165
166                 nlist = child->children();
167
168                 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
169                         
170                         RouteBooleanState rbs;
171                         boost::shared_ptr<Route> route;
172                         ID id;
173                         
174                         prop = (*niter)->property ("id");
175                         id = prop->value ();
176                         
177                         if ((route = sess.route_by_id (id)) == 0) {
178                                 warning << string_compose (_("cannot find track/bus \"%1\" while rebuilding a global route state command, ignored"), id.to_s()) << endmsg;
179                                 continue;
180                         }
181                         
182                         rbs.first = boost::weak_ptr<Route> (route);
183                         
184                         prop = (*niter)->property ("yn");
185                         rbs.second = (prop->value() == "1");
186                         
187                         if (loop) {
188                                 after.push_back (rbs);
189                         } else {
190                                 before.push_back (rbs);
191                         }
192                 }
193         }
194
195         return 0;
196 }
197
198 XMLNode&
199 Session::GlobalRouteStateCommand::get_state ()
200 {
201         XMLNode* node = new XMLNode (X_("GlobalRouteStateCommand"));
202         XMLNode* nbefore = new XMLNode (X_("before"));
203         XMLNode* nafter = new XMLNode (X_("after"));
204
205         for (Session::GlobalRouteBooleanState::iterator x = before.begin(); x != before.end(); ++x) {
206                 XMLNode* child = new XMLNode ("s");
207                 boost::shared_ptr<Route> r = x->first.lock();
208
209                 if (r) {
210                         child->add_property (X_("id"), r->id().to_s());
211                         child->add_property (X_("yn"), (x->second ? "1" : "0"));
212                         nbefore->add_child_nocopy (*child);
213                 }
214         }
215
216         for (Session::GlobalRouteBooleanState::iterator x = after.begin(); x != after.end(); ++x) {
217                 XMLNode* child = new XMLNode ("s");
218                 boost::shared_ptr<Route> r = x->first.lock();
219
220                 if (r) {
221                         child->add_property (X_("id"), r->id().to_s());
222                         child->add_property (X_("yn"), (x->second ? "1" : "0"));
223                         nafter->add_child_nocopy (*child);
224                 }
225         }
226
227         node->add_child_nocopy (*nbefore);
228         node->add_child_nocopy (*nafter);
229
230         return *node;
231 }
232
233 // solo
234
235 Session::GlobalSoloStateCommand::GlobalSoloStateCommand(Session &sess, void *src)
236         : GlobalRouteStateCommand (sess, src)
237 {
238     after = before = sess.get_global_route_boolean(&Route::soloed);
239 }
240
241 Session::GlobalSoloStateCommand::GlobalSoloStateCommand (Session& sess, const XMLNode& node)
242         : Session::GlobalRouteStateCommand (sess, node)
243 {
244 }
245
246 void 
247 Session::GlobalSoloStateCommand::mark()
248 {
249     after = sess.get_global_route_boolean(&Route::soloed);
250 }
251
252 void 
253 Session::GlobalSoloStateCommand::operator()()
254 {
255     sess.set_global_solo(after, src);
256 }
257
258 void 
259 Session::GlobalSoloStateCommand::undo()
260 {
261     sess.set_global_solo(before, src);
262 }
263
264 XMLNode&
265 Session::GlobalSoloStateCommand::get_state()
266 {
267         XMLNode& node = GlobalRouteStateCommand::get_state();
268         node.add_property ("type", "solo");
269         return node;
270 }
271
272 // mute
273 Session::GlobalMuteStateCommand::GlobalMuteStateCommand(Session &sess, void *src)
274         : GlobalRouteStateCommand (sess, src)
275 {
276     after = before = sess.get_global_route_boolean(&Route::muted);
277 }
278
279 Session::GlobalMuteStateCommand::GlobalMuteStateCommand (Session& sess, const XMLNode& node)
280         : Session::GlobalRouteStateCommand (sess, node)
281 {
282 }
283
284 void 
285 Session::GlobalMuteStateCommand::mark()
286 {
287         after = sess.get_global_route_boolean(&Route::muted);
288 }
289
290 void 
291 Session::GlobalMuteStateCommand::operator()()
292 {
293         sess.set_global_mute(after, src);
294 }
295
296 void 
297 Session::GlobalMuteStateCommand::undo()
298 {
299         sess.set_global_mute(before, src);
300 }
301
302 XMLNode&
303 Session::GlobalMuteStateCommand::get_state()
304 {
305         XMLNode& node = GlobalRouteStateCommand::get_state();
306         node.add_property ("type", "mute");
307         return node;
308 }
309
310 // record enable
311 Session::GlobalRecordEnableStateCommand::GlobalRecordEnableStateCommand(Session &sess, void *src) 
312         : GlobalRouteStateCommand (sess, src)
313 {
314         after = before = sess.get_global_route_boolean(&Route::record_enabled);
315 }
316
317 Session::GlobalRecordEnableStateCommand::GlobalRecordEnableStateCommand (Session& sess, const XMLNode& node)
318         : Session::GlobalRouteStateCommand (sess, node)
319 {
320 }
321
322 void 
323 Session::GlobalRecordEnableStateCommand::mark()
324 {
325         after = sess.get_global_route_boolean(&Route::record_enabled);
326 }
327
328 void 
329 Session::GlobalRecordEnableStateCommand::operator()()
330 {
331         sess.set_global_record_enable(after, src);
332 }
333
334 void 
335 Session::GlobalRecordEnableStateCommand::undo()
336 {
337         sess.set_global_record_enable(before, src);
338 }
339
340 XMLNode& 
341 Session::GlobalRecordEnableStateCommand::get_state()
342 {
343         XMLNode& node = GlobalRouteStateCommand::get_state();
344         node.add_property ("type", "rec-enable");
345         return node;
346 }
347
348 // metering
349 Session::GlobalMeteringStateCommand::GlobalMeteringStateCommand(Session &s, void *p) 
350         : sess (s), src (p)
351 {
352         after = before = sess.get_global_route_metering();
353 }
354
355 Session::GlobalMeteringStateCommand::GlobalMeteringStateCommand (Session& s, const XMLNode& node)
356         : sess (s), src (this)
357 {
358         if (set_state (node)) {
359                 throw failed_constructor();
360         }
361 }
362
363 void 
364 Session::GlobalMeteringStateCommand::mark()
365 {
366         after = sess.get_global_route_metering();
367 }
368
369 void 
370 Session::GlobalMeteringStateCommand::operator()()
371 {
372         sess.set_global_route_metering(after, src);
373 }
374
375 void 
376 Session::GlobalMeteringStateCommand::undo()
377 {
378         sess.set_global_route_metering(before, src);
379 }
380
381 XMLNode&
382 Session::GlobalMeteringStateCommand::get_state()
383 {
384         XMLNode* node = new XMLNode (X_("GlobalRouteStateCommand"));
385         XMLNode* nbefore = new XMLNode (X_("before"));
386         XMLNode* nafter = new XMLNode (X_("after"));
387
388         for (Session::GlobalRouteMeterState::iterator x = before.begin(); x != before.end(); ++x) {
389                 XMLNode* child = new XMLNode ("s");
390                 boost::shared_ptr<Route> r = x->first.lock();
391
392                 if (r) {
393                         child->add_property (X_("id"), r->id().to_s());
394
395                         const char* meterstr = 0;
396                         
397                         switch (x->second) {
398                         case MeterInput:
399                                 meterstr = X_("input");
400                                 break;
401                         case MeterPreFader:
402                                 meterstr = X_("pre");
403                                 break;
404                         case MeterPostFader:
405                                 meterstr = X_("post");
406                                 break;
407                         default:
408                                 fatal << string_compose (_("programming error: %1") , "no meter state in Session::GlobalMeteringStateCommand::get_state") << endmsg;
409                         }
410
411                         child->add_property (X_("meter"), meterstr);
412                         nbefore->add_child_nocopy (*child);
413                 }
414         }
415
416         for (Session::GlobalRouteMeterState::iterator x = after.begin(); x != after.end(); ++x) {
417                 XMLNode* child = new XMLNode ("s");
418                 boost::shared_ptr<Route> r = x->first.lock();
419
420                 if (r) {
421                         child->add_property (X_("id"), r->id().to_s());
422
423                         const char* meterstr;
424                         
425                         switch (x->second) {
426                         case MeterInput:
427                                 meterstr = X_("input");
428                                 break;
429                         case MeterPreFader:
430                                 meterstr = X_("pre");
431                                 break;
432                         case MeterPostFader:
433                                 meterstr = X_("post");
434                                 break;
435                         default: meterstr = "";
436                         }
437
438                         child->add_property (X_("meter"), meterstr);
439                         nafter->add_child_nocopy (*child);
440                 }
441         }
442
443         node->add_child_nocopy (*nbefore);
444         node->add_child_nocopy (*nafter);
445
446         node->add_property ("type", "metering");
447
448         return *node;
449 }
450
451 int
452 Session::GlobalMeteringStateCommand::set_state (const XMLNode& node)
453 {
454         GlobalRouteBooleanState states;
455         XMLNodeList nlist;
456         const XMLProperty* prop;
457         XMLNode* child;
458         XMLNodeConstIterator niter;
459         int loop;
460
461         before.clear ();
462         after.clear ();
463         
464         for (loop = 0; loop < 2; ++loop) {
465
466                 const char *str;
467
468                 if (loop) {
469                         str = "after";
470                 } else {
471                         str = "before";
472                 }
473                 
474                 if ((child = node.child (str)) == 0) {
475                         warning << string_compose (_("global route meter state command has no \"%1\" node, ignoring entire command"), str) << endmsg;
476                         return -1;
477                 }
478
479                 nlist = child->children();
480
481                 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
482                         
483                         RouteMeterState rms;
484                         boost::shared_ptr<Route> route;
485                         ID id;
486                         
487                         prop = (*niter)->property ("id");
488                         id = prop->value ();
489                         
490                         if ((route = sess.route_by_id (id)) == 0) {
491                                 warning << string_compose (_("cannot find track/bus \"%1\" while rebuilding a global route state command, ignored"), id.to_s()) << endmsg;
492                                 continue;
493                         }
494                         
495                         rms.first = boost::weak_ptr<Route> (route);
496                         
497                         prop = (*niter)->property ("meter");
498
499                         if (prop->value() == X_("pre")) {
500                                 rms.second = MeterPreFader;
501                         } else if (prop->value() == X_("post")) {
502                                 rms.second = MeterPostFader;
503                         } else {
504                                 rms.second = MeterInput;
505                         }
506                         
507                         if (loop) {
508                                 after.push_back (rms);
509                         } else {
510                                 before.push_back (rms);
511                         }
512                 }
513         }
514
515         return 0;
516 }