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