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