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