Set up region BBT time when frame position changes, if the region is glued to BBT...
[ardour.git] / libs / ardour / session_state.cc
index 0973e294c5f5c438797d45f260bd026cba9e11e5..7d6f44b7861d2566c1974b48c7e4cc76ca5ad7f0 100644 (file)
@@ -28,8 +28,6 @@
 #include <fstream>
 #include <string>
 #include <cerrno>
-
-
 #include <cstdio> /* snprintf(3) ... grrr */
 #include <cmath>
 #include <unistd.h>
@@ -53,6 +51,8 @@
 #include <glibmm.h>
 #include <glibmm/thread.h>
 
+#include <boost/algorithm/string.hpp>
+
 #include "midi++/mmc.h"
 #include "midi++/port.h"
 #include "midi++/manager.h"
@@ -90,6 +90,7 @@
 #include "ardour/io_processor.h"
 #include "ardour/location.h"
 #include "ardour/midi_diskstream.h"
+#include "ardour/midi_model.h"
 #include "ardour/midi_patch_manager.h"
 #include "ardour/midi_playlist.h"
 #include "ardour/midi_region.h"
 #include "ardour/processor.h"
 #include "ardour/port.h"
 #include "ardour/proxy_controllable.h"
+#include "ardour/recent_sessions.h"
 #include "ardour/region_factory.h"
 #include "ardour/route_group.h"
 #include "ardour/send.h"
 #include "ardour/sndfile_helpers.h"
 #include "ardour/sndfilesource.h"
 #include "ardour/source_factory.h"
+#include "ardour/speakers.h"
 #include "ardour/template_utils.h"
 #include "ardour/tempo.h"
 #include "ardour/ticker.h"
@@ -133,7 +136,7 @@ using namespace std;
 using namespace ARDOUR;
 using namespace PBD;
 
-
+/** @param snapshot_name Snapshot name, without the .ardour prefix */
 void
 Session::first_stage_init (string fullpath, string snapshot_name)
 {
@@ -499,9 +502,11 @@ Session::ensure_subdirs ()
        return 0;
 }
 
-/** Caller must not hold process lock */
+/** @param session_template directory containing session template, or empty.
+ *  Caller must not hold process lock.
+ */
 int
