Merge branch 'master' into saveas
authorPaul Davis <paul@linuxaudiosystems.com>
Tue, 21 Apr 2015 04:19:29 +0000 (00:19 -0400)
committerPaul Davis <paul@linuxaudiosystems.com>
Tue, 21 Apr 2015 04:19:29 +0000 (00:19 -0400)
24 files changed:
gtk2_ardour/ardour.menus.in
gtk2_ardour/ardour_ui.cc
gtk2_ardour/ardour_ui.h
gtk2_ardour/ardour_ui_ed.cc
gtk2_ardour/save_as_dialog.cc [new file with mode: 0644]
gtk2_ardour/save_as_dialog.h [new file with mode: 0644]
gtk2_ardour/wscript
libs/ardour/ardour/coreaudiosource.h
libs/ardour/ardour/file_source.h
libs/ardour/ardour/session.h
libs/ardour/ardour/silentfilesource.h
libs/ardour/ardour/smf_source.h
libs/ardour/ardour/sndfilesource.h
libs/ardour/ardour/srcfilesource.h
libs/ardour/coreaudiosource.cc
libs/ardour/file_source.cc
libs/ardour/import.cc
libs/ardour/session_directory.cc
libs/ardour/session_state.cc
libs/ardour/smf_source.cc
libs/ardour/sndfilesource.cc
libs/ardour/srcfilesource.cc
libs/evoral/evoral/SMF.hpp
libs/evoral/src/SMF.cpp

index b081b58342f388582324ed35badfc348aa6e9874..33645570004f58b25522f04fe7873b45f91c9ee0 100644 (file)
@@ -10,8 +10,9 @@
       <menuitem action='SaveAs'/>
 #ifndef WINDOWS // can't move open files.
       <menuitem action='Rename'/>
+      <menuitem action='SnapshotStay'/>
+      <menuitem action='SnapshotSwitch'/>
 #endif
-      <menuitem action='Snapshot'/>
       <menuitem action='SaveTemplate'/>
       <menu name='Metadata' action='Metadata'>
         <menuitem action='EditMetadata'/>
index 307a8ba7f25e1137a8d49d06f8cccb256ea40e32..4939c7873562c56e634dcb3b5b442e70219ec6b2 100644 (file)
@@ -128,6 +128,7 @@ typedef uint64_t microseconds_t;
 #include "rc_option_editor.h"
 #include "route_time_axis.h"
 #include "route_params_ui.h"
+#include "save_as_dialog.h"
 #include "session_dialog.h"
 #include "session_metadata_dialog.h"
 #include "session_option_editor.h"
@@ -2373,6 +2374,78 @@ ARDOUR_UI::stop_clocking ()
        clock_signal_connection.disconnect ();
 }
 
+bool
+ARDOUR_UI::save_as_progress_update (float fraction, int64_t cnt, int64_t total, Gtk::Label* label, Gtk::ProgressBar* bar)
+{
+       char buf[256];
+
+       snprintf (buf, sizeof (buf), _("Copied %" PRId64 " of %" PRId64), cnt, total);
+
+       label->set_text (buf);
+       bar->set_fraction (fraction);
+
+       /* process events, redraws, etc. */
+       
+       while (gtk_events_pending()) {
+               gtk_main_iteration ();
+       }
+
+       return true; /* continue with save-as */
+}
+
+void
+ARDOUR_UI::save_session_as ()
+{
+       if (!_session) {
+               return;
+       }
+
+       SaveAsDialog sad;
+
+       switch (sad.run()) {
+       case Gtk::RESPONSE_OK:
+               break;
+       default:
+               return;
+       }
+       
+       ArdourDialog progress_dialog(_("Save As"), true);
+       Gtk::Label label;
+       Gtk::ProgressBar progress_bar;
+
+       progress_dialog.get_vbox()->pack_start (label);
+       progress_dialog.get_vbox()->pack_start (progress_bar);
+       label.show ();
+       progress_bar.show ();
+       
+       Session::SaveAs sa;
+
+       sa.new_parent_folder = sad.new_parent_folder ();
+       sa.new_name = sad.new_name ();
+       sa.switch_to = sad.switch_to();
+       sa.copy_media = sad.copy_media();
+       sa.copy_external = sad.copy_external();
+
+       /* this signal will be emitted from within this, the calling thread,
+        * after every file is copied. It provides information on percentage
+        * complete (in terms of total data to copy), the number of files
+        * copied so far, and the total number to copy.
+        */
+
+       ScopedConnection c;
+
+       sa.Progress.connect_same_thread (c, boost::bind (&ARDOUR_UI::save_as_progress_update, this, _1, _2, _3, &label, &progress_bar));
+       
+       progress_dialog.show_all ();
+       progress_dialog.present ();
+       
+       if (_session->save_as (sa)) {
+               /* ERROR MESSAGE */
+               MessageDialog msg (string_compose (_("Save As failed: %1"), sa.failure_message));
+               msg.run ();
+       }
+}
+
 /** Ask the user for the name of a new snapshot and then take it.
  */
 
index e80761cf32d98b78141b11b07a0b7f09ab29fe98..6cce503511a92a9a4398c31bf20f29da59b506f7 100644 (file)
@@ -126,6 +126,10 @@ namespace Gtkmm2ext {
        class TearOff;
 }
 
+namespace Gtk {
+       class ProgressBar;
+}
+
 class ARDOUR_UI : public Gtkmm2ext::UI, public ARDOUR::SessionHandlePtr
 {
     private:
@@ -612,6 +616,8 @@ class ARDOUR_UI : public Gtkmm2ext::UI, public ARDOUR::SessionHandlePtr
        guint32  last_key_press_time;
 
        void snapshot_session (bool switch_to_it);
+       bool save_as_progress_update (float fraction, int64_t cnt, int64_t total, Gtk::Label* label, Gtk::ProgressBar* bar);
+       void save_session_as ();
        void rename_session ();
        void setup_order_hint (AddRouteDialog::InsertAt);
 
index ccd048d69d69286f12e343a322d0967c9c49db9c..7c3db7a7e3d6bab572dc6bf46c497e8dd250cb4e 100644 (file)
@@ -141,11 +141,15 @@ ARDOUR_UI::install_actions ()
                        hide_return (sigc::bind (sigc::mem_fun(*this, &ARDOUR_UI::export_video), false)));
        ActionManager::session_sensitive_actions.push_back (act);
 
