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