-Session::create (const string& mix_template, BusProfile* bus_profile)
+Session::create (const string& session_template, BusProfile* bus_profile)
 {
        if (g_mkdir_with_parents (_path.c_str(), 0755) < 0) {
                error << string_compose(_("Session: cannot create session folder \"%1\" (%2)"), _path, strerror (errno)) << endmsg;
@@ -514,8 +519,8 @@ Session::create (const string& mix_template, BusProfile* bus_profile)
 
        _writable = exists_and_writable (sys::path (_path));
 
-       if (!mix_template.empty()) {
-               std::string in_path = mix_template;
+       if (!session_template.empty()) {
+               std::string in_path = session_template_dir_to_file (session_template);
 
                ifstream in(in_path.c_str());
 
@@ -529,16 +534,22 @@ Session::create (const string& mix_template, BusProfile* bus_profile)
                        if (out) {
                                out << in.rdbuf();
                                 _is_new = false;
+
+                               /* Copy plugin state files from template to new session */
+                               sys::path template_plugins = session_template;
+                               template_plugins /= X_("plugins");
+                               sys::copy_files (template_plugins, plugins_dir ());
+                               
                                return 0;
 
                        } else {
-                               error << string_compose (_("Could not open %1 for writing mix template"), out_path)
+                               error << string_compose (_("Could not open %1 for writing session template"), out_path)
                                        << endmsg;
                                return -1;
                        }
 
                } else {
-                       error << string_compose (_("Could not open mix template %1 for reading"), in_path)
+                       error << string_compose (_("Could not open session template %1 for reading"), in_path)
                                << endmsg;
                        return -1;
                }
@@ -567,7 +578,7 @@ Session::create (const string& mix_template, BusProfile* bus_profile)
                                 return -1;
                         }
 #ifdef BOOST_SP_ENABLE_DEBUG_HOOKS
-                       boost_debug_shared_ptr_mark_interesting (r.get(), "Route");
+                       // boost_debug_shared_ptr_mark_interesting (r.get(), "Route");
 #endif
                        {
                                Glib::Mutex::Lock lm (AudioEngine::instance()->process_lock ());
@@ -584,7 +595,7 @@ Session::create (const string& mix_template, BusProfile* bus_profile)
                                         return -1;
                                 }
 #ifdef BOOST_SP_ENABLE_DEBUG_HOOKS
-                                boost_debug_shared_ptr_mark_interesting (r.get(), "Route");
+                                // boost_debug_shared_ptr_mark_interesting (r.get(), "Route");
 #endif
                                {
                                        Glib::Mutex::Lock lm (AudioEngine::instance()->process_lock ());
@@ -1270,6 +1281,24 @@ Session::set_state (const XMLNode& node, int version)
                }
        }
 
+        if ((child = find_named_node (node, X_("Speakers"))) != 0) {
+                _speakers->set_state (*child, version);
+        }
+
+       if ((child = find_named_node (node, "Sources")) == 0) {
+               error << _("Session: XML state has no sources section") << endmsg;
+               goto out;
+       } else if (load_sources (*child)) {
+               goto out;
+       }
+
+       if ((child = find_named_node (node, "TempoMap")) == 0) {
+               error << _("Session: XML state has no Tempo Map section") << endmsg;
+               goto out;
+       } else if (_tempo_map->set_state (*child, version)) {
+               goto out;
+       }
+
        if ((child = find_named_node (node, "Locations")) == 0) {
                error << _("Session: XML state has no locations section") << endmsg;
                goto out;
@@ -1277,10 +1306,6 @@ Session::set_state (const XMLNode& node, int version)
                goto out;
        }
 
-        if ((child = find_named_node (node, X_("Speakers"))) != 0) {
-                _speakers->set_state (*child, version);
-        }
-
        Location* location;
 
        if ((location = _locations->auto_loop_location()) != 0) {
@@ -1300,20 +1325,6 @@ Session::set_state (const XMLNode& node, int version)
                AudioFileSource::set_header_position_offset (_session_range_location->start());
        }
 
-       if ((child = find_named_node (node, "Sources")) == 0) {
-               error << _("Session: XML state has no sources section") << endmsg;
-               goto out;
-       } else if (load_sources (*child)) {
-               goto out;
-       }
-
-       if ((child = find_named_node (node, "TempoMap")) == 0) {
-               error << _("Session: XML state has no Tempo Map section") << endmsg;
-               goto out;
-       } else if (_tempo_map->set_state (*child, version)) {
-               goto out;
-       }
-
        if ((child = find_named_node (node, "Regions")) == 0) {
                error << _("Session: XML state has no Regions section") << endmsg;
                goto out;
@@ -1497,7 +1508,7 @@ Session::XMLRouteFactory (const XMLNode& node, int version)
                 }
 
 #ifdef BOOST_SP_ENABLE_DEBUG_HOOKS
-                boost_debug_shared_ptr_mark_interesting (track.get(), "Track");
+                // boost_debug_shared_ptr_mark_interesting (track.get(), "Track");
 #endif
                 ret = track;
 
@@ -1506,7 +1517,7 @@ Session::XMLRouteFactory (const XMLNode& node, int version)
 
                 if (r->init () == 0 && r->set_state (node, version) == 0) {
 #ifdef BOOST_SP_ENABLE_DEBUG_HOOKS
-                        boost_debug_shared_ptr_mark_interesting (r.get(), "Route");
+                        // boost_debug_shared_ptr_mark_interesting (r.get(), "Route");
 #endif
                         ret = r;
                 }
@@ -1569,7 +1580,7 @@ Session::XMLRouteFactory_2X (const XMLNode& node, int version)
                track->set_diskstream (*i);
 
 #ifdef BOOST_SP_ENABLE_DEBUG_HOOKS
-                boost_debug_shared_ptr_mark_interesting (track.get(), "Track");
+                // boost_debug_shared_ptr_mark_interesting (track.get(), "Track");
 #endif
                 ret = track;
 
@@ -1578,7 +1589,7 @@ Session::XMLRouteFactory_2X (const XMLNode& node, int version)
 
                 if (r->init () == 0 && r->set_state (node, version) == 0) {
 #ifdef BOOST_SP_ENABLE_DEBUG_HOOKS
-                        boost_debug_shared_ptr_mark_interesting (r.get(), "Route");
+                        // boost_debug_shared_ptr_mark_interesting (r.get(), "Route");
 #endif
                         ret = r;
                 }
@@ -2038,66 +2049,43 @@ Session::save_template (string template_name)
        }
        catch(sys::filesystem_error& ex)
        {
-               error << string_compose(_("Could not create mix templates directory \"%1\" (%2)"),
+               error << string_compose(_("Could not create templates directory \"%1\" (%2)"),
                                user_template_dir.to_string(), ex.what()) << endmsg;
                return -1;
        }
 
        tree.set_root (&get_template());
 
-       sys::path template_file_path(user_template_dir);
-       template_file_path /= template_name + template_suffix;
-
-       if (sys::exists (template_file_path))
+       sys::path template_dir_path(user_template_dir);
+       
+       /* directory to put the template in */
+       template_dir_path /= template_name;
+       if (sys::exists (template_dir_path))
        {
                warning << string_compose(_("Template \"%1\" already exists - new version not created"),
-                               template_file_path.to_string()) << endmsg;
+                               template_dir_path.to_string()) << endmsg;
                return -1;
        }