-       act = ActionManager::register_action (main_actions, X_("Snapshot"), _("Snapshot..."), sigc::bind (sigc::mem_fun(*this, &ARDOUR_UI::snapshot_session), false));
+       act = ActionManager::register_action (main_actions, X_("SnapshotStay"), _("Snapshot (& keep working on current version) ..."), sigc::bind (sigc::mem_fun(*this, &ARDOUR_UI::snapshot_session), false));
        ActionManager::session_sensitive_actions.push_back (act);
        ActionManager::write_sensitive_actions.push_back (act);
 
-       act = ActionManager::register_action (main_actions, X_("SaveAs"), _("Save As..."), sigc::bind (sigc::mem_fun(*this, &ARDOUR_UI::snapshot_session), true));
+       act = ActionManager::register_action (main_actions, X_("SnapshotSwitch"), _("Snapshot (& switch to new version) ..."), sigc::bind (sigc::mem_fun(*this, &ARDOUR_UI::snapshot_session), true));
+       ActionManager::session_sensitive_actions.push_back (act);
+       ActionManager::write_sensitive_actions.push_back (act);
+
+       act = ActionManager::register_action (main_actions, X_("SaveAs"), _("Save As..."), sigc::mem_fun(*this, &ARDOUR_UI::save_session_as));
        ActionManager::session_sensitive_actions.push_back (act);
        ActionManager::write_sensitive_actions.push_back (act);
 
