[Summary] Adding informative message to failed file copy exception in Session::save_as.
[ardour.git] / libs / ardour / session_state.cc
index 03acb63f0e0debe3ef368862fe197afe58842ff3..669517bdfb3fa24b95d26de8d139209584811ad4 100644 (file)
 #include "ardour/playlist_source.h"
 #include "ardour/port.h"
 #include "ardour/processor.h"
+#include "ardour/profile.h"
 #include "ardour/proxy_controllable.h"
 #include "ardour/recent_sessions.h"
 #include "ardour/region_factory.h"
@@ -490,7 +491,7 @@ Session::create (const string& session_template, BusProfile* bus_profile)
        _writable = exists_and_writable (_path);
 
        if (!session_template.empty()) {
-               std::string in_path = session_template_dir_to_file (session_template);
+               std::string in_path = (ARDOUR::Profile->get_trx () ? session_template : session_template_dir_to_file (session_template));
 
                ifstream in(in_path.c_str());
 
@@ -1045,17 +1046,31 @@ Session::state (bool full_state)
                        }
                }
        }
+       
+       
 
        if (full_state) {
-               node->add_child_nocopy (_locations->get_state());
+               
+               if (_locations) {
+                       node->add_child_nocopy (_locations->get_state());       
+               }
        } else {
+               Locations loc (*this);
                // for a template, just create a new Locations, populate it
                // with the default start and end, and get the state for that.
-               Locations loc (*this);
                Location* range = new Location (*this, 0, 0, _("session"), Location::IsSessionRange);
                range->set (max_framepos, 0);
                loc.add (range);
-               node->add_child_nocopy (loc.get_state());
+               XMLNode& locations_state = loc.get_state();
+               
+               if (ARDOUR::Profile->get_trx() && _locations) {
+                       for (Locations::LocationList::const_iterator i = _locations->list ().begin (); i != _locations->list ().end (); ++i) {
+                               if ((*i)->is_mark () || (*i)->is_auto_loop ()) {
+                                       locations_state.add_child_nocopy ((*i)->get_state ());
+                               }
+                       }
+               }
+               node->add_child_nocopy (locations_state);
        }
 
        child = node->add_child ("Bundles");
@@ -1076,12 +1091,12 @@ Session::state (bool full_state)
                RoutePublicOrderSorter cmp;
                RouteList public_order (*r);
                public_order.sort (cmp);