+       
+       sys::create_directories (template_dir_path);
+
+       /* file to write */
+       sys::path template_file_path = template_dir_path;
+       template_file_path /= template_name + template_suffix;
 
        if (!tree.write (template_file_path.to_string())) {
                error << _("template not saved") << endmsg;
                return -1;
        }
 
-       return 0;
-}
-
-int
-Session::rename_template (string old_name, string new_name)
-{
-       sys::path old_path (user_template_directory());
-       old_path /= old_name + template_suffix;
-
-       sys::path new_path(user_template_directory());
-       new_path /= new_name + template_suffix;
+       /* copy plugin state directory */
 
-       if (sys::exists (new_path)) {
-               warning << string_compose(_("Template \"%1\" already exists - template not renamed"),
-                                         new_path.to_string()) << endmsg;
-               return -1;
-       }
-
-       try {
-               sys::rename (old_path, new_path);
-               return 0;
-       } catch (...) {
-               return -1;
-       }
-}
-
-int
-Session::delete_template (string name)
-{
-       sys::path path = user_template_directory();
-       path /= name + template_suffix;
+       sys::path template_plugin_state_path = template_dir_path;
+       template_plugin_state_path /= X_("plugins");
+       sys::create_directories (template_plugin_state_path);
+       sys::copy_files (plugins_dir(), template_plugin_state_path);
 
-       try {
-               sys::remove (path);
-               return 0;
-       } catch (...) {
-               return -1;
-       }
+       return 0;
 }
 
 void
@@ -2387,8 +2375,9 @@ Session::add_route_group (RouteGroup* g)
        _route_groups.push_back (g);
        route_group_added (g); /* EMIT SIGNAL */
 
-       g->MembershipChanged.connect_same_thread (*this, boost::bind (&Session::route_group_changed, this));
-       g->PropertyChanged.connect_same_thread (*this, boost::bind (&Session::route_group_changed, this));
+       g->RouteAdded.connect_same_thread (*this, boost::bind (&Session::route_added_to_route_group, this, _1, _2));
+       g->RouteRemoved.connect_same_thread (*this, boost::bind (&Session::route_removed_from_route_group, this, _1, _2));
+       g->PropertyChanged.connect_same_thread (*this, boost::bind (&Session::route_group_property_changed, this, g));
 
        set_dirty ();
 }
@@ -2659,16 +2648,10 @@ Session::cleanup_regions ()
 
        for (RegionFactory::RegionMap::const_iterator i = regions.begin(); i != regions.end(); ++i) {
 
-               boost::shared_ptr<AudioRegion> audio_region = boost::dynamic_pointer_cast<AudioRegion>( i->second);
+               uint32_t used = playlists->region_use_count (i->second);
 
-               if (!audio_region) {
-                       continue;
-               }
-
-               uint32_t used = playlists->region_use_count (audio_region);
-
-               if (used == 0 && !audio_region->automatic()) {
-                       RegionFactory::map_remove(i->second);
+               if (used == 0 && !i->second->automatic ()) {
+                       RegionFactory::map_remove (i->second);
                }
        }
 
@@ -3452,11 +3435,11 @@ Session::config_changed (std::string p, bool ours)
 
                //poke_midi_thread ();
 
-       } else if (p == "mmc-device-id" || p == "mmc-receive-id") {
+       } else if (p == "mmc-device-id" || p == "mmc-receive-id" || p == "mmc-receive-device-id") {
 
                MIDI::Manager::instance()->mmc()->set_receive_device_id (Config->get_mmc_receive_device_id());
 
-       } else if (p == "mmc-send-id") {
+       } else if (p == "mmc-send-id" || p == "mmc-send-device-id") {
 
                MIDI::Manager::instance()->mmc()->set_send_device_id (Config->get_mmc_send_device_id());
 
@@ -3666,6 +3649,8 @@ Session::rename (const std::string& new_name)
        string newstr;
        bool first = true;
 
+       string const old_sources_root = _session_dir->sources_root().to_string ();
+
 #define RENAME ::rename
 
        /* Rename:
@@ -3787,6 +3772,21 @@ Session::rename (const std::string& new_name)
                }
        }
 
+       /* update file source paths */
+       
+       for (SourceMap::iterator i = sources.begin(); i != sources.end(); ++i) {
+               boost::shared_ptr<FileSource> fs = boost::dynamic_pointer_cast<FileSource> (i->second);
+               if (fs) {
+                       string p = fs->path ();
+                       boost::replace_all (p, old_sources_root, _session_dir->sources_root().to_string ());
+                       fs->set_path (p);
+               }
+       }
+
+       /* remove old name from recent sessions */
+
+       remove_recent_sessions (_path);
+
        _path = newpath;
        _current_snapshot_name = new_name;
        _name = new_name;
@@ -3797,6 +3797,11 @@ Session::rename (const std::string& new_name)
 
        save_state (_current_snapshot_name);
 
+
+       /* add to recent sessions */
+
+       store_recent_sessions (new_name, _path);
+
        return 0;
 
 #undef RENAME