diff --git a/gtk2_ardour/save_as_dialog.cc b/gtk2_ardour/save_as_dialog.cc
new file mode 100644 (file)
index 0000000..504d479
--- /dev/null
@@ -0,0 +1,113 @@
+/*
+    Copyright (C) 2015 Paul Davis
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include <gtkmm/stock.h>
+
+#include "ardour/session.h"
+
+#include "save_as_dialog.h"
+
+#include "i18n.h"
+
+using namespace std;
+using namespace Gtk;
+using namespace ARDOUR;
+
+SaveAsDialog::SaveAsDialog ()
+       : ArdourDialog (_("Save As"))
+       , switch_to_button (_("Switch to newly-saved version"))
+       , copy_media_button (_("Copy media to new session"))
+       , copy_external_button (_("Copy external media into new session"))
+{
+       VBox* vbox = get_vbox();
+
+       vbox->set_spacing (6);
+       
+       HBox* hbox;
+       Label* label;
+
+       hbox = manage (new HBox);
+       hbox->set_spacing (6);
+       label = manage (new Label (_("Save as session name")));
+       hbox->pack_start (*label, false, false);
+       hbox->pack_start (new_name_entry, true, true);
+       vbox->pack_start (*hbox, false, false);
+
+       hbox = manage (new HBox);
+       hbox->set_spacing (6);
+       label = manage (new Label (_("Parent directory/folder")));
+       hbox->pack_start (*label, false, false);
+       hbox->pack_start (new_parent_folder_selector, true, true);
+       vbox->pack_start (*hbox, false, false);
+
+       vbox->pack_start (switch_to_button, false, false);
+       vbox->pack_start (copy_media_button, false, false);
+       vbox->pack_start (copy_external_button, false, false);
+
+       switch_to_button.set_active (true);
+       copy_media_button.set_active (true);
+       
+       vbox->show_all ();
+
+       add_button (Stock::CANCEL, RESPONSE_CANCEL);
+       add_button (Stock::OK, RESPONSE_OK);
+
+       new_parent_folder_selector.set_action (FILE_CHOOSER_ACTION_SELECT_FOLDER);
+       new_parent_folder_selector.set_current_folder (Glib::get_home_dir());
+       new_name_entry.signal_changed().connect (sigc::mem_fun (*this, &SaveAsDialog::name_entry_changed));
+       set_response_sensitive (RESPONSE_OK, false);
+}
+
+void
+SaveAsDialog::name_entry_changed ()
+{
+       if (!new_name_entry.get_text().empty()) {
+               set_response_sensitive (RESPONSE_OK);
+       }
+}
+
+string
+SaveAsDialog::new_parent_folder () const
+{
+       return new_parent_folder_selector.get_current_folder ();
+}
+
+string
+SaveAsDialog::new_name () const
+{
+       return new_name_entry.get_text ();
+}
+
+bool
+SaveAsDialog::switch_to () const
+{
+       return switch_to_button.get_active ();
+}
+
+bool
+SaveAsDialog::copy_media () const
+{
+       return copy_media_button.get_active ();
+}
+
+bool
+SaveAsDialog::copy_external () const
+{
+       return copy_external_button.get_active ();
+}
diff --git a/gtk2_ardour/save_as_dialog.h b/gtk2_ardour/save_as_dialog.h
new file mode 100644 (file)
index 0000000..140eb9f
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+    Copyright (C) 2015 Paul Davis
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#ifndef __ardour_gtk_save_as_dialog_h__
+#define __ardour_gtk_save_as_dialog_h__
+
+#include <gtkmm/entry.h>
+#include <gtkmm/checkbutton.h>
+#include <gtkmm/filechooserbutton.h>
+
+#include "ardour_dialog.h"
+
+class SaveAsDialog : public ArdourDialog
+{
+public:
+       SaveAsDialog ();
+
+       std::string new_parent_folder () const;
+       std::string new_name () const;
+       
+       bool switch_to () const;
+       bool copy_media () const;
+       bool copy_external () const;
+               
+private:
+       Gtk::CheckButton switch_to_button;
+       Gtk::CheckButton copy_media_button;
+       Gtk::CheckButton copy_external_button;
+       Gtk::FileChooserButton new_parent_folder_selector;
+       Gtk::Entry new_name_entry;
+
+       void name_entry_changed ();
+};
+
+#endif /* __ardour_gtk_tempo_dialog_h__ */
index c3e56559ee866421f93e3988dc2920e05c96446e..6f094daa73cb58723e293f98e193d9a12c9234c8 100644 (file)
@@ -199,6 +199,7 @@ gtk2_ardour_sources = [
         'route_time_axis.cc',
         'route_ui.cc',
         'ruler_dialog.cc',
+       'save_as_dialog.cc',
         'search_path_option.cc',
         'selection.cc',
         'selection_memento.cc',
index f12a67ded7fae8ad8c05a8e1e38cf16a38add86a..5e8e696bf9ebd3100cd9835edd69e3c11a37746b 100644 (file)
@@ -48,6 +48,7 @@ class LIBARDOUR_API CoreAudioSource : public AudioFileSource {
        static int get_soundfile_info (string path, SoundFileInfo& _info, string& error_msg);
 
   protected:
+       void close ();
        framecnt_t read_unlocked (Sample *dst, framepos_t start, framecnt_t cnt) const;
        framecnt_t write_unlocked (Sample *, framecnt_t) { return 0; }
 
index ac073d934ccc4dd38b0652bae43fb793bb55613a..0929c3303fa5947fc3b29fd203c0d73b17f02548 100644 (file)
@@ -46,8 +46,8 @@ class LIBARDOUR_API FileSource : virtual public Source {
 public:
        virtual ~FileSource ();
 
-       virtual const std::string& path() const { return _path; }
-
+       const std::string& path() const { return _path; }
+       
        virtual bool safe_file_extension (const std::string& path) const = 0;
 
        int  move_to_trash (const std::string& trash_dir_name);
@@ -89,9 +89,9 @@ public:
         */
        int rename (const std::string& name);
 
-       virtual void release_descriptor () {}
+       virtual void close () = 0;
 
-protected:
+  protected:
        FileSource (Session& session, DataType type,
                    const std::string& path,
                    const std::string& origin,
index 9967c9c75883b7746ad9a7ce811d560cc0662622..75b3bd2006c69e1035b859d7c12d5762b03a3f5a 100644 (file)
@@ -392,7 +392,33 @@ class LIBARDOUR_API Session : public PBD::StatefulDestructible, public PBD::Scop
        framecnt_t worst_input_latency ()  const { return _worst_input_latency; }
        framecnt_t worst_track_latency ()  const { return _worst_track_latency; }
        framecnt_t worst_playback_latency () const { return _worst_output_latency + _worst_track_latency; }
+       
+       struct SaveAs {
+               std::string new_parent_folder;  /* parent folder where new session folder will be created */
+               std::string new_name;           /* name of newly saved session */
+               bool        switch_to;     /* true if we should be working on newly saved session after save-as; false otherwise */
+               bool        copy_media;    /* true if media files (audio, media, etc) should be copied into newly saved session; false otherwise */
+               bool        copy_external; /* true if external media should be consolidated into the newly saved session; false otherwise */
+               
+               /* emitted as we make progress. 3 arguments passed to signal
+                * handler:
+                *
+                *  1: percentage complete measured as a fraction (0-1.0) of
+                *     total data copying done.
+                *  2: number of files copied so far
+                *  3: total number of files to copy
+                *
+                * Handler should return true for save-as to continue, or false
+                * to stop (and remove all evidence of partial save-as).
+                */
+               PBD::Signal3<bool,float,int64_t,int64_t> Progress;
+
+               /* if save_as() returns non-zero, this string will indicate the reason why.
+                */
+               std::string failure_message;
+       };
 
+       int save_as (SaveAs&);
        int save_state (std::string snapshot_name, bool pending = false, bool switch_to_snapshot = false);
        int restore_state (std::string snapshot_name);
        int save_template (std::string template_name);
@@ -401,7 +427,7 @@ class LIBARDOUR_API Session : public PBD::StatefulDestructible, public PBD::Scop
        void remove_state (std::string snapshot_name);
        void rename_state (std::string old_name, std::string new_name);
        void remove_pending_capture_state ();
-       int rename (const std::string&);
+       int rename (const std::string&, bool after_copy = false);
        bool get_nsm_state () const { return _under_nsm_control; }
        void set_nsm_state (bool state) { _under_nsm_control = state; }
        bool save_default_options ();
@@ -1437,8 +1463,10 @@ class LIBARDOUR_API Session : public PBD::StatefulDestructible, public PBD::Scop
        typedef std::map<PBD::ID,boost::shared_ptr<Source> > SourceMap;
 
   private:
+       void reset_write_sources (bool mark_write_complete, bool force = false);
        SourceMap sources;
 
+
   private:
        int load_sources (const XMLNode& node);
        XMLNode& get_sources_as_xml ();
@@ -1699,6 +1727,8 @@ class LIBARDOUR_API Session : public PBD::StatefulDestructible, public PBD::Scop
        void setup_click_state (const XMLNode*);
        void setup_bundles ();
        
+       void save_as_bring_callback (uint32_t, uint32_t, std::string);
+
        static int get_session_info_from_path (XMLTree& state_tree, const std::string& xmlpath);
 };
 
index cf6462247ae05ca1d07d8a7894e53bfddaf23fab..80f4b361b233517693003840722d45967ab46f86 100644 (file)
@@ -40,6 +40,7 @@ public:
        bool clamped_at_unity() const { return false; }
 
 protected:
+       void close() {}
        friend class SourceFactory;
 
        SilentFileSource (Session& s, const XMLNode& x, framecnt_t len, float srate)
index d088f2d867a3e8d6aa8606ada90d803b23d24629..35e0c892970da18c338c430ebfc048924f70b32c 100644 (file)
@@ -72,6 +72,7 @@ public:
        void prevent_deletion ();
 
   protected:
+       void close ();
        void set_path (const std::string& newpath);
        void flush_midi (const Lock& lock);
 
index 11ec6fe33c6cf800737843257b2698aa9fa94aeb..5a4a11515c16d499f1bde9daddb41d4c9316d6cd 100644 (file)
@@ -75,6 +75,8 @@ class LIBARDOUR_API SndFileSource : public AudioFileSource {
        static int get_soundfile_info (const std::string& path, SoundFileInfo& _info, std::string& error_msg);
 
   protected:
+       void close ();
+
        void set_path (const std::string& p);
        void set_header_timeline_position ();
 
index 78564c8b8893a7562ce2c1cb72c189c9f8e6b923..073329cc29474fa51b440b5a4b428a7ee02aeb0d 100644 (file)
@@ -52,6 +52,7 @@ public:
        bool clamped_at_unity() const { return false; }
 
 protected:
+       void close ();
        framecnt_t read_unlocked (Sample *dst, framepos_t start, framecnt_t cnt) const;
        framecnt_t write_unlocked (Sample */*dst*/, framecnt_t /*cnt*/) { return 0; }
 
index 010905d120cc371beaa8437b0e6613a8674bed8c..33753f481f2a22a960168020e0704ccb78a3f248 100644 (file)
@@ -104,6 +104,12 @@ CoreAudioSource::~CoreAudioSource ()
 {
 }
 
+void
+CoreAudioSource::close ()
+{
+       af.Close ();
+}
+
 int
 CoreAudioSource::safe_read (Sample* dst, framepos_t start, framecnt_t cnt, AudioBufferList& abl) const
 {
index 0aec49e6bdd74899dbfafa28d903d0aa6047fc75..507f0df49c84647144cc616cfa4b1c8b4ec430d8 100644 (file)
@@ -337,7 +337,6 @@ FileSource::find (Session& s, DataType type, const string& path, bool must_exist
         }
 
         found_path = keeppath;
-
         ret = true;
 
   out:
@@ -545,6 +544,7 @@ FileSource::set_within_session_from_path (const std::string& path)
 void
 FileSource::set_path (const std::string& newpath)
 {
+       close ();
         _path = newpath;
        set_within_session_from_path (newpath);
        if (_within_session) {
index 3ed028ef918d2a09719a8953993d5cacb7efc550..90a15c9e78defd73ae21151d2bbcc754aa673812 100644 (file)
@@ -417,14 +417,14 @@ write_midi_data_to_new_files (Evoral::SMF* source, ImportStatus& status,
                                        break;
                                }
                        } else {
-                               info << string_compose (_("Track %1 of %2 contained no usable MIDI data"), i, source->file_path()) << endmsg;
+                               info << string_compose (_("Track %1 of %2 contained no usable MIDI data"), i, source->num_tracks()) << endmsg;
                        }
 
                        ++s; // next source
                }
 
-       } catch (...) {
-               error << string_compose (_("MIDI file %1 was not readable (no reason available)"), source->file_path()) << endmsg;
+       } catch (exception& e) {
+               error << string_compose (_("MIDI file could not be written (best guess: %1)"), e.what()) << endmsg;
        }
 
        if (buf) {
index 9d0be414da7edee302ed307da83a435cf849713e..b6536ebec968bd55379c675ea7c75270cfeb56f8 100644 (file)
@@ -176,6 +176,7 @@ SessionDirectory::sub_directories () const
 
        tmp_paths.push_back (sound_path ());
        tmp_paths.push_back (midi_path ());
+       tmp_paths.push_back (video_path ());
        tmp_paths.push_back (peak_path ());
        tmp_paths.push_back (dead_path ());
        tmp_paths.push_back (export_path ());
index 3f1fd7bf7c5141b11df3dd7965cdc4d1f183a801..32b6e28c47e00fe82f283191122432f02a0e6bf3 100644 (file)
@@ -54,6 +54,7 @@
 
 #include <glibmm.h>
 #include <glibmm/threads.h>
+#include <glibmm/fileutils.h>
 
 #include <boost/algorithm/string.hpp>
 
@@ -139,12 +140,6 @@ Session::pre_engine_init (string fullpath)
 
        _path = canonical_path(fullpath);
 
-       /* we require _path to end with a dir separator */
-
-       if (_path[_path.length()-1] != G_DIR_SEPARATOR) {
-               _path += G_DIR_SEPARATOR;
-       }
-
        /* is it new ? */
 
        _is_new = !Glib::file_test (_path, Glib::FileTest (G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR));
@@ -731,8 +726,8 @@ Session::save_state (string snapshot_name, bool pending, bool switch_to_snapshot
        std::string tmp_path(_session_dir->root_path());
        tmp_path = Glib::build_filename (tmp_path, legalize_for_path (snapshot_name) + temp_suffix);
 
-       // cerr << "actually writing state to " << xml_path << endl;
-
+       cerr << "actually writing state to " << tmp_path << endl;
+       
        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) {
@@ -743,6 +738,8 @@ Session::save_state (string snapshot_name, bool pending, bool switch_to_snapshot
 
        } else {
 
+               cerr << "renaming state to " << xml_path << endl;
+               
                if (::g_rename (tmp_path.c_str(), xml_path.c_str()) != 0) {
                        error << string_compose (_("could not rename temporary session file %1 to %2 (%3)"),
                                        tmp_path, xml_path, g_strerror(errno)) << endmsg;
@@ -1852,6 +1849,22 @@ Session::get_sources_as_xml ()
        return *node;
 }
 
+void
+Session::reset_write_sources (bool mark_write_complete, bool force)
+{
+    boost::shared_ptr<RouteList> rl = routes.reader();
+    for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) {
+        boost::shared_ptr<Track> tr = boost::dynamic_pointer_cast<Track> (*i);
+        if (tr) {
+                       
+                       // block state saving
+                       _state_of_the_state = StateOfTheState (_state_of_the_state|InCleanup);
+                       tr->reset_write_sources(mark_write_complete, force);
+                       _state_of_the_state = StateOfTheState (_state_of_the_state & ~InCleanup);
+        }
+    }
+}
+
 int
 Session::load_sources (const XMLNode& node)
 {
@@ -3614,10 +3627,10 @@ Session::solo_cut_control() const
 }
 
 int
-Session::rename (const std::string& new_name)
+Session::rename (const std::string& new_name, bool after_copy)
 {
        string legal_name = legalize_for_path (new_name);
-       string newpath;
+       string new_path;
        string oldstr;
        string newstr;
        bool first = true;
@@ -3649,100 +3662,120 @@ Session::rename (const std::string& new_name)
         * already exist ...
         */
 
-       vector<space_and_path> new_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 (!after_copy) {
 
-               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);
+               for (vector<space_and_path>::const_iterator i = session_dirs.begin(); i != session_dirs.end(); ++i) {
+                       
+                       if (first) {
+                               /* primary session directory */
+                               newstr = _path;
+                               first = false;
+                       } else {
+                               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);
+                               }
 
-               newstr = Glib::build_filename (base, legal_name);
-               
-               if (Glib::file_test (newstr, Glib::FILE_TEST_EXISTS)) {
-                       return -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;
+                       }
                }
-
-               space_and_path sp;
-               sp.path = newstr;
-               sp.blocks = 0; // not needed
-               new_session_dirs.push_back(sp);
        }
 
        /* Session dirs */
 
-       for (vector<space_and_path>::const_iterator i = session_dirs.begin(); i != session_dirs.end(); ++i) {
+       first = false;
+       
+       for (vector<space_and_path>::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 (first) {
+                       newstr = _path;
+               } else {
+                       string base = Glib::path_get_dirname (oldstr);
+                       newstr = Glib::build_filename (base, legal_name);
+               }
 
-               if (::g_rename (oldstr.c_str(), newstr.c_str()) != 0) {
-                       error << string_compose (_("renaming %1 as %2 failed (%3)"), oldstr, newstr, g_strerror (errno)) << endmsg;
-                       return 1;
+               if (!after_copy) {
+                       cerr << "Rename " << oldstr << " => " << newstr << endl;                
+                       if (::g_rename (oldstr.c_str(), newstr.c_str()) != 0) {
+                               error << string_compose (_("renaming %s as %2 failed (%3)"), oldstr, newstr, g_strerror (errno)) << endmsg;
+                               return 1;
+                       }
                }
 
+               /* Reset path in "session dirs" */
+               
+               (*i).path = newstr;
+               (*i).blocks = 0;
+               
+               /* reset primary SessionDirectory object */
+               
                if (first) {
                        (*_session_dir) = newstr;
-                       newpath = newstr;
-                       first = 1;
+                       new_path = newstr;
+                       first = false;
                }
 
-               /* directory below interchange */
+               /* now rename directory below session_dir/interchange */
 
-               v.push_back (newstr);
+               string old_interchange_dir;
+               string new_interchange_dir;
+
+               /* use newstr here because we renamed the path that used to be oldstr to newstr above */                
+               
+               v.push_back (newstr); 
                v.push_back (interchange_dir_name);
-               v.push_back (p);
+               v.push_back (Glib::path_get_basename (oldstr));
 
-               oldstr = Glib::build_filename (v);
+               old_interchange_dir = 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;
+               new_interchange_dir = Glib::build_filename (v);
                
-               if (::g_rename (oldstr.c_str(), newstr.c_str()) != 0) {
-                       error << string_compose (_("renaming %1 as %2 failed (%3)"), oldstr, newstr, g_strerror (errno)) << endmsg;
+               cerr << "Rename " << old_interchange_dir << " => " << new_interchange_dir << endl;
+               
+               if (::g_rename (old_interchange_dir.c_str(), new_interchange_dir.c_str()) != 0) {
+                       error << string_compose (_("renaming %s as %2 failed (%3)"),
+                                                old_interchange_dir, new_interchange_dir,
+                                                g_strerror (errno))
+                             << endmsg;
                        return 1;
                }
        }
 
-       session_dirs = new_session_dirs;
-
        /* state file */
        
-       oldstr = Glib::build_filename (newpath, _current_snapshot_name) + statefile_suffix;
-       newstr= Glib::build_filename (newpath, legal_name) + statefile_suffix;
+       oldstr = Glib::build_filename (new_path, _current_snapshot_name) + statefile_suffix;
+       newstr= Glib::build_filename (new_path, legal_name) + statefile_suffix;
        
        cerr << "Rename " << oldstr << " => " << newstr << endl;                
 
@@ -3752,12 +3785,11 @@ Session::rename (const std::string& new_name)
        }
 
        /* history file */
-
        
-       oldstr = Glib::build_filename (newpath, _current_snapshot_name) + history_suffix;
+       oldstr = Glib::build_filename (new_path, _current_snapshot_name) + history_suffix;
 
        if (Glib::file_test (oldstr, Glib::FILE_TEST_EXISTS))  {
-               newstr = Glib::build_filename (newpath, legal_name) + history_suffix;
+               newstr = Glib::build_filename (new_path, legal_name) + history_suffix;
                
                cerr << "Rename " << oldstr << " => " << newstr << endl;                
                
@@ -3767,38 +3799,33 @@ Session::rename (const std::string& new_name)
                }
        }
 
-       /* update file source paths */
-       
-       for (SourceMap::iterator i = sources.begin(); i != sources.end(); ++i) {
-               boost::shared_ptr<FileSource> fs = boost::dynamic_pointer_cast<FileSource> (i->second);
-               if (fs) {
-                       string p = fs->path ();
-                       boost::replace_all (p, old_sources_root, _session_dir->sources_root());
-                       fs->set_path (p);
-                       SourceFactory::setup_peakfile(i->second, true);
+       if (!after_copy) {
+               /* remove old name from recent sessions */
+               remove_recent_sessions (_path);
+               _path = new_path;
+
+               /* 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);
+                               SourceFactory::setup_peakfile(i->second, true);
+                       }
                }
        }
 
-       /* remove old name from recent sessions */
-
-       remove_recent_sessions (_path);
-
-       _path = newpath;
        _current_snapshot_name = new_name;
        _name = new_name;
 
-       /* re-add directory separator - reverse hack to oldstr above */
-       if (_path[_path.length()-1] != G_DIR_SEPARATOR) {
-               _path += G_DIR_SEPARATOR;
-       }
-
        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);
@@ -3898,7 +3925,6 @@ Session::bring_all_sources_into_session (boost::function<void(uint32_t,uint32_t,
                        }
                        
                        if (fs->within_session()) {
-                               cerr << "skip " << fs->name() << endl;
                                continue;
                        }
                        
@@ -3933,9 +3959,14 @@ Session::bring_all_sources_into_session (boost::function<void(uint32_t,uint32_t,
                                break;
                                
                        case DataType::MIDI:
+                               /* XXX not implemented yet */
                                break;
                        }
                        
+                       if (new_path.empty()) {
+                               continue;
+                       }
+                       
                        cerr << "Move " << old_path << " => " << new_path << endl;
                        
                        if (!copy_file (old_path, new_path)) {
@@ -3959,3 +3990,314 @@ Session::bring_all_sources_into_session (boost::function<void(uint32_t,uint32_t,
 
        return ret;
 }
+
+static
+bool accept_all_files (string const &, void *)
+{
+       return true;
+}
+
+void
+Session::save_as_bring_callback (uint32_t,uint32_t,string)
+{
+       /* It would be good if this did something useful vis-a-vis save-as, but the arguments doesn't provide the correct information right now to do this.
+       */
+}
+
+static string
+make_new_media_path (string old_path, string new_session_folder, string new_session_path)
+{
+       /* typedir is the "midifiles" or "audiofiles" etc. part of the path. */
+
+       string typedir = Glib::path_get_basename (Glib::path_get_dirname (old_path));
+       vector<string> v;
+       v.push_back (new_session_folder); /* full path */
+       v.push_back (interchange_dir_name);
+       v.push_back (new_session_path);   /* just one directory/folder */
+       v.push_back (typedir);
+       v.push_back (Glib::path_get_basename (old_path));
+       
+       return Glib::build_filename (v);
+}
+
+int
+Session::save_as (SaveAs& saveas)
+{
+       vector<string> files;
+       string current_folder = Glib::path_get_dirname (_path);
+       string new_folder = legalize_for_path (saveas.new_name);
+       string to_dir = Glib::build_filename (saveas.new_parent_folder, new_folder);
+       int64_t total_bytes = 0;
+       int64_t copied = 0;
+       int64_t cnt = 0;
+       int64_t all = 0;
+       int32_t internal_file_cnt = 0;
+
+       vector<string> do_not_copy_extensions;
+       do_not_copy_extensions.push_back (statefile_suffix);
+       do_not_copy_extensions.push_back (pending_suffix);
+       do_not_copy_extensions.push_back (backup_suffix);
+       do_not_copy_extensions.push_back (temp_suffix);
+       do_not_copy_extensions.push_back (history_suffix);
+
+       /* get total size */
+
+       for (vector<space_and_path>::const_iterator sd = session_dirs.begin(); sd != session_dirs.end(); ++sd) {
+               
+               /* need to clear this because
+                * find_files_matching_filter() is cumulative
+                */
+               
+               files.clear ();
+               
+               find_files_matching_filter (files, (*sd).path, accept_all_files, 0, false, true, true);
+               
+               all += files.size();
+
+               for (vector<string>::iterator i = files.begin(); i != files.end(); ++i) {
+                       GStatBuf gsb;
+                       g_stat ((*i).c_str(), &gsb);
+                       total_bytes += gsb.st_size;
+               }
+       }
+
+       /* save old values so we can switch back if we are not switching to the new session */
+       
+       string old_path = _path;
+       string old_name = _name;
+       string old_snapshot = _current_snapshot_name;
+       string old_sd = _session_dir->root_path();
+       vector<string> old_search_path[DataType::num_types];
+       string old_config_search_path[DataType::num_types];
+
+       old_search_path[DataType::AUDIO] = source_search_path (DataType::AUDIO);
+       old_search_path[DataType::MIDI] = source_search_path (DataType::MIDI);
+       old_config_search_path[DataType::AUDIO]  = config.get_audio_search_path ();     
+       old_config_search_path[DataType::MIDI]  = config.get_midi_search_path ();       
+
+       /* switch session directory */
+       
+       (*_session_dir) = to_dir;
+
+       /* create new tree */
+       
+       if (!_session_dir->create()) {
+               saveas.failure_message = string_compose (_("Cannot create new session folder %1"), to_dir);
+               return -1;
+       }
+
+       try {
+               /* copy all media files. Find each location in
+                * session_dirs, and copy files from there to
+                * target.
+                */
+               
+               for (vector<space_and_path>::const_iterator sd = session_dirs.begin(); sd != session_dirs.end(); ++sd) {
+                       
+                       /* need to clear this because
+                        * find_files_matching_filter() is cumulative
+                        */
+                       
+                       files.clear ();
+                       
+                       const size_t prefix_len = (*sd).path.size();
+                       
+                       /* Work just on the files within this session dir */
+                       
+                       find_files_matching_filter (files, (*sd).path, accept_all_files, 0, false, true, true);
+                       
+                       /* 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
+                          implementing ::save_as().
+                       */
+                       
+                       for (vector<string>::iterator i = files.begin(); i != files.end(); ++i) {
+
+                               std::string from = *i;
+                               
+                               if ((*i).find (interchange_dir_name) != string::npos) {
+                                       
+                                       /* media file */
+
+                                       if (saveas.copy_media) {
+                                               
+                                               string to = make_new_media_path (*i, to_dir, new_folder);
+
+                                               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 {
+                                       
+                                       /* normal non-media file. Don't copy state, history, etc.
+                                        */
+                                       
+                                       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())) {
+                                                       /* end of filename matches extension, do not copy file */
+                                                       do_copy = false;
+                                                       break;
+                                               } 
+                                       }
+                                       
+                                       if (do_copy) {
+                                               string to = Glib::build_filename (to_dir, (*i).substr (prefix_len));
+                                               
+                                               if (g_mkdir_with_parents (Glib::path_get_dirname (to).c_str(), 0755)) {
+                                                       throw Glib::FileError (Glib::FileError::IO_ERROR, "cannot create required directory");
+                                               }
+                                               
+                                               if (!copy_file (from, to)) {
+                                                       throw Glib::FileError (Glib::FileError::IO_ERROR, "copy failed");
+                                               }
+                                       }
+                               }
+                               
+                               /* measure file size even if we're not going to copy so that our Progress
+                                  signals are correct, since we included these do-not-copy files
+                                  in the computation of the total size and file count.
+                               */
+                               
+                               GStatBuf gsb;
+                               g_stat ((*i).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 (!keep_going) {
+                                       throw Glib::FileError (Glib::FileError::FAILED, "copy cancelled");
+                               }
+                       }
+
+               }
+
+               _path = to_dir;
+               _current_snapshot_name = saveas.new_name;
+               _name = saveas.new_name;
+
+               if (!saveas.copy_media) {
+
+                       /* reset search paths of the new session (which we're pretending to be right now) to
+                          include the original session search path, so we can still find all audio.
+                       */
+
+                       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);
+                               }
+
+                               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);
+                               }
+                       }
+               }
+               
+               bool was_dirty = dirty ();
+
+               save_state ("", false, false);
+               save_default_options ();
+               
+               if (saveas.copy_media && saveas.copy_external) {
+                       if (bring_all_sources_into_session (boost::bind (&Session::save_as_bring_callback, this, _1, _2, _3))) {
+                               throw Glib::FileError (Glib::FileError::NO_SPACE_LEFT, "consolidate failed");
+                       }
+               }
+
+               if (!saveas.switch_to) {
+
+                       /* switch back to the way things were */
+
+                       _path = old_path;
+                       _name = old_name;
+                       _current_snapshot_name = old_snapshot;
+
+                       (*_session_dir) = old_sd;
+
+                       if (was_dirty) {
+                               set_dirty ();
+                       }
+
+                       if (internal_file_cnt) {
+                               /* reset these to their original values */
+                               config.set_audio_search_path (old_config_search_path[DataType::AUDIO]);
+                               config.set_midi_search_path (old_config_search_path[DataType::MIDI]);
+                       }
+                       
+               } else {
+
+                       /* prune session dirs, and update disk space statistics
+                        */
+
+                       space_and_path sp;
+                       sp.path = _path;
+                       session_dirs.clear ();
+                       session_dirs.push_back (sp);
+                       refresh_disk_space ();
+
+                       /* ensure that all existing tracks reset their current capture source paths 
+                        */
+                       reset_write_sources (true, true);
+
+                       /* the copying above was based on actually discovering files, not just iterating over the sources list.
+                          But if we're going to switch to the new (copied) session, we need to change the paths in the sources also.
+                       */
+
+                       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) {
+                                       continue;
+                               }
+
+                               if (fs->within_session()) {
+                                       string newpath = make_new_media_path (fs->path(), to_dir, new_folder);
+                                       fs->set_path (newpath);
+                               }
+                       }
+               }
+
+       } catch (Glib::FileError& e) {
+
+               saveas.failure_message = e.what();
+               
+               /* recursively remove all the directories */
+               
+               remove_directory (to_dir);
+               
+               /* return error */
+               
+               return -1;
+
+       } catch (...) {
+
+               saveas.failure_message = _("unknown reason");
+               
+               /* recursively remove all the directories */
+               
+               remove_directory (to_dir);
+               
+               /* return error */
+               
+               return -1;
+       }
+       
+       return 0;
+}
index d5c89b5ee97bd0bbfe9fb3535ad389530e755699..cc547ed9f5bd88cdd255acc30cee4e8ad9599eb5 100644 (file)
@@ -202,6 +202,12 @@ SMFSource::open_for_write ()
        return 0;
 }
 
+void
+SMFSource::close ()
+{
+       /* nothing to do: file descriptor is never kept open */
+}
+
 /** All stamps in audio frames */
 framecnt_t
 SMFSource::read_unlocked (const Lock&                    lock,
@@ -546,7 +552,7 @@ SMFSource::mark_midi_streaming_write_completed (const Lock& lm, Evoral::Sequence
                _model->set_edited(false);
        }
 
-       Evoral::SMF::end_write ();
+       Evoral::SMF::end_write (_path);
 
        /* data in the file now, not removable */
 
@@ -726,7 +732,7 @@ SMFSource::flush_midi (const Lock& lock)
 
        ensure_disk_file (lock);
 
-       Evoral::SMF::end_write ();
+       Evoral::SMF::end_write (_path);
        /* data in the file means its no longer removable */
        mark_nonremovable ();
 
@@ -737,7 +743,6 @@ void
 SMFSource::set_path (const string& p)
 {
        FileSource::set_path (p);
-       SMF::set_path (_path);
 }
 
 /** Ensure that this source has some file on disk, even if it's just a SMF header */
index cc6cfdb4befc865ad775723f26ed583f0c15d07d..5acfe7b119246983cce6e02fe7856333853a29a4 100644 (file)
@@ -180,7 +180,7 @@ SndFileSource::SndFileSource (Session& s, const string& path, const string& orig
                        throw failed_constructor();
                }
        } else {
-               /* normal mode: do not open the file here - do that in write_unlocked() as needed
+               /* normal mode: do not open the file here - do that in {read,write}_unlocked() as needed
                 */
        }
 }
@@ -230,6 +230,15 @@ SndFileSource::init_sndfile ()
        AudioFileSource::HeaderPositionOffsetChanged.connect_same_thread (header_position_connection, boost::bind (&SndFileSource::handle_header_position_change, this));
 }
 
+void
+SndFileSource::close ()
+{
+       if (_sndfile) {
+               sf_close (_sndfile);
+               _sndfile = 0;
+       }
+}
+
 int
 SndFileSource::open ()
 {
@@ -334,10 +343,7 @@ SndFileSource::open ()
 
 SndFileSource::~SndFileSource ()
 {
-       if (_sndfile) {
-               sf_close (_sndfile);
-               _sndfile = 0;
-       }
+       close ();
        delete _broadcast_info;
        delete [] xfade_buf;
 }
@@ -364,10 +370,10 @@ SndFileSource::read_unlocked (Sample *dst, framepos_t start, framecnt_t cnt) con
                 return cnt;
         }
 
-       if (_sndfile == 0) {
-               error << string_compose (_("could not allocate file %1 for reading."), _path) << endmsg;
+        if (const_cast<SndFileSource*>(this)->open()) {
+               error << string_compose (_("could not open file %1 for reading."), _path) << endmsg;
                return 0;
-       }
+        }
 
        if (start > _length) {
 
index e54afbfaba738e64c50381109d7a9998bf639a67..276a31e46b5343746844f7fa8cf60f0a8d55bbbb 100644 (file)
@@ -84,6 +84,15 @@ SrcFileSource::~SrcFileSource ()
        delete [] _src_buffer;
 }
 
+void
+SrcFileSource::close ()
+{
+       boost::shared_ptr<FileSource> fs = boost::dynamic_pointer_cast<FileSource> (_source);
+       if (fs) {
+               fs->close ();
+       }
+}
+
 framecnt_t
 SrcFileSource::read_unlocked (Sample *dst, framepos_t start, framecnt_t cnt) const
 {
index 9d43426a8f66f873028576dc3e04e2f6595e6d15..fd7263b5c44f0009d5885b48b78c29b28fd661b9 100644 (file)
@@ -58,8 +58,6 @@ public:
        int  create(const std::string& path, int track=1, uint16_t ppqn=19200) THROW_FILE_ERROR;
        void close() THROW_FILE_ERROR;
 
-       const std::string& file_path() const { return _file_path; };
-
        void seek_to_start() const;
        int  seek_to_track(int track);
 
@@ -71,17 +69,13 @@ public:
 
        void begin_write();
        void append_event_delta(uint32_t delta_t, uint32_t size, const uint8_t* buf, event_id_t note_id);
-       void end_write() THROW_FILE_ERROR;
+       void end_write(std::string const &) THROW_FILE_ERROR;
 
        void flush() {};
 
        double round_to_file_precision (double val) const;
 
-protected:
-       void set_path (const std::string& p);
-
 private:
-       std::string  _file_path;
        smf_t*       _smf;
        smf_track_t* _smf_track;
        bool         _empty; ///< true iff file contains(non-empty) events
index 0a5bf589f500811eab279081a274794fc9c6f255..b6ec8ecba73bb2e6085f8cf500b2ba42ff853aac 100644 (file)
@@ -108,9 +108,7 @@ SMF::open(const std::string& path, int track) THROW_FILE_ERROR
                smf_delete(_smf);
        }
 
-       _file_path = path;
-
-       FILE* f = fopen(_file_path.c_str(), "r");
+       FILE* f = fopen(path.c_str(), "r");
        if (f == 0) {
                return -1;
        } else if ((_smf = smf_load(f)) == 0) {
@@ -151,8 +149,6 @@ SMF::create(const std::string& path, int track, uint16_t ppqn) THROW_FILE_ERROR
                smf_delete(_smf);
        }
 
-       _file_path = path;
-
        _smf = smf_new();
 
        if (_smf == NULL) {
@@ -180,7 +176,7 @@ SMF::create(const std::string& path, int track, uint16_t ppqn) THROW_FILE_ERROR
        {
                /* put a stub file on disk */
 
-               FILE* f = fopen (_file_path.c_str(), "w+");
+               FILE* f = fopen (path.c_str(), "w+");
                if (f == 0) {
                        return -1;
                }
@@ -401,17 +397,22 @@ SMF::begin_write()
 }
 
 void
-SMF::end_write() THROW_FILE_ERROR
+SMF::end_write(string const & path) THROW_FILE_ERROR
 {
        Glib::Threads::Mutex::Lock lm (_smf_lock);
-       FILE* f = fopen (_file_path.c_str(), "w+");
+
+       if (!_smf) {
+               return;
+       }
+
+       FILE* f = fopen (path.c_str(), "w+");
        if (f == 0) {
-               throw FileError (_file_path);
+               throw FileError (path);
        }
 
        if (smf_save(_smf, f) != 0) {
                fclose(f);
-               throw FileError (_file_path);
+               throw FileError (path);
        }
 
        fclose(f);
@@ -425,10 +426,4 @@ SMF::round_to_file_precision (double val) const
        return round (val * div) / div;
 }
 
-void
-SMF::set_path (const std::string& p)
-{
-       _file_path = p;
-}
-
 } // namespace Evoral