volatile in stupid
[ardour.git] / libs / ardour / session_state.cc
index 604fc4bc52b07804ca9645a6bf7c168537ecc590..b6569ba00edbc36765ff5a6d3f4980fdc5e93d41 100644 (file)
@@ -28,8 +28,6 @@
 #include <fstream>
 #include <string>
 #include <cerrno>
-
-
 #include <cstdio> /* snprintf(3) ... grrr */
 #include <cmath>
 #include <unistd.h>
 #include <sys/mount.h>
 #endif
 
+#ifdef HAVE_SYS_STATVFS_H
+#include <sys/statvfs.h>
+#endif
+
+#include <glib.h>
+#include <glib/gstdio.h>
+
 #include <glibmm.h>
-#include <glibmm/thread.h>
+#include <glibmm/threads.h>
+
+#include <boost/algorithm/string.hpp>
 
 #include "midi++/mmc.h"
 #include "midi++/port.h"
 #include "midi++/manager.h"
 
+#include "evoral/SMF.hpp"
+
 #include "pbd/boost_debug.h"
 #include "pbd/basename.h"
 #include "pbd/controllable_descriptor.h"
 #include "pbd/enumwriter.h"
 #include "pbd/error.h"
+#include "pbd/file_utils.h"
 #include "pbd/pathscanner.h"
 #include "pbd/pthread_utils.h"
-#include "pbd/search_path.h"
 #include "pbd/stacktrace.h"
 #include "pbd/convert.h"
 #include "pbd/clear_dir.h"
 
 #include "ardour/amp.h"
 #include "ardour/audio_diskstream.h"
-#include "ardour/audio_playlist_source.h"
 #include "ardour/audio_track.h"
 #include "ardour/audioengine.h"
 #include "ardour/audiofilesource.h"
-#include "ardour/audioplaylist.h"
 #include "ardour/audioregion.h"
-#include "ardour/auditioner.h"
 #include "ardour/automation_control.h"
-#include "ardour/buffer.h"
 #include "ardour/butler.h"
-#include "ardour/configuration.h"
 #include "ardour/control_protocol_manager.h"
-#include "ardour/crossfade.h"
-#include "ardour/cycle_timer.h"
 #include "ardour/directory_names.h"
 #include "ardour/filename_extensions.h"
-#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/midi_source.h"
 #include "ardour/midi_track.h"
-#include "ardour/named_selection.h"
 #include "ardour/pannable.h"
-#include "ardour/processor.h"
+#include "ardour/playlist_factory.h"
 #include "ardour/port.h"
+#include "ardour/processor.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/session.h"
 #include "ardour/session_directory.h"
 #include "ardour/session_metadata.h"
-#include "ardour/session_state_utils.h"
 #include "ardour/session_playlists.h"
-#include "ardour/session_utils.h"
+#include "ardour/session_state_utils.h"
 #include "ardour/silentfilesource.h"
-#include "ardour/slave.h"
-#include "ardour/smf_source.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"
 #include "ardour/user_bundle.h"
-#include "ardour/utils.h"
-#include "ardour/utils.h"
-#include "ardour/version.h"
-#include "ardour/playlist_factory.h"
 
 #include "control_protocol/control_protocol.h"
 