-
-                /* the sort should have put control outs first */
-
-                if (_monitor_out) {
-                        assert (_monitor_out == public_order.front());
-                }
+               
+               /* the sort should have put control outs first */
+               
+               if (_monitor_out) {
+                       assert (_monitor_out == public_order.front());
+               }
 
                for (RouteList::iterator i = public_order.begin(); i != public_order.end(); ++i) {
                        if (!(*i)->is_auditioner()) {
@@ -1985,60 +2000,80 @@ Session::XMLSourceFactory (const XMLNode& node)
 int
 Session::save_template (string template_name)
 {
-       XMLTree tree;
-
-       if (_state_of_the_state & CannotSave) {
+       if ((_state_of_the_state & CannotSave) || template_name.empty ()) {
                return -1;
        }
 
-       std::string user_template_dir(user_template_directory());
+       bool absolute_path = Glib::path_is_absolute (template_name);
 
-       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;
-       }
+       /* directory to put the template in */
+       std::string template_dir_path;
 
-       tree.set_root (&get_template());
+       if (!absolute_path) {
+               std::string user_template_dir(user_template_directory());
 
-       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 (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;
+               }
 
-       if (Glib::file_test (template_dir_path, Glib::FILE_TEST_EXISTS)) {
-               warning << string_compose(_("Template \"%1\" already exists - new version not created"),
-                               template_dir_path) << endmsg;
-               return -1;
+               template_dir_path = Glib::build_filename (user_template_dir, template_name);
+       } else {
+               template_dir_path = template_name;
        }
-       
-       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;
+
+       if (!ARDOUR::Profile->get_trx()) {
+               if (Glib::file_test (template_dir_path, Glib::FILE_TEST_EXISTS)) {
+                       warning << string_compose(_("Template \"%1\" already exists - new version not created"),
+                                                                         template_dir_path) << endmsg;
+                       return -1;
+               }
+               
+               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;
+               }
        }
 
        /* file to write */
-       std::string template_file_path(template_dir_path);
-       template_file_path = Glib::build_filename (template_file_path, template_name + template_suffix);
+       std::string template_file_path;
+       
+       if (ARDOUR::Profile->get_trx()) {
+               template_file_path = template_name;
+       } else {
+               if (absolute_path) {
+                       template_file_path = Glib::build_filename (template_dir_path, Glib::path_get_basename (template_dir_path) + template_suffix);
+               } else {
+                       template_file_path = Glib::build_filename (template_dir_path, template_name + template_suffix);
+               }
+       }
 
+       SessionSaveUnderway (); /* EMIT SIGNAL */
+       
+       XMLTree tree;
+
+       tree.set_root (&get_template());
        if (!tree.write (template_file_path)) {
                error << _("template not saved") << endmsg;
                return -1;
        }
 
-       /* copy plugin state directory */
+       if (!ARDOUR::Profile->get_trx()) {
+               /* copy plugin state directory */
 
-       std::string template_plugin_state_path(template_dir_path);
-       template_plugin_state_path = Glib::build_filename (template_plugin_state_path, X_("plugins"));
+               std::string template_plugin_state_path (Glib::build_filename (template_dir_path, X_("plugins")));
 
-       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;
+               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);
        }
 
-       copy_recurse (plugins_dir(), template_plugin_state_path);
+       store_recent_templates (template_file_path);
 
        return 0;
 }
@@ -3676,6 +3711,20 @@ Session::rename (const std::string& new_name)
         * Backup files are left unchanged and not renamed.
         */
 
+       /* Windows requires that we close all files before attempting the
+        * rename. This works on other platforms, but isn't necessary there.
+        * Leave it in place for all platforms though, since it may help
+        * catch issues that could arise if the way Source files work ever
+        * change (since most developers are not using Windows).
+        */
+
+       for (SourceMap::const_iterator i = sources.begin(); i != sources.end(); ++i) {
+               boost::shared_ptr<FileSource> fs = boost::dynamic_pointer_cast<FileSource> (i->second);
+               if (fs) {
+                       fs->close ();
+               }
+       }
+       
        /* pass one: not 100% safe check that the new directory names don't
         * already exist ...
         */
@@ -4119,6 +4168,15 @@ Session::save_as (SaveAs& saveas)
                        
                        find_files_matching_filter (files, (*sd).path, accept_all_files, 0, false, true, true);
                        
+                       /* add dir separator to protect against collisions with
+                        * track names (e.g. track named "audiofiles" or
+                        * "analysis".
+                        */
+
+                       static const std::string audiofile_dir_string = string (sound_dir_name) + G_DIR_SEPARATOR;
+                       static const std::string midifile_dir_string = string (midi_dir_name) + G_DIR_SEPARATOR;
+                       static const std::string analysis_dir_string = analysis_dir() + G_DIR_SEPARATOR;
+
                        /* copy all the files. Handling is different for media files
                           than others because of the *silly* subtree we have below the interchange
                           folder. That really was a bad idea, but I'm not fixing it as part of
@@ -4128,10 +4186,18 @@ Session::save_as (SaveAs& saveas)
                        for (vector<string>::iterator i = files.begin(); i != files.end(); ++i) {
 
                                std::string from = *i;
+
+#ifdef __APPLE__
+                               string filename = Glib::path_get_basename (from);
+                               std::transform (filename.begin(), filename.end(), filename.begin(), ::toupper);
+                               if (filename == ".DS_STORE") {
+                                       continue;
+                               }
+#endif
                                
-                               if ((*i).find (interchange_dir_name) != string::npos) {
+                               if (from.find (audiofile_dir_string) != string::npos) {
                                        
-                                       /* media file */
+                                       /* audio file: only copy if asked */
 
                                        if (saveas.include_media && saveas.copy_media) {
                                                
@@ -4140,13 +4206,46 @@ Session::save_as (SaveAs& saveas)
                                                info << "media file copying from " << from << " to " << to << endmsg;
                                                
                                                if (!copy_file (from, to)) {
-                                                       throw Glib::FileError (Glib::FileError::IO_ERROR, "copy failed");
+                                                       throw Glib::FileError (Glib::FileError::IO_ERROR,
+                                                                                                  string_compose(_("\ncopying \"%1\" failed !"), from));
                                                }
                                        }
                                        
                                        /* we found media files inside the session folder */
                                        
                                        internal_file_cnt++;
+
+                               } else if (from.find (midifile_dir_string) != string::npos) {
+
+                                       /* midi file: always copy unless
+                                        * creating an empty new session
+                                        */
+
+                                       if (saveas.include_media) {
+                                       
+                                               string to = make_new_media_path (*i, to_dir, new_folder);
+                                               
+                                               info << "media file copying from " << from << " to " << to << endmsg;
+                                               
+                                               if (!copy_file (from, to)) {
+                                                       throw Glib::FileError (Glib::FileError::IO_ERROR, "copy failed");
+                                               }
+                                       }
+
+                                       /* we found media files inside the session folder */
+                                               
+                                       internal_file_cnt++;
+                                       
+                               } else if (from.find (analysis_dir_string) != string::npos) {
+
+                                       /*  make sure analysis dir exists in
+                                        *  new session folder, but we're not
+                                        *  copying analysis files here, see
+                                        *  below 
+                                        */
+                                       
+                                       (void) g_mkdir_with_parents (analysis_dir().c_str(), 775);
+                                       continue;
                                        
                                } else {
                                        
@@ -4156,15 +4255,22 @@ Session::save_as (SaveAs& saveas)
                                        bool do_copy = true;
                                        
                                        for (vector<string>::iterator v = do_not_copy_extensions.begin(); v != do_not_copy_extensions.end(); ++v) {
-                                               if (((*i).length() > (*v).length()) && ((*i).find (*v) == (*i).length() - (*v).length())) {
+                                               if ((from.length() > (*v).length()) && (from.find (*v) == from.length() - (*v).length())) {
                                                        /* end of filename matches extension, do not copy file */
                                                        do_copy = false;
                                                        break;
                                                } 
                                        }
+
+                                       if (!saveas.copy_media && from.find (peakfile_suffix) != string::npos) {
+                                               /* don't copy peakfiles if
+                                                * we're not copying media
+                                                */
+                                               do_copy = false;
+                                       }
                                        
                                        if (do_copy) {
-                                               string to = Glib::build_filename (to_dir, (*i).substr (prefix_len));
+                                               string to = Glib::build_filename (to_dir, from.substr (prefix_len));
                                                
                                                info << "attempting to make directory/folder " << to << endmsg;
 
@@ -4175,7 +4281,8 @@ Session::save_as (SaveAs& saveas)
                                                info << "attempting to copy " << from << " to " << to << endmsg;
                                                
                                                if (!copy_file (from, to)) {
-                                                       throw Glib::FileError (Glib::FileError::IO_ERROR, "copy failed");
+                                                       throw Glib::FileError (Glib::FileError::IO_ERROR,
+                                                                                                  string_compose(_("\ncopying \"%1\" failed !"), from));
                                                }
                                        }
                                }
@@ -4186,19 +4293,28 @@ Session::save_as (SaveAs& saveas)
                                */
                                
                                GStatBuf gsb;
-                               g_stat ((*i).c_str(), &gsb);
+                               g_stat (from.c_str(), &gsb);
                                copied += gsb.st_size;
                                cnt++;
                                
                                double fraction = (double) copied / total_bytes;
                                
-                               /* tell someone "X percent, file M of N"; M is one-based */
-                               
-                               boost::optional<bool> res = saveas.Progress (fraction, cnt, all);
                                bool keep_going = true;
 
-                               if (res) {
-                                       keep_going = *res;
+                               if (saveas.copy_media) {
+
+                                       /* no need or expectation of this if
+                                        * media is not being copied, because
+                                        * it will be fast(ish).
+                                        */
+                                       
+                                       /* tell someone "X percent, file M of N"; M is one-based */
+                                       
+                                       boost::optional<bool> res = saveas.Progress (fraction, cnt, all);
+                                       
+                                       if (res) {
+                                               keep_going = *res;
+                                       }
                                }
 
                                if (!keep_going) {
@@ -4231,7 +4347,20 @@ Session::save_as (SaveAs& saveas)
                if (saveas.include_media) {
                
                        if (saveas.copy_media) {
-                               
+#ifndef PLATFORM_WINDOWS
+                               /* There are problems with analysis files on
+                                * Windows, because they used a colon in their
+                                * names as late as 4.0. Colons are not legal
+                                * under Windows even if NTFS allows them.
+                                *
+                                * This is a tricky problem to solve so for
+                                * just don't copy these files. They will be
+                                * regenerated as-needed anyway, subject to the 
+                                * existing issue that the filenames will be
+                                * rejected by Windows, which is a separate
+                                * problem (though related).
+                                */
+
                                /* only needed if we are copying media, since the
                                 * analysis data refers to media data
                                 */
@@ -4241,6 +4370,7 @@ Session::save_as (SaveAs& saveas)
                                        string newdir = Glib::build_filename (to_dir, "analysis");
                                        copy_files (old, newdir);
                                }
+#endif /* PLATFORM_WINDOWS */                          
                        }
                }
                        
@@ -4258,12 +4388,12 @@ Session::save_as (SaveAs& saveas)
                        if (internal_file_cnt) {
                                for (vector<string>::iterator s = old_search_path[DataType::AUDIO].begin(); s != old_search_path[DataType::AUDIO].end(); ++s) {
                                        ensure_search_path_includes (*s, DataType::AUDIO);
-                                       cerr << "be sure to include " << *s << "  for audio" << endl;
                                }
 
-                               for (vector<string>::iterator s = old_search_path[DataType::MIDI].begin(); s != old_search_path[DataType::MIDI].end(); ++s) {
-                                       ensure_search_path_includes (*s, DataType::MIDI);
-                               }
+                               /* we do not do this for MIDI because we copy
+                                  all MIDI files if saveas.include_media is
+                                  true
+                               */
                        }
                }