@@ -131,7 +125,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)
 {
@@ -174,6 +168,7 @@ Session::first_stage_init (string fullpath, string snapshot_name)
        _solo_isolated_cnt = 0;
        g_atomic_int_set (&processing_prohibited, 0);
        _transport_speed = 0;
+       _default_transport_speed = 1.0;
        _last_transport_speed = 0;
        _target_transport_speed = 0;
        auto_play_legal = false;
@@ -211,6 +206,7 @@ Session::first_stage_init (string fullpath, string snapshot_name)
        _play_range = false;
        _exporting = false;
        pending_abort = false;
+       _adding_routes_in_progress = false;
        destructive_index = 0;
        first_file_data_format_reset = true;
        first_file_header_format_reset = true;
@@ -219,13 +215,14 @@ Session::first_stage_init (string fullpath, string snapshot_name)
         _step_editors = 0;
         no_questions_about_missing_files = false;
         _speakers.reset (new Speakers);
+       _clicks_cleared = 0;
+       ignore_route_processor_changes = false;
+       _pre_export_mmc_enabled = false;
 
        AudioDiskstream::allocate_working_buffers();
-       AudioSource::allocate_working_buffers ();
 
        /* default short fade = 15ms */
 
-       Crossfade::set_short_xfade_length ((framecnt_t) floor (config.get_short_xfade_seconds() * frame_rate()));
        SndFileSource::setup_standard_crossfades (*this, frame_rate());
 
        last_mmc_step.tv_sec = 0;
@@ -291,7 +288,7 @@ Session::first_stage_init (string fullpath, string snapshot_name)
 int
 Session::second_stage_init ()
 {
-       AudioFileSource::set_peak_dir (_session_dir->peak_path().to_string());
+       AudioFileSource::set_peak_dir (_session_dir->peak_path());
 
        if (!_is_new) {
                if (load_state (_current_snapshot_name)) {
@@ -342,6 +339,9 @@ Session::second_stage_init ()
        _engine.Halted.connect_same_thread (*this, boost::bind (&Session::engine_halted, this));
        _engine.Xrun.connect_same_thread (*this, boost::bind (&Session::xrun_recovery, this));
 
+       midi_clock = new MidiClockTicker ();
+       midi_clock->set_session (this);
+
        try {
                when_engine_running ();
        }
@@ -365,9 +365,9 @@ Session::second_stage_init ()
        MIDI::Manager::instance()->mmc()->send (MIDI::MachineControlCommand (MIDI::MachineControl::cmdMmcReset));
        MIDI::Manager::instance()->mmc()->send (MIDI::MachineControlCommand (Timecode::Time ()));
 
-       MidiClockTicker::instance().set_session (this);
        MIDI::Name::MidiPatchManager::instance().set_session (this);
 
+       ltc_tx_initialize();
        /* initial program change will be delivered later; see ::config_changed() */
 
        _state_of_the_state = Clean;
@@ -393,7 +393,7 @@ Session::raid_path () const
        SearchPath raid_search_path;
 
        for (vector<space_and_path>::const_iterator i = session_dirs.begin(); i != session_dirs.end(); ++i) {
-               raid_search_path += sys::path((*i).path);
+               raid_search_path += (*i).path;
        }
 
        return raid_search_path.to_string ();
@@ -416,7 +416,7 @@ Session::setup_raid_path (string path)
        SearchPath midi_search_path;
 
        for (SearchPath::const_iterator i = search_path.begin(); i != search_path.end(); ++i) {
-               sp.path = (*i).to_string ();
+               sp.path = *i;
                sp.blocks = 0; // not needed
                session_dirs.push_back (sp);
 
@@ -434,7 +434,7 @@ bool
 Session::path_is_within_session (const std::string& path)
 {
        for (vector<space_and_path>::const_iterator i = session_dirs.begin(); i != session_dirs.end(); ++i) {
-               if (path.find ((*i).path) == 0) {
+               if (PBD::path_is_within (i->path, path)) {
                        return true;
                }
        }
@@ -446,35 +446,35 @@ Session::ensure_subdirs ()
 {
        string dir;
 
-       dir = session_directory().peak_path().to_string();
+       dir = session_directory().peak_path();
 
        if (g_mkdir_with_parents (dir.c_str(), 0755) < 0) {
                error << string_compose(_("Session: cannot create session peakfile folder \"%1\" (%2)"), dir, strerror (errno)) << endmsg;
                return -1;
        }
 
-       dir = session_directory().sound_path().to_string();
+       dir = session_directory().sound_path();
 
        if (g_mkdir_with_parents (dir.c_str(), 0755) < 0) {
                error << string_compose(_("Session: cannot create session sounds dir \"%1\" (%2)"), dir, strerror (errno)) << endmsg;
                return -1;
        }
 
-       dir = session_directory().midi_path().to_string();
+       dir = session_directory().midi_path();
 
        if (g_mkdir_with_parents (dir.c_str(), 0755) < 0) {
                error << string_compose(_("Session: cannot create session midi dir \"%1\" (%2)"), dir, strerror (errno)) << endmsg;
                return -1;
        }
 
-       dir = session_directory().dead_path().to_string();
+       dir = session_directory().dead_path();
 
        if (g_mkdir_with_parents (dir.c_str(), 0755) < 0) {
                error << string_compose(_("Session: cannot create session dead sounds folder \"%1\" (%2)"), dir, strerror (errno)) << endmsg;
                return -1;
        }
 
-       dir = session_directory().export_path().to_string();
+       dir = session_directory().export_path();
 
        if (g_mkdir_with_parents (dir.c_str(), 0755) < 0) {
                error << string_compose(_("Session: cannot create session export folder \"%1\" (%2)"), dir, strerror (errno)) << endmsg;
@@ -495,12 +495,21 @@ Session::ensure_subdirs ()
                return -1;
        }
 
+       dir = externals_dir ();
+
+       if (g_mkdir_with_parents (dir.c_str(), 0755) < 0) {
+               error << string_compose(_("Session: cannot create session externals folder \"%1\" (%2)"), dir, strerror (errno)) << endmsg;
+               return -1;
+       }
+
        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;
@@ -511,10 +520,10 @@ Session::create (const string& mix_template, BusProfile* bus_profile)
                return -1;
        }
 
-       _writable = exists_and_writable (sys::path (_path));
+       _writable = exists_and_writable (_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());
 
@@ -528,36 +537,36 @@ 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 */
+                               std::string template_plugins = Glib::build_filename (session_template, X_("plugins"));
+                               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;
                }
 
        }
 
-       /* Instantiate metadata */
-
-       _metadata = new SessionMetadata ();
-
        /* set initial start + end point */
 
        _state_of_the_state = Clean;
-        
+
         /* set up Master Out and Control Out if necessary */
 
         if (bus_profile) {
 
                RouteList rl;
-               int control_id = 1;
                 ChanCount count(DataType::AUDIO, bus_profile->master_out_channels);
 
                if (bus_profile->master_out_channels) {
@@ -566,42 +575,23 @@ Session::create (const string& mix_template, BusProfile* bus_profile)
                                 return -1;
                         }
 #ifdef BOOST_SP_ENABLE_DEBUG_HOOKS
-                       boost_debug_shared_ptr_mark_interesting (rt.get(), "Route");
+                       // boost_debug_shared_ptr_mark_interesting (r.get(), "Route");
 #endif
                        {
-                               Glib::Mutex::Lock lm (AudioEngine::instance()->process_lock ());
+                               Glib::Threads::Mutex::Lock lm (AudioEngine::instance()->process_lock ());
                                r->input()->ensure_io (count, false, this);
                                r->output()->ensure_io (count, false, this);
                        }
-                       r->set_remote_control_id (control_id++);
 
                        rl.push_back (r);
 
-                        if (Config->get_use_monitor_bus()) {
-                               boost::shared_ptr<Route> r (new Route (*this, _("monitor"), Route::MonitorOut, DataType::AUDIO));
-                                if (r->init ()) {
-                                        return -1;
-                                }
-#ifdef BOOST_SP_ENABLE_DEBUG_HOOKS
-                                boost_debug_shared_ptr_mark_interesting (rt, "Route");
-#endif
-                               {
-                                       Glib::Mutex::Lock lm (AudioEngine::instance()->process_lock ());
-                                       r->input()->ensure_io (count, false, this);
-                                       r->output()->ensure_io (count, false, this);
-                               }
-                                r->set_remote_control_id (control_id);
-                                
-                                rl.push_back (r);
-                        }
-
                } else {
                        /* prohibit auto-connect to master, because there isn't one */
                        bus_profile->output_ac = AutoConnectOption (bus_profile->output_ac & ~AutoConnectMaster);
                }
 
                if (!rl.empty()) {
-                       add_routes (rl, false, false);
+                       add_routes (rl, false, false, false);
                }
 
                 /* this allows the user to override settings with an environment variable.
@@ -611,11 +601,15 @@ Session::create (const string& mix_template, BusProfile* bus_profile)
                         bus_profile->input_ac = AutoConnectOption (0);
                         bus_profile->output_ac = AutoConnectOption (0);
                 }
-                
+
                 Config->set_input_auto_connect (bus_profile->input_ac);
                 Config->set_output_auto_connect (bus_profile->output_ac);
         }
 
+       if (Config->get_use_monitor_bus() && bus_profile) {
+               add_monitor_section ();
+       }
+
        save_state ("");
 
        return 0;
@@ -632,23 +626,21 @@ Session::maybe_write_autosave()
 void
 Session::remove_pending_capture_state ()
 {
-       sys::path pending_state_file_path(_session_dir->root_path());
+       std::string pending_state_file_path(_session_dir->root_path());
 
-       pending_state_file_path /= legalize_for_path (_current_snapshot_name) + pending_suffix;
+       pending_state_file_path = Glib::build_filename (pending_state_file_path, legalize_for_path (_current_snapshot_name) + pending_suffix);
 
-       try
-       {
-               sys::remove (pending_state_file_path);
-       }
-       catch(sys::filesystem_error& ex)
-       {
-               error << string_compose(_("Could remove pending capture state at path \"%1\" (%2)"),
-                               pending_state_file_path.to_string(), ex.what()) << endmsg;
+       if (!Glib::file_test (pending_state_file_path, Glib::FILE_TEST_EXISTS)) return;
+
+       if (g_remove (pending_state_file_path.c_str()) != 0) {
+               error << string_compose(_("Could not remove pending capture state at path \"%1\" (%2)"),
+                               pending_state_file_path, g_strerror (errno)) << endmsg;
        }
 }
 
 /** Rename a state file.
- * @param snapshot_name Snapshot name.
+ *  @param old_name Old snapshot name.
+ *  @param new_name New snapshot name.
  */
 void
 Session::rename_state (string old_name, string new_name)
@@ -661,34 +653,29 @@ Session::rename_state (string old_name, string new_name)
        const string old_xml_filename = legalize_for_path (old_name) + statefile_suffix;
        const string new_xml_filename = legalize_for_path (new_name) + statefile_suffix;
 
-       const sys::path old_xml_path = _session_dir->root_path() / old_xml_filename;
-       const sys::path new_xml_path = _session_dir->root_path() / new_xml_filename;
+       const std::string old_xml_path(Glib::build_filename (_session_dir->root_path(), old_xml_filename));
+       const std::string new_xml_path(Glib::build_filename (_session_dir->root_path(), new_xml_filename));
 
-       try
-       {
-               sys::rename (old_xml_path, new_xml_path);
-       }
-       catch (const sys::filesystem_error& err)
-       {
+       if (::g_rename (old_xml_path.c_str(), new_xml_path.c_str()) != 0) {
                error << string_compose(_("could not rename snapshot %1 to %2 (%3)"),
-                               old_name, new_name, err.what()) << endmsg;
+                               old_name, new_name, g_strerror(errno)) << endmsg;
        }
 }
 
 /** Remove a state file.
- * @param snapshot_name Snapshot name.
+ *  @param snapshot_name Snapshot name.
  */
 void
 Session::remove_state (string snapshot_name)
 {
-       if (snapshot_name == _current_snapshot_name || snapshot_name == _name) {
+       if (!_writable || snapshot_name == _current_snapshot_name || snapshot_name == _name) {
                // refuse to remove the current snapshot or the "main" one
                return;
        }
 
-       sys::path xml_path(_session_dir->root_path());
+       std::string xml_path(_session_dir->root_path());
 
-       xml_path /= legalize_for_path (snapshot_name) + statefile_suffix;
+       xml_path = Glib::build_filename (xml_path, legalize_for_path (snapshot_name) + statefile_suffix);
 
        if (!create_backup_file (xml_path)) {
                // don't remove it if a backup can't be made
@@ -697,7 +684,10 @@ Session::remove_state (string snapshot_name)
        }
 
        // and delete it
-       sys::remove (xml_path);
+       if (g_remove (xml_path.c_str()) != 0) {
+               error << string_compose(_("Could not remove session file at path \"%1\" (%2)"),
+                               xml_path, g_strerror (errno)) << endmsg;
+       }
 }
 
 #ifdef HAVE_JACK_SESSION
@@ -715,7 +705,7 @@ Session::jack_session_event (jack_session_event_t * event)
         if (event->type == JackSessionSaveTemplate)
         {
                 if (save_template( timebuf )) {
-                        event->flags = JackSessionSaveError; 
+                        event->flags = JackSessionSaveError;
                 } else {
                         string cmd ("ardour3 -P -U ");
                         cmd += event->client_uuid;
@@ -728,15 +718,16 @@ Session::jack_session_event (jack_session_event_t * event)
         else
         {
                 if (save_state (timebuf)) {
-                        event->flags = JackSessionSaveError; 
+                        event->flags = JackSessionSaveError;
                 } else {
-                        sys::path xml_path (_session_dir->root_path());
-                        xml_path /= legalize_for_path (timebuf) + statefile_suffix;
+                       std::string xml_path (_session_dir->root_path());
+                       std::string legalized_filename = legalize_for_path (timebuf) + statefile_suffix;
+                       xml_path = Glib::build_filename (xml_path, legalized_filename);
 
                         string cmd ("ardour3 -P -U ");
                         cmd += event->client_uuid;
                         cmd += " \"";
-                        cmd += xml_path.to_string();
+                        cmd += xml_path;
                         cmd += '\"';
 
                         event->command_line = strdup (cmd.c_str());
@@ -758,7 +749,7 @@ int
 Session::save_state (string snapshot_name, bool pending, bool switch_to_snapshot)
 {
        XMLTree tree;
-       sys::path xml_path(_session_dir->root_path());
+       std::string xml_path(_session_dir->root_path());
 
        if (!_writable || (_state_of_the_state & CannotSave)) {
                return 1;
@@ -774,8 +765,12 @@ Session::save_state (string snapshot_name, bool pending, bool switch_to_snapshot
        /* tell sources we're saving first, in case they write out to a new file
         * which should be saved with the state rather than the old one */
        for (SourceMap::const_iterator i = sources.begin(); i != sources.end(); ++i) {
-               i->second->session_saved();
-        }
+               try {
+                       i->second->session_saved();
+               } catch (Evoral::SMF::FileError& e) {
+                       error << string_compose ("Could not write to MIDI file %1; MIDI data not saved.", e.file_name ()) << endmsg;
+               }
+       }
 
        tree.set_root (&get_state());
 
@@ -789,11 +784,11 @@ Session::save_state (string snapshot_name, bool pending, bool switch_to_snapshot
 
                /* proper save: use statefile_suffix (.ardour in English) */
 
-               xml_path /= legalize_for_path (snapshot_name) + statefile_suffix;
+               xml_path = Glib::build_filename (xml_path, legalize_for_path (snapshot_name) + statefile_suffix);
 
                /* make a backup copy of the old file */
 
-               if (sys::exists(xml_path) && !create_backup_file (xml_path)) {
+               if (Glib::file_test (xml_path, Glib::FILE_TEST_EXISTS) && !create_backup_file (xml_path)) {
                        // create_backup_file will log the error
                        return -1;
                }
@@ -801,26 +796,31 @@ Session::save_state (string snapshot_name, bool pending, bool switch_to_snapshot
        } else {
 
                /* pending save: use pending_suffix (.pending in English) */
-               xml_path /= legalize_for_path (snapshot_name) + pending_suffix;
+               xml_path = Glib::build_filename (xml_path, legalize_for_path (snapshot_name) + pending_suffix);
        }
 
-       sys::path tmp_path(_session_dir->root_path());
+       std::string tmp_path(_session_dir->root_path());
+       tmp_path = Glib::build_filename (tmp_path, legalize_for_path (snapshot_name) + temp_suffix);
 
-       tmp_path /= legalize_for_path (snapshot_name) + temp_suffix;
+       // cerr << "actually writing state to " << xml_path << endl;
 
-       // cerr << "actually writing state to " << xml_path.to_string() << endl;
-
-       if (!tree.write (tmp_path.to_string())) {
-               error << string_compose (_("state could not be saved to %1"), tmp_path.to_string()) << endmsg;
-               sys::remove (tmp_path);
+       if (!tree.write (tmp_path)) {
+               error << string_compose (_("state could not be saved to %1"), tmp_path) << endmsg;
+               if (g_remove (tmp_path.c_str()) != 0) {
+                       error << string_compose(_("Could not remove temporary session file at path \"%1\" (%2)"),
+                                       tmp_path, g_strerror (errno)) << endmsg;
+               }
                return -1;
 
        } else {
 
-               if (rename (tmp_path.to_string().c_str(), xml_path.to_string().c_str()) != 0) {
+               if (::rename (tmp_path.c_str(), xml_path.c_str()) != 0) {
                        error << string_compose (_("could not rename temporary session file %1 to %2"),
-                                       tmp_path.to_string(), xml_path.to_string()) << endmsg;
-                       sys::remove (tmp_path);
+                                       tmp_path, xml_path) << endmsg;
+                       if (g_remove (tmp_path.c_str()) != 0) {
+                               error << string_compose(_("Could not remove temporary session file at path \"%1\" (%2)"),
+                                               tmp_path, g_strerror (errno)) << endmsg;
+                       }
                        return -1;
                }
        }
@@ -863,10 +863,10 @@ Session::load_state (string snapshot_name)
 
        /* check for leftover pending state from a crashed capture attempt */
 
-       sys::path xmlpath(_session_dir->root_path());
-       xmlpath /= legalize_for_path (snapshot_name) + pending_suffix;
+       std::string xmlpath(_session_dir->root_path());
+       xmlpath = Glib::build_filename (xmlpath, legalize_for_path (snapshot_name) + pending_suffix);
 
-       if (sys::exists (xmlpath)) {
+       if (Glib::file_test (xmlpath, Glib::FILE_TEST_EXISTS)) {
 
                /* there is pending state from a crashed capture attempt */
 
@@ -877,15 +877,13 @@ Session::load_state (string snapshot_name)
        }
 
        if (!state_was_pending) {
-               xmlpath = _session_dir->root_path();
-               xmlpath /= snapshot_name;
+               xmlpath = Glib::build_filename (_session_dir->root_path(), snapshot_name);
        }
 
-        if (!sys::exists (xmlpath)) {
-                xmlpath = _session_dir->root_path();
-                xmlpath /= legalize_for_path (snapshot_name) + statefile_suffix;
-                if (!sys::exists (xmlpath)) {
-                        error << string_compose(_("%1: session state information file \"%2\" doesn't exist!"), _name, xmlpath.to_string()) << endmsg;
+       if (!Glib::file_test (xmlpath, Glib::FILE_TEST_EXISTS)) {
+               xmlpath = Glib::build_filename (_session_dir->root_path(), legalize_for_path (snapshot_name) + statefile_suffix);
+               if (!Glib::file_test (xmlpath, Glib::FILE_TEST_EXISTS)) {
+                        error << string_compose(_("%1: session file \"%2\" doesn't exist!"), _name, xmlpath) << endmsg;
                         return 1;
                 }
         }
@@ -896,8 +894,8 @@ Session::load_state (string snapshot_name)
 
        _writable = exists_and_writable (xmlpath);
 
-       if (!state_tree->read (xmlpath.to_string())) {
-               error << string_compose(_("Could not understand ardour file %1"), xmlpath.to_string()) << endmsg;
+       if (!state_tree->read (xmlpath)) {
+               error << string_compose(_("Could not understand session file %1"), xmlpath) << endmsg;
                delete state_tree;
                state_tree = 0;
                return -1;
@@ -906,7 +904,7 @@ Session::load_state (string snapshot_name)
        XMLNode& root (*state_tree->root());
 
        if (root.name() != X_("Session")) {
-               error << string_compose (_("Session file %1 is not a session"), xmlpath.to_string()) << endmsg;
+               error << string_compose (_("Session file %1 is not a session"), xmlpath) << endmsg;
                delete state_tree;
                state_tree = 0;
                return -1;
@@ -918,39 +916,35 @@ Session::load_state (string snapshot_name)
                /* no version implies very old version of Ardour */
                Stateful::loading_state_version = 1000;
        } else {
-               int major;
-               int minor;
-               int micro;
-
-               sscanf (prop->value().c_str(), "%d.%d.%d", &major, &minor, &micro);
-               Stateful::loading_state_version = (major * 1000) + minor;
+               if (prop->value().find ('.') != string::npos) {
+                       /* old school version format */
+                       if (prop->value()[0] == '2') {
+                               Stateful::loading_state_version = 2000;
+                       } else {
+                               Stateful::loading_state_version = 3000;
+                       }
+               } else {
+                       Stateful::loading_state_version = atoi (prop->value());
+               }
        }
-               
-       if (Stateful::loading_state_version < CURRENT_SESSION_FILE_VERSION) {
 
-               sys::path backup_path(_session_dir->root_path());
+       if (Stateful::loading_state_version < CURRENT_SESSION_FILE_VERSION && _writable) {
 
-               backup_path /= legalize_for_path (snapshot_name) + "-1" + statefile_suffix;
+               std::string backup_path(_session_dir->root_path());
+               std::string backup_filename = string_compose ("%1-%2%3", legalize_for_path (snapshot_name), Stateful::loading_state_version, statefile_suffix);
+               backup_path = Glib::build_filename (backup_path, backup_filename);
 
-               // only create a backup once
-               if (sys::exists (backup_path)) {
-                       return 0;
-               }
+               // only create a backup for a given statefile version once
 
-               info << string_compose (_("Copying old session file %1 to %2\nUse %2 with %3 versions before 2.0 from now on"),
-                                       xmlpath.to_string(), backup_path.to_string(), PROGRAM_NAME)
-                    << endmsg;
-
-               try
-               {
-                       sys::copy_file (xmlpath, backup_path);
-               }
-               catch(sys::filesystem_error& ex)
-               {
-                       error << string_compose (_("Unable to make backup of state file %1 (%2)"),
-                                       xmlpath.to_string(), ex.what())
-                               << endmsg;
-                       return -1;
+               if (!Glib::file_test (backup_path, Glib::FILE_TEST_EXISTS)) {
+                       
+                       info << string_compose (_("Copying old session file %1 to %2\nUse %2 with %3 versions before 2.0 from now on"),
+                                               xmlpath, backup_path, PROGRAM_NAME)
+                            << endmsg;
+                       
+                       if (!copy_file (xmlpath, backup_path)) {;
+                               return -1;
+                       }
                }
        }
 
@@ -985,15 +979,14 @@ Session::get_template()
 }
 
 XMLNode&
-Session::state(bool full_state)
+Session::state (bool full_state)
 {
        XMLNode* node = new XMLNode("Session");
        XMLNode* child;
 
-       // store libardour version, just in case
        char buf[16];
-       snprintf(buf, sizeof(buf), "%d.%d.%d", libardour3_major_version, libardour3_minor_version, libardour3_micro_version);
-       node->add_property("version", string(buf));
+       snprintf(buf, sizeof(buf), "%d", CURRENT_SESSION_FILE_VERSION);
+       node->add_property("version", buf);
 
        /* store configuration settings */
 
@@ -1047,36 +1040,39 @@ Session::state(bool full_state)
 
        node->add_child_nocopy (config.get_variables ());
 
-       node->add_child_nocopy (_metadata->get_state());
+       node->add_child_nocopy (ARDOUR::SessionMetadata::Metadata()->get_state());
 
        child = node->add_child ("Sources");
 
        if (full_state) {
-               Glib::Mutex::Lock sl (source_lock);
+               Glib::Threads::Mutex::Lock sl (source_lock);
 
                for (SourceMap::iterator siter = sources.begin(); siter != sources.end(); ++siter) {
 
-                       /* Don't save information about non-destructive file sources that are empty
-                           and unused by any regions.
+                       /* Don't save information about non-file Sources, or
+                        * about non-destructive file sources that are empty
+                        * and unused by any regions.
                         */
 
                        boost::shared_ptr<FileSource> fs;
+
                        if ((fs = boost::dynamic_pointer_cast<FileSource> (siter->second)) != 0) {
+
                                if (!fs->destructive()) {
                                        if (fs->empty() && !fs->used()) {
                                                continue;
                                        }
                                }
-                       }
 
-                       child->add_child_nocopy (siter->second->get_state());
+                               child->add_child_nocopy (siter->second->get_state());
+                       }
                }
        }
 
        child = node->add_child ("Regions");
 
        if (full_state) {
-               Glib::Mutex::Lock rl (region_lock);
+               Glib::Threads::Mutex::Lock rl (region_lock);
                 const RegionFactory::RegionMap& region_map (RegionFactory::all_regions());
                 for (RegionFactory::RegionMap::const_iterator i = region_map.begin(); i != region_map.end(); ++i) {
                         boost::shared_ptr<Region> r = i->second;
@@ -1085,6 +1081,22 @@ Session::state(bool full_state)
                                 child->add_child_nocopy (r->state ());
                         }
                 }
+
+               RegionFactory::CompoundAssociations& cassocs (RegionFactory::compound_associations());
+
+               if (!cassocs.empty()) {
+                       XMLNode* ca = node->add_child (X_("CompoundAssociations"));
+
+                       for (RegionFactory::CompoundAssociations::iterator i = cassocs.begin(); i != cassocs.end(); ++i) {
+                               char buf[64];
+                               XMLNode* can = new XMLNode (X_("CompoundAssociation"));
+                               i->first->id().print (buf, sizeof (buf));
+                               can->add_property (X_("copy"), buf);
+                               i->second->id().print (buf, sizeof (buf));
+                               can->add_property (X_("original"), buf);
+                               ca->add_child_nocopy (*can);
+                       }
+               }
        }
 
        if (full_state) {
@@ -1143,17 +1155,19 @@ Session::state(bool full_state)
        }
 
        if (_click_io) {
-               child = node->add_child ("Click");
-               child->add_child_nocopy (_click_io->state (full_state));
+               XMLNode* gain_child = node->add_child ("Click");
+               gain_child->add_child_nocopy (_click_io->state (full_state));
+               gain_child->add_child_nocopy (_click_gain->state (full_state));
        }
 
-       if (full_state) {
-               child = node->add_child ("NamedSelections");
-               for (NamedSelectionList::iterator i = named_selections.begin(); i != named_selections.end(); ++i) {
-                       if (full_state) {
-                               child->add_child_nocopy ((*i)->get_state());
-                       }
-               }
+       if (_ltc_input) {
+               XMLNode* ltc_input_child = node->add_child ("LTC-In");
+               ltc_input_child->add_child_nocopy (_ltc_input->state (full_state));
+       }
+
+       if (_ltc_input) {
+               XMLNode* ltc_output_child = node->add_child ("LTC-Out");
+               ltc_output_child->add_child_nocopy (_ltc_output->state (full_state));
        }
 
         node->add_child_nocopy (_speakers->get_state());
@@ -1189,10 +1203,6 @@ Session::set_state (const XMLNode& node, int version)
                return -1;
        }
 
-       if ((prop = node.property ("version")) != 0) {
-               version = atoi (prop->value ()) * 1000;
-       }
-
        if ((prop = node.property ("name")) != 0) {
                _name = prop->value ();
        }
@@ -1209,7 +1219,7 @@ Session::set_state (const XMLNode& node, int version)
                }
        }
 
-       setup_raid_path(_session_dir->root_path().to_string());
+       setup_raid_path(_session_dir->root_path());
 
        if ((prop = node.property (X_("id-counter"))) != 0) {
                uint64_t x;
@@ -1231,9 +1241,7 @@ Session::set_state (const XMLNode& node, int version)
 
        IO::disable_connecting ();
 
-       if ((child = find_named_node (node, "Extra")) != 0) {
-               _extra_xml = new XMLNode (*child);
-       }
+       Stateful::save_extra_xml (node);
 
        if (((child = find_named_node (node, "Options")) != 0)) { /* old style */
                load_options (*child);
@@ -1246,11 +1254,29 @@ Session::set_state (const XMLNode& node, int version)
        if (version >= 3000) {
                if ((child = find_named_node (node, "Metadata")) == 0) {
                        warning << _("Session: XML state has no metadata section") << endmsg;
-               } else if (_metadata->set_state (*child, version)) {
+               } else if ( ARDOUR::SessionMetadata::Metadata()->set_state (*child, version) ) {
                        goto out;
                }
        }
 
+        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;
@@ -1258,10 +1284,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) {
@@ -1281,20 +1303,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;
@@ -1314,9 +1322,9 @@ Session::set_state (const XMLNode& node, int version)
        } else if (playlists->load_unused (*this, *child)) {
                goto out;
        }
-       
-       if ((child = find_named_node (node, "NamedSelections")) != 0) {
-               if (load_named_selections (*child)) {
+
+       if ((child = find_named_node (node, "CompoundAssociations")) != 0) {
+               if (load_compounds (*child)) {
                        goto out;
                }
        }
@@ -1332,7 +1340,7 @@ Session::set_state (const XMLNode& node, int version)
                        _bundle_xml_node = new XMLNode (*child);
                }
        }
-       
+
        if (version < 3000) {
                if ((child = find_named_node (node, X_("DiskStreams"))) == 0) {
                        error << _("Session: XML state has no diskstreams section") << endmsg;
@@ -1353,16 +1361,16 @@ Session::set_state (const XMLNode& node, int version)
        _diskstreams_2X.clear ();
 
        if (version >= 3000) {
-               
+
                if ((child = find_named_node (node, "RouteGroups")) == 0) {
                        error << _("Session: XML state has no route groups section") << endmsg;
                        goto out;
                } else if (load_route_groups (*child, version)) {
                        goto out;
                }
-               
+
        } else if (version < 3000) {
-               
+
                if ((child = find_named_node (node, "EditGroups")) == 0) {
                        error << _("Session: XML state has no edit groups section") << endmsg;
                        goto out;
@@ -1381,13 +1389,21 @@ Session::set_state (const XMLNode& node, int version)
        if ((child = find_named_node (node, "Click")) == 0) {
                warning << _("Session: XML state has no click section") << endmsg;
        } else if (_click_io) {
-               _click_io->set_state (*child, version);
+               const XMLNodeList& children (child->children());
+               XMLNodeList::const_iterator i = children.begin();
+               _click_io->set_state (**i, version);
+               ++i;
+               if (i != children.end()) {
+                       _click_gain->set_state (**i, version);
+               }
        }
 
-       if ((child = find_named_node (node, "ControlProtocols")) != 0) {
-               ControlProtocolManager::instance().set_protocol_states (*child);
+       if ((child = find_named_node (node, ControlProtocolManager::state_node_name)) != 0) {
+               ControlProtocolManager::instance().set_state (*child, version);
        }
 
+       update_have_rec_enabled_track ();
+
        /* here beginneth the second phase ... */
 
        StateReady (); /* EMIT SIGNAL */
@@ -1417,7 +1433,7 @@ Session::load_routes (const XMLNode& node, int version)
                } else {
                        route = XMLRouteFactory (**niter, version);
                }
-               
+
                if (route == 0) {
                        error << _("Session: cannot create Route from XML description.") << endmsg;
                        return -1;
@@ -1428,7 +1444,7 @@ Session::load_routes (const XMLNode& node, int version)
                new_routes.push_back (route);
        }
 
-       add_routes (new_routes, false, false);
+       add_routes (new_routes, false, false, false);
 
        return 0;
 }
@@ -1456,32 +1472,32 @@ Session::XMLRouteFactory (const XMLNode& node, int version)
        if (ds_child) {
 
                boost::shared_ptr<Track> track;
-                
+
                 if (type == DataType::AUDIO) {
                         track.reset (new AudioTrack (*this, X_("toBeResetFroXML")));
                 } else {
                         track.reset (new MidiTrack (*this, X_("toBeResetFroXML")));
                 }
-                
+
                 if (track->init()) {
                         return ret;
                 }
-                
+
                 if (track->set_state (node, version)) {
                         return ret;
                 }
-                
+
 #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;
-                
+
        } else {
                boost::shared_ptr<Route> r (new Route (*this, X_("toBeResetFroXML")));
 
                 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;
                 }
@@ -1532,28 +1548,28 @@ Session::XMLRouteFactory_2X (const XMLNode& node, int version)
                 } else {
                         track.reset (new MidiTrack (*this, X_("toBeResetFroXML")));
                 }
-                
+
                 if (track->init()) {
                         return ret;
                 }
-                
+
                 if (track->set_state (node, version)) {
                         return ret;
                 }
 
                track->set_diskstream (*i);
-                
-#ifdef BOOST_SP_ENABLE_DEBUG_HOOKS                
-                boost_debug_shared_ptr_mark_interesting (track.get(), "Track");
+
+#ifdef BOOST_SP_ENABLE_DEBUG_HOOKS
+                // boost_debug_shared_ptr_mark_interesting (track.get(), "Track");
 #endif
                 ret = track;
-                
+
        } else {
                boost::shared_ptr<Route> r (new Route (*this, X_("toBeResetFroXML")));
 
                 if (r->init () == 0 && r->set_state (node, version) == 0) {
 #ifdef BOOST_SP_ENABLE_DEBUG_HOOKS
-                        boost_debug_shared_ptr_mark_interesting (rt, "Route");
+                        // boost_debug_shared_ptr_mark_interesting (r.get(), "Route");
 #endif
                         ret = r;
                 }
@@ -1589,6 +1605,79 @@ Session::load_regions (const XMLNode& node)
        return 0;
 }
 
+int
+Session::load_compounds (const XMLNode& node)
+{
+       XMLNodeList calist = node.children();
+       XMLNodeConstIterator caiter;
+       XMLProperty *caprop;
+
+       for (caiter = calist.begin(); caiter != calist.end(); ++caiter) {
+               XMLNode* ca = *caiter;
+               ID orig_id;
+               ID copy_id;
+
+               if ((caprop = ca->property (X_("original"))) == 0) {
+                       continue;
+               }
+               orig_id = caprop->value();
+
+               if ((caprop = ca->property (X_("copy"))) == 0) {
+                       continue;
+               }
+               copy_id = caprop->value();
+
+               boost::shared_ptr<Region> orig = RegionFactory::region_by_id (orig_id);
+               boost::shared_ptr<Region> copy = RegionFactory::region_by_id (copy_id);
+
+               if (!orig || !copy) {
+                       warning << string_compose (_("Regions in compound description not found (ID's %1 and %2): ignored"),
+                                                  orig_id, copy_id)
+                               << endmsg;
+                       continue;
+               }
+
+               RegionFactory::add_compound_association (orig, copy);
+       }
+
+       return 0;
+}
+
+void
+Session::load_nested_sources (const XMLNode& node)
+{
+       XMLNodeList nlist;
+       XMLNodeConstIterator niter;
+
+       nlist = node.children();
+
+       for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
+               if ((*niter)->name() == "Source") {
+
+                       /* it may already exist, so don't recreate it unnecessarily 
+                        */
+
+                       XMLProperty* prop = (*niter)->property (X_("id"));
+                       if (!prop) {
+                               error << _("Nested source has no ID info in session file! (ignored)") << endmsg;
+                               continue;
+                       }
+
+                       ID source_id (prop->value());
+
+                       if (!source_by_id (source_id)) {
+
+                               try {
+                                       SourceFactory::create (*this, **niter, true);
+                               }
+                               catch (failed_constructor& err) {
+                                       error << string_compose (_("Cannot reconstruct nested source for region %1"), name()) << endmsg;
+                               }
+                       }
+               }
+       }
+}
+
 boost::shared_ptr<Region>
 Session::XMLRegionFactory (const XMLNode& node, bool full)
 {
@@ -1596,6 +1685,15 @@ Session::XMLRegionFactory (const XMLNode& node, bool full)
 
        try {
 
+               const XMLNodeList& nlist = node.children();
+
+               for (XMLNodeConstIterator niter = nlist.begin(); niter != nlist.end(); ++niter) {
+                       XMLNode *child = (*niter);
+                       if (child->name() == "NestedSource") {
+                               load_nested_sources (*child);
+                       }
+               }
+
                 if (!type || type->value() == "audio") {
                         return boost::shared_ptr<Region>(XMLAudioRegionFactory (node, full));
                 } else if (type->value() == "midi") {
@@ -1793,7 +1891,7 @@ Session::get_sources_as_xml ()
 
 {
        XMLNode* node = new XMLNode (X_("Sources"));
-       Glib::Mutex::Lock lm (source_lock);
+       Glib::Threads::Mutex::Lock lm (source_lock);
 
        for (SourceMap::iterator i = sources.begin(); i != sources.end(); ++i) {
                node->add_child_nocopy (i->second->get_state());
@@ -1808,7 +1906,7 @@ Session::path_from_region_name (DataType type, string name, string identifier)
        char buf[PATH_MAX+1];
        uint32_t n;
        SessionDirectory sdir(get_best_session_directory_for_new_source());
-       sys::path source_dir = ((type == DataType::AUDIO)
+       std::string source_dir = ((type == DataType::AUDIO)
                ? sdir.sound_path() : sdir.midi_path());
 
         string ext = native_header_format_extension (config.get_native_file_header_format(), type);
@@ -1822,10 +1920,10 @@ Session::path_from_region_name (DataType type, string name, string identifier)
                                        n, ext.c_str());
                }
 
-               sys::path source_path = source_dir / buf;
+               std::string source_path = Glib::build_filename (source_dir, buf);
 
-               if (!sys::exists (source_path)) {
-                       return source_path.to_string();
+               if (!Glib::file_test (source_path, Glib::FILE_TEST_EXISTS)) {
+                       return source_path;
                }
        }
 
@@ -1869,8 +1967,8 @@ Session::load_sources (const XMLNode& node)
                         case 0:
                                 /* user added a new search location, so try again */
                                 goto retry;
-                                
-                                
+
+
                         case 1:
                                 /* user asked to quit the entire session load
                                  */
@@ -1923,96 +2021,103 @@ Session::save_template (string template_name)
                return -1;
        }
 
-       sys::path user_template_dir(user_template_directory());
+       std::string user_template_dir(user_template_directory());
 
-       try
-       {
-               sys::create_directories (user_template_dir);
-       }
-       catch(sys::filesystem_error& ex)
-       {
-               error << string_compose(_("Could not create mix templates directory \"%1\" (%2)"),
-                               user_template_dir.to_string(), ex.what()) << endmsg;
+       if (g_mkdir_with_parents (user_template_dir.c_str(), 0755) != 0) {
+               error << string_compose(_("Could not create templates directory \"%1\" (%2)"),
+                               user_template_dir, g_strerror (errno)) << endmsg;
                return -1;
        }
 
        tree.set_root (&get_template());
 
-       sys::path template_file_path(user_template_dir);
-       template_file_path /= template_name + template_suffix;
+       std::string template_dir_path(user_template_dir);
+       
+       /* directory to put the template in */
+       template_dir_path = Glib::build_filename (template_dir_path, template_name);
 
-       if (sys::exists (template_file_path))
-       {
+       if (Glib::file_test (template_dir_path, Glib::FILE_TEST_EXISTS)) {
                warning << string_compose(_("Template \"%1\" already exists - new version not created"),
-                               template_file_path.to_string()) << endmsg;
+                               template_dir_path) << endmsg;
                return -1;
        }
-
-       if (!tree.write (template_file_path.to_string())) {
-               error << _("template not saved") << endmsg;
+       
+       if (g_mkdir_with_parents (template_dir_path.c_str(), 0755) != 0) {
+               error << string_compose(_("Could not create directory for Session template\"%1\" (%2)"),
+                               template_dir_path, g_strerror (errno)) << endmsg;
                return -1;
        }
 
-       return 0;
-}
+       /* file to write */
+       std::string template_file_path(template_dir_path);
+       template_file_path = Glib::build_filename (template_file_path, template_name + template_suffix);
 
-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;
-
-       if (sys::exists (new_path)) {
-               warning << string_compose(_("Template \"%1\" already exists - template not renamed"),
-                                         new_path.to_string()) << endmsg;
+       if (!tree.write (template_file_path)) {
+               error << _("template not saved") << endmsg;
                return -1;
        }
 
-       try {
-               sys::rename (old_path, new_path);
-               return 0;
-       } catch (...) {
-               return -1;
-       }
-}
+       /* copy plugin state directory */
 
-int
-Session::delete_template (string name)
-{
-       sys::path path = user_template_directory();
-       path /= name + template_suffix;
+       std::string template_plugin_state_path(template_dir_path);
+       template_plugin_state_path = Glib::build_filename (template_plugin_state_path, X_("plugins"));
 
-       try {
-               sys::remove (path);
-               return 0;
-       } catch (...) {
+       if (g_mkdir_with_parents (template_plugin_state_path.c_str(), 0755) != 0) {
+               error << string_compose(_("Could not create directory for Session template plugin state\"%1\" (%2)"),
+                               template_plugin_state_path, g_strerror (errno)) << endmsg;
                return -1;
        }
+
+       copy_files (plugins_dir(), template_plugin_state_path);
+
+       return 0;
 }
 
 void
 Session::refresh_disk_space ()
 {
-#if HAVE_SYS_VFS_H
-       struct statfs statfsbuf;
-       vector<space_and_path>::iterator i;
-       Glib::Mutex::Lock lm (space_lock);
-       double scale;
+#if HAVE_SYS_VFS_H && HAVE_SYS_STATVFS_H
+       
+       Glib::Threads::Mutex::Lock lm (space_lock);
 
        /* get freespace on every FS that is part of the session path */
 
        _total_free_4k_blocks = 0;
+       _total_free_4k_blocks_uncertain = false;
 
-       for (i = session_dirs.begin(); i != session_dirs.end(); ++i) {
-               statfs ((*i).path.c_str(), &statfsbuf);
+       for (vector<space_and_path>::iterator i = session_dirs.begin(); i != session_dirs.end(); ++i) {
 
-               scale = statfsbuf.f_bsize/4096.0;
+               struct statfs statfsbuf;
+               statfs (i->path.c_str(), &statfsbuf);
+
+               double const scale = statfsbuf.f_bsize / 4096.0;
+
+               /* See if this filesystem is read-only */
+               struct statvfs statvfsbuf;
+               statvfs (i->path.c_str(), &statvfsbuf);
+
+               /* f_bavail can be 0 if it is undefined for whatever
+                  filesystem we are looking at; Samba shares mounted
+                  via GVFS are an example of this.
+               */
+               if (statfsbuf.f_bavail == 0) {
+                       /* block count unknown */
+                       i->blocks = 0;
+                       i->blocks_unknown = true;
+               } else if (statvfsbuf.f_flag & ST_RDONLY) {
+                       /* read-only filesystem */
+                       i->blocks = 0;
+                       i->blocks_unknown = false;
+               } else {
+                       /* read/write filesystem with known space */
+                       i->blocks = (uint32_t) floor (statfsbuf.f_bavail * scale);
+                       i->blocks_unknown = false;
+               }
 
-               (*i).blocks = (uint32_t) floor (statfsbuf.f_bavail * scale);
-               _total_free_4k_blocks += (*i).blocks;
+               _total_free_4k_blocks += i->blocks;
+               if (i->blocks_unknown) {
+                       _total_free_4k_blocks_uncertain = true;
+               }
        }
 #endif
 }
@@ -2021,7 +2126,7 @@ string
 Session::get_best_session_directory_for_new_source ()
 {
        vector<space_and_path>::iterator i;
-       string result = _session_dir->root_path().to_string();
+       string result = _session_dir->root_path();
 
        /* handle common case without system calls */
 
@@ -2080,7 +2185,8 @@ Session::get_best_session_directory_for_new_source ()
                        }
 
                        if ((*i).blocks * 4096 >= Config->get_disk_choice_space_threshold()) {
-                               if (create_session_directory ((*i).path)) {
+                               SessionDirectory sdir(i->path);
+                               if (sdir.create ()) {
                                        result = (*i).path;
                                        last_rr_session_dir = i;
                                        return result;
@@ -2102,7 +2208,8 @@ Session::get_best_session_directory_for_new_source ()
                sort (sorted.begin(), sorted.end(), cmp);
 
                for (i = sorted.begin(); i != sorted.end(); ++i) {
-                       if (create_session_directory ((*i).path)) {
+                       SessionDirectory sdir(i->path);
+                       if (sdir.create ()) {
                                result = (*i).path;
                                last_rr_session_dir = i;
                                return result;
@@ -2113,39 +2220,6 @@ Session::get_best_session_directory_for_new_source ()
        return result;
 }
 
-int
-Session::load_named_selections (const XMLNode& node)
-{
-       XMLNodeList nlist;
-       XMLNodeConstIterator niter;
-       NamedSelection *ns;
-
-       nlist = node.children();
-
-       set_dirty();
-
-       for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
-
-               if ((ns = XMLNamedSelectionFactory (**niter)) == 0) {
-                       error << _("Session: cannot create Named Selection from XML description.") << endmsg;
-               }
-       }
-
-       return 0;
-}
-
-NamedSelection *
-Session::XMLNamedSelectionFactory (const XMLNode& node)
-{
-       try {
-               return new NamedSelection (*this, node);
-       }
-
-       catch (failed_constructor& err) {
-               return 0;
-       }
-}
-
 string
 Session::automation_dir () const
 {
@@ -2164,6 +2238,12 @@ Session::plugins_dir () const
        return Glib::build_filename (_path, "plugins");
 }
 
+string
+Session::externals_dir () const
+{
+       return Glib::build_filename (_path, "externals");
+}
+
 int
 Session::load_bundles (XMLNode const & node)
 {
@@ -2178,7 +2258,7 @@ Session::load_bundles (XMLNode const & node)
                } else if ((*niter)->name() == "OutputBundle") {
                        add_bundle (boost::shared_ptr<UserBundle> (new UserBundle (**niter, false)));
                } else {
-                       error << string_compose(_("Unknown node \"%1\" found in Bundles list from state file"), (*niter)->name()) << endmsg;
+                       error << string_compose(_("Unknown node \"%1\" found in Bundles list from session file"), (*niter)->name()) << endmsg;
                        return -1;
                }
        }
@@ -2195,7 +2275,7 @@ Session::load_route_groups (const XMLNode& node, int version)
        set_dirty ();
 
        if (version >= 3000) {
-               
+
                for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
                        if ((*niter)->name() == "RouteGroup") {
                                RouteGroup* rg = new RouteGroup (*this, "");
@@ -2280,9 +2360,10 @@ 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 ();
 }
 
@@ -2356,14 +2437,14 @@ Session::begin_reversible_command (GQuark q)
           to hold all the commands that are committed.  This keeps the order of
           commands correct in the history.
        */
-       
+
        if (_current_trans == 0) {
                /* start a new transaction */
                assert (_current_trans_quarks.empty ());
                _current_trans = new UndoTransaction();
                _current_trans->set_name (g_quark_to_string (q));
        }
-       
+
        _current_trans_quarks.push_front (q);
 }
 
@@ -2372,7 +2453,7 @@ Session::commit_reversible_command (Command *cmd)
 {
        assert (_current_trans);
        assert (!_current_trans_quarks.empty ());
-       
+
        struct timeval now;
 
        if (cmd) {
@@ -2402,7 +2483,7 @@ Session::commit_reversible_command (Command *cmd)
 
 static bool
 accept_all_audio_files (const string& path, void */*arg*/)
-{ 
+{
         if (!Glib::file_test (path, Glib::FILE_TEST_IS_REGULAR)) {
                 return false;
         }
@@ -2504,7 +2585,7 @@ Session::find_all_sources_across_snapshots (set<string>& result, bool exclude_th
                ripped = ripped.substr (0, ripped.length() - 1);
        }
 
-       state_files = scanner (ripped, accept_all_state_files, (void *) 0, false, true);
+       state_files = scanner (ripped, accept_all_state_files, (void *) 0, true, true);
 
        if (state_files == 0) {
                /* impossible! */
@@ -2552,16 +2633,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);
-               
-               if (!audio_region) {
-                       continue;
-               }
-               
-               uint32_t used = playlists->region_use_count (audio_region);
+               uint32_t used = playlists->region_use_count (i->second);
 
-               if (used == 0 && !audio_region->automatic()) {
-                       RegionFactory::map_remove(i->second);
+               if (used == 0 && !i->second->automatic ()) {
+                       RegionFactory::map_remove (i->second);
                }
        }
 
@@ -2593,7 +2668,7 @@ Session::cleanup_sources (CleanupReport& rep)
        _state_of_the_state = (StateOfTheState) (_state_of_the_state | InCleanup);
 
        /* consider deleting all unused playlists */
-       
+
        if (playlists->maybe_delete_unused (boost::bind (Session::ask_about_playlist_deletion, _1))) {
                ret = 0;
                goto out;
@@ -2636,7 +2711,7 @@ Session::cleanup_sources (CleanupReport& rep)
                ++nexti;
 
                SessionDirectory sdir ((*i).path);
-               audio_path += sdir.sound_path().to_string();
+               audio_path += sdir.sound_path();
 
                if (nexti != session_dirs.end()) {
                        audio_path += ':';
@@ -2654,7 +2729,7 @@ Session::cleanup_sources (CleanupReport& rep)
                ++nexti;
 
                SessionDirectory sdir ((*i).path);
-               midi_path += sdir.midi_path().to_string();
+               midi_path += sdir.midi_path();
 
                if (nexti != session_dirs.end()) {
                        midi_path += ':';
@@ -2698,13 +2773,13 @@ Session::cleanup_sources (CleanupReport& rep)
                         if (playlists->source_use_count (fs) != 0) {
                                 all_sources.insert (fs->path());
                         } else {
-                                
+
                                 /* we might not remove this source from disk, because it may be used
                                    by other snapshots, but its not being used in this version
                                    so lets get rid of it now, along with any representative regions
                                    in the region list.
                                 */
-                                
+
                                 RegionFactory::remove_regions_using_source (i->second);
                                 sources.erase (i);
                         }
@@ -2718,18 +2793,18 @@ Session::cleanup_sources (CleanupReport& rep)
 
         if (candidates) {
                 for (vector<string*>::iterator x = candidates->begin(); x != candidates->end(); ++x) {
-                        
+
                         used = false;
                         spath = **x;
-                        
+
                         for (set<string>::iterator i = all_sources.begin(); i != all_sources.end(); ++i) {
-                                
+
                                 if (realpath(spath.c_str(), tmppath1) == 0) {
                                         error << string_compose (_("Cannot expand path %1 (%2)"),
                                                                  spath, strerror (errno)) << endmsg;
                                         continue;
                                 }
-                                
+
                                 if (realpath((*i).c_str(),  tmppath2) == 0) {
                                         error << string_compose (_("Cannot expand path %1 (%2)"),
                                                                  (*i), strerror (errno)) << endmsg;
@@ -2741,7 +2816,7 @@ Session::cleanup_sources (CleanupReport& rep)
                                         break;
                                 }
                         }
-                        
+
                         if (!used) {
                                 unused.push_back (spath);
                         }
@@ -2789,7 +2864,7 @@ Session::cleanup_sources (CleanupReport& rep)
                }
 
                newpath = Glib::build_filename (newpath, Glib::path_get_basename ((*x)));
-                
+
                if (Glib::file_test (newpath, Glib::FILE_TEST_EXISTS)) {
 
                        /* the new path already exists, try versioning */
@@ -2833,19 +2908,19 @@ Session::cleanup_sources (CleanupReport& rep)
                 */
 
                 string base = basename_nosuffix (*x);
-                base += "%A"; /* this is what we add for the channel suffix of all native files, 
+                base += "%A"; /* this is what we add for the channel suffix of all native files,
                                  or for the first channel of embedded files. it will miss
                                  some peakfiles for other channels
                               */
                string peakpath = peak_path (base);
-                
+
                if (Glib::file_test (peakpath.c_str(), Glib::FILE_TEST_EXISTS)) {
                        if (::unlink (peakpath.c_str()) != 0) {
                                error << string_compose (_("cannot remove peakfile %1 for %2 (%3)"),
                                                          peakpath, _path, strerror (errno))
                                      << endmsg;
                                /* try to back out */
-                               rename (newpath.c_str(), _path.c_str());
+                               ::rename (newpath.c_str(), _path.c_str());
                                goto out;
                        }
                }
@@ -2940,7 +3015,7 @@ Session::add_controllable (boost::shared_ptr<Controllable> c)
           as part of the session.
        */
 
-       Glib::Mutex::Lock lm (controllables_lock);
+       Glib::Threads::Mutex::Lock lm (controllables_lock);
        controllables.insert (c);
 }
 
@@ -2949,11 +3024,11 @@ struct null_deleter { void operator()(void const *) const {} };
 void
 Session::remove_controllable (Controllable* c)
 {
-       if (_state_of_the_state | Deletion) {
+       if (_state_of_the_state & Deletion) {
                return;
        }
 
-       Glib::Mutex::Lock lm (controllables_lock);
+       Glib::Threads::Mutex::Lock lm (controllables_lock);
 
        Controllables::iterator x = controllables.find (boost::shared_ptr<Controllable>(c, null_deleter()));
 
@@ -2965,7 +3040,7 @@ Session::remove_controllable (Controllable* c)
 boost::shared_ptr<Controllable>
 Session::controllable_by_id (const PBD::ID& id)
 {
-       Glib::Mutex::Lock lm (controllables_lock);
+       Glib::Threads::Mutex::Lock lm (controllables_lock);
 
        for (Controllables::iterator i = controllables.begin(); i != controllables.end(); ++i) {
                if ((*i)->id() == id) {
@@ -3000,7 +3075,7 @@ Session::controllable_by_descriptor (const ControllableDescriptor& desc)
                r = route_by_remote_id (desc.rid());
                break;
        }
-       
+
        if (!r) {
                return c;
        }
@@ -3021,7 +3096,7 @@ Session::controllable_by_descriptor (const ControllableDescriptor& desc)
        case ControllableDescriptor::Recenable:
        {
                boost::shared_ptr<Track> t = boost::dynamic_pointer_cast<Track>(r);
-               
+
                if (t) {
                        c = t->rec_enable_control ();
                }
@@ -3056,17 +3131,17 @@ Session::controllable_by_descriptor (const ControllableDescriptor& desc)
                uint32_t parameter_index = desc.target (1);
 
                /* revert to zero based counting */
-               
+
                if (plugin > 0) {
                        --plugin;
                }
-               
+
                if (parameter_index > 0) {
                        --parameter_index;
                }
 
                boost::shared_ptr<Processor> p = r->nth_plugin (plugin);
-               
+
                if (p) {
                        c = boost::dynamic_pointer_cast<ARDOUR::AutomationControl>(
                                p->control(Evoral::Parameter(PluginAutomation, 0, parameter_index)));
@@ -3074,22 +3149,22 @@ Session::controllable_by_descriptor (const ControllableDescriptor& desc)
                break;
        }
 
-       case ControllableDescriptor::SendGain: 
+       case ControllableDescriptor::SendGain:
        {
                uint32_t send = desc.target (0);
 
                /* revert to zero-based counting */
-               
+
                if (send > 0) {
                        --send;
                }
-               
+
                boost::shared_ptr<Processor> p = r->nth_send (send);
-               
+
                if (p) {
                        boost::shared_ptr<Send> s = boost::dynamic_pointer_cast<Send>(p);
                        boost::shared_ptr<Amp> a = s->amp();
-
+                       
                        if (a) {
                                c = s->amp()->gain_control();
                        }
@@ -3138,16 +3213,11 @@ Session::save_history (string snapshot_name)
 
        const string history_filename = legalize_for_path (snapshot_name) + history_suffix;
        const string backup_filename = history_filename + backup_suffix;
-       const sys::path xml_path = _session_dir->root_path() / history_filename;
-       const sys::path backup_path = _session_dir->root_path() / backup_filename;
+       const std::string xml_path(Glib::build_filename (_session_dir->root_path(), history_filename));
+       const std::string backup_path(Glib::build_filename (_session_dir->root_path(), backup_filename));
 
-       if (sys::exists (xml_path)) {
-               try
-               {
-                       sys::rename (xml_path, backup_path);
-               }
-               catch (const sys::filesystem_error& err)
-               {
+       if (Glib::file_test (xml_path, Glib::FILE_TEST_EXISTS)) {
+               if (::g_rename (xml_path.c_str(), backup_path.c_str()) != 0) {
                        error << _("could not backup old history file, current history not saved") << endmsg;
                        return -1;
                }
@@ -3159,19 +3229,17 @@ Session::save_history (string snapshot_name)
 
        tree.set_root (&_history.get_state (Config->get_saved_history_depth()));
 
-       if (!tree.write (xml_path.to_string()))
+       if (!tree.write (xml_path))
        {
-               error << string_compose (_("history could not be saved to %1"), xml_path.to_string()) << endmsg;
+               error << string_compose (_("history could not be saved to %1"), xml_path) << endmsg;
 
-               try
-               {
-                       sys::remove (xml_path);
-                       sys::rename (backup_path, xml_path);
+               if (g_remove (xml_path.c_str()) != 0) {
+                       error << string_compose(_("Could not remove history file at path \"%1\" (%2)"),
+                                       xml_path, g_strerror (errno)) << endmsg;
                }
-               catch (const sys::filesystem_error& err)
-               {
+               if (::g_rename (backup_path.c_str(), xml_path.c_str()) != 0) {
                        error << string_compose (_("could not restore history file from backup %1 (%2)"),
-                                       backup_path.to_string(), err.what()) << endmsg;
+                                       backup_path, g_strerror (errno)) << endmsg;
                }
 
                return -1;
@@ -3189,20 +3257,20 @@ Session::restore_history (string snapshot_name)
                snapshot_name = _current_snapshot_name;
        }
 
-       const string xml_filename = legalize_for_path (snapshot_name) + history_suffix;
-       const sys::path xml_path = _session_dir->root_path() / xml_filename;
+       const std::string xml_filename = legalize_for_path (snapshot_name) + history_suffix;
+       const std::string xml_path(Glib::build_filename (_session_dir->root_path(), xml_filename));
 
-       info << "Loading history from " << xml_path.to_string() << endmsg;
+       info << "Loading history from " << xml_path << endmsg;
 
-       if (!sys::exists (xml_path)) {
+       if (!Glib::file_test (xml_path, Glib::FILE_TEST_EXISTS)) {
                info << string_compose (_("%1: no history file \"%2\" for this session."),
-                               _name, xml_path.to_string()) << endmsg;
+                               _name, xml_path) << endmsg;
                return 1;
        }
 
-       if (!tree.read (xml_path.to_string())) {
+       if (!tree.read (xml_path)) {
                error << string_compose (_("Could not understand session history file \"%1\""),
-                               xml_path.to_string()) << endmsg;
+                               xml_path) << endmsg;
                return -1;
        }
 
@@ -3256,7 +3324,7 @@ Session::restore_history (string snapshot_name)
                                } else {
                                        error << _("Failed to downcast MidiSource for SysExDiffCommand") << endmsg;
                                }
-                               
+
                        } else if (n->name() == "PatchChangeDiffCommand") {
 
                                PBD::ID id (n->property("midi-source")->value());
@@ -3331,7 +3399,7 @@ Session::config_changed (std::string p, bool ours)
 
        } else if (p == "edit-mode") {
 
-               Glib::Mutex::Lock lm (playlists->lock);
+               Glib::Threads::Mutex::Lock lm (playlists->lock);
 
                for (SessionPlaylists::List::iterator i = playlists->playlists.begin(); i != playlists->playlists.end(); ++i) {
                        (*i)->set_edit_mode (Config->get_edit_mode ());
@@ -3345,11 +3413,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());
 
@@ -3399,6 +3467,12 @@ Session::config_changed (std::string p, bool ours)
                        _clicking = false;
                }
 
+       } else if (p == "click-gain") {
+               
+               if (_click_gain) {
+                       _click_gain->set_gain (Config->get_click_gain(), this);
+               }
+
        } else if (p == "send-mtc") {
 
                if (Config->get_send_mtc ()) {
@@ -3438,16 +3512,32 @@ Session::config_changed (std::string p, bool ours)
                if (!config.get_external_sync()) {
                        drop_sync_source ();
                } else {
-                       switch_to_sync_source (config.get_sync_source());
+                       switch_to_sync_source (Config->get_sync_source());
                }
-       } else if (p == "remote-model") {
-               set_remote_control_ids ();
        }  else if (p == "denormal-model") {
                setup_fpu ();
        } else if (p == "history-depth") {
                set_history_depth (Config->get_history_depth());
+       } else if (p == "remote-model") {
+               /* XXX DO SOMETHING HERE TO TELL THE GUI THAT WE NEED
+                  TO SET REMOTE ID'S
+               */
        } else if (p == "sync-all-route-ordering") {
-               sync_order_keys ("session");
+
+               /* sync to editor order unless mixer is used for remote IDs 
+                */
+
+               switch (Config->get_remote_model()) {
+               case UserOrdered:
+                       sync_order_keys (EditorSort);
+                       break;
+               case EditorOrdered:
+                       sync_order_keys (EditorSort);
+                       break;
+               case MixerOrdered:
+                       sync_order_keys (MixerSort);
+               }
+                       
        } else if (p == "initial-program-change") {
 
                if (MIDI::Manager::instance()->mmc()->output_port() && Config->get_initial_program_change() >= 0) {
@@ -3466,6 +3556,16 @@ Session::config_changed (std::string p, bool ours)
                solo_control_mode_changed ();
        } else if (p == "timecode-offset" || p == "timecode-offset-negative") {
                last_timecode_valid = false;
+       } else if (p == "playback-buffer-seconds") {
+               AudioSource::allocate_working_buffers (frame_rate());
+       } else if (p == "automation-thinning-factor") {
+               Evoral::ControlList::set_thinning_factor (Config->get_automation_thinning_factor());
+       } else if (p == "ltc-source-port") {
+               reconnect_ltc_input ();
+       } else if (p == "ltc-sink-port") {
+               reconnect_ltc_output ();
+       } else if (p == "timecode-generator-offset") {
+               ltc_tx_parse_offset();
        }
 
        set_dirty ();
@@ -3511,7 +3611,7 @@ void
 Session::setup_midi_machine_control ()
 {
        MIDI::MachineControl* mmc = MIDI::Manager::instance()->mmc ();
-       
+
        mmc->Play.connect_same_thread (*this, boost::bind (&Session::mmc_deferred_play, this, _1));
        mmc->DeferredPlay.connect_same_thread (*this, boost::bind (&Session::mmc_deferred_play, this, _1));
        mmc->Stop.connect_same_thread (*this, boost::bind (&Session::mmc_stop, this, _1));
@@ -3528,9 +3628,9 @@ Session::setup_midi_machine_control ()
 
        /* also handle MIDI SPP because its so common */
 
-       mmc->SPPStart.connect_same_thread (*this, boost::bind (&Session::spp_start, this, _1, _2));
-       mmc->SPPContinue.connect_same_thread (*this, boost::bind (&Session::spp_continue, this, _1, _2));
-       mmc->SPPStop.connect_same_thread (*this, boost::bind (&Session::spp_stop, this, _1, _2));
+       mmc->SPPStart.connect_same_thread (*this, boost::bind (&Session::spp_start, this));
+       mmc->SPPContinue.connect_same_thread (*this, boost::bind (&Session::spp_continue, this));
+       mmc->SPPStop.connect_same_thread (*this, boost::bind (&Session::spp_stop, this));
 }
 
 boost::shared_ptr<Controllable>
@@ -3544,6 +3644,173 @@ Session::solo_cut_control() const
            it up as a Controllable. Changes to the Controllable will just map back to the RCConfiguration
            parameter.
         */
-        
+
         return _solo_cut_control;
 }
+
+int
+Session::rename (const std::string& new_name)
+{
+       string legal_name = legalize_for_path (new_name);
+       string newpath;
+       string oldstr;
+       string newstr;
+       bool first = true;
+
+       string const old_sources_root = _session_dir->sources_root();
+
+#define RENAME ::rename
+
+       /* Rename:
+
+        * session directory
+        * interchange subdirectory
+        * session file
+        * session history
+        
+        * Backup files are left unchanged and not renamed.
+        */
+
+       /* pass one: not 100% safe check that the new directory names don't
+        * already exist ...
+        */
+
+       for (vector<space_and_path>::const_iterator i = session_dirs.begin(); i != session_dirs.end(); ++i) {
+               vector<string> v;
+
+               oldstr = (*i).path;
+
+               /* this is a stupid hack because Glib::path_get_dirname() is
+                * lexical-only, and so passing it /a/b/c/ gives a different
+                * result than passing it /a/b/c ...
+                */
+
+               if (oldstr[oldstr.length()-1] == G_DIR_SEPARATOR) {
+                       oldstr = oldstr.substr (0, oldstr.length() - 1);
+               }
+
+               string base = Glib::path_get_dirname (oldstr);
+               string p = Glib::path_get_basename (oldstr);
+
+               newstr = Glib::build_filename (base, legal_name);
+               
+               if (Glib::file_test (newstr, Glib::FILE_TEST_EXISTS)) {
+                       return -1;
+               }
+       }
+
+       /* Session dirs */
+       
+       for (vector<space_and_path>::const_iterator i = session_dirs.begin(); i != session_dirs.end(); ++i) {
+               vector<string> v;
+
+               oldstr = (*i).path;
+
+               /* this is a stupid hack because Glib::path_get_dirname() is
+                * lexical-only, and so passing it /a/b/c/ gives a different
+                * result than passing it /a/b/c ...
+                */
+
+               if (oldstr[oldstr.length()-1] == G_DIR_SEPARATOR) {
+                       oldstr = oldstr.substr (0, oldstr.length() - 1);
+               }
+
+               string base = Glib::path_get_dirname (oldstr);
+               string p = Glib::path_get_basename (oldstr);
+
+               newstr = Glib::build_filename (base, legal_name);
+
+               cerr << "Rename " << oldstr << " => " << newstr << endl;                
+
+               if (RENAME (oldstr.c_str(), newstr.c_str()) != 0) {
+                       return 1;
+               }
+
+               if (first) {
+                       (*_session_dir) = newstr;
+                       newpath = newstr;
+                       first = 1;
+               }
+
+               /* directory below interchange */
+
+               v.push_back (newstr);
+               v.push_back (interchange_dir_name);
+               v.push_back (p);
+
+               oldstr = Glib::build_filename (v);
+
+               v.clear ();
+               v.push_back (newstr);
+               v.push_back (interchange_dir_name);
+               v.push_back (legal_name);
+
+               newstr = Glib::build_filename (v);
+               
+               cerr << "Rename " << oldstr << " => " << newstr << endl;
+               
+               if (RENAME (oldstr.c_str(), newstr.c_str()) != 0) {
+                       return 1;
+               }
+       }
+
+       /* state file */
+       
+       oldstr = Glib::build_filename (newpath, _current_snapshot_name) + statefile_suffix;
+       newstr= Glib::build_filename (newpath, legal_name) + statefile_suffix;
+       
+       cerr << "Rename " << oldstr << " => " << newstr << endl;                
+
+       if (RENAME (oldstr.c_str(), newstr.c_str()) != 0) {
+               return 1;
+       }
+
+       /* history file */
+
+       
+       oldstr = Glib::build_filename (newpath, _current_snapshot_name) + history_suffix;
+
+       if (Glib::file_test (oldstr, Glib::FILE_TEST_EXISTS))  {
+               newstr = Glib::build_filename (newpath, legal_name) + history_suffix;
+               
+               cerr << "Rename " << oldstr << " => " << newstr << endl;                
+               
+               if (RENAME (oldstr.c_str(), newstr.c_str()) != 0) {
+                       return 1;
+               }
+       }
+
+       /* 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());
+                       fs->set_path (p);
+               }
+       }
+
+       /* remove old name from recent sessions */
+
+       remove_recent_sessions (_path);
+
+       _path = newpath;
+       _current_snapshot_name = new_name;
+       _name = new_name;
+
+       set_dirty ();
+
+       /* save state again to get everything just right */
+
+       save_state (_current_snapshot_name);
+
+
+       /* add to recent sessions */
+
+       store_recent_sessions (new_name, _path);
+
+       return 0;
+
+#undef RENAME
+}