when moving tempo and metric sections around (or adding new ones), prevent the existe...
[ardour.git] / gtk2_ardour / editor_audio_import.cc
index 9eb448d998e0405aefa7145114ee1dd772122d19..6200a6f98ff42f6fbb0b8f3dce3fe72345535057 100644 (file)
@@ -24,8 +24,6 @@
 #include <unistd.h>
 #include <algorithm>
 
-#include <glibmm/ustring.h>
-
 #include <sndfile.h>
 
 #include "pbd/pthread_utils.h"
@@ -50,6 +48,7 @@
 #include "ardour/source_factory.h"
 #include "ardour/session.h"
 #include "ardour/smf_source.h"
+#include "ardour/operations.h"
 #include "pbd/memento_command.h"
 
 #include "ardour_ui.h"
@@ -62,6 +61,8 @@
 #include "utils.h"
 #include "gui_thread.h"
 #include "interthread_progress_window.h"
+#include "mouse_cursors.h"
+#include "editor_cursors.h"
 
 #include "i18n.h"
 
@@ -71,7 +72,7 @@ using namespace PBD;
 using namespace Gtk;
 using namespace Gtkmm2ext;
 using namespace Editing;
-using Glib::ustring;
+using std::string;
 
 /* Functions supporting the incorporation of external (non-captured) audio material into ardour */
 
@@ -83,7 +84,7 @@ Editor::add_external_audio_action (ImportMode mode_hint)
                msg.run ();
                return;
        }
-       
+
        if (sfbrowser == 0) {
                sfbrowser = new SoundFileOmega (*this, _("Add Existing Media"), _session, 0, true, mode_hint);
        } else {
@@ -97,7 +98,8 @@ void
 Editor::external_audio_dialog ()
 {
        vector<string> paths;
-       uint32_t track_cnt;
+       uint32_t audio_track_cnt;
+       uint32_t midi_track_cnt;
 
        if (_session == 0) {
                MessageDialog msg (_("You can't import or embed an audiofile until you have a session loaded."));
@@ -105,22 +107,32 @@ Editor::external_audio_dialog ()
                return;
        }
 
-       track_cnt = 0;
+       audio_track_cnt = 0;
+       midi_track_cnt = 0;
 
        for (TrackSelection::iterator x = selection->tracks.begin(); x != selection->tracks.end(); ++x) {
                AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*>(*x);
 
-               if (!atv) {
-                       continue;
-               } else if (atv->is_audio_track()) {
-                       track_cnt++;
+               if (atv) {
+                       if (atv->is_audio_track()) {
+                               audio_track_cnt++;
+                       } 
+
+               } else {
+                       MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*>(*x);
+
+                       if (mtv) {
+                               if (mtv->is_midi_track()) {
+                                       midi_track_cnt++;
+                               }
+                       }
                }
        }
 
        if (sfbrowser == 0) {
-               sfbrowser = new SoundFileOmega (*this, _("Add Existing Media"), _session, track_cnt, true);
+               sfbrowser = new SoundFileOmega (*this, _("Add Existing Media"), _session, audio_track_cnt, midi_track_cnt, true);
        } else {
-               sfbrowser->reset (track_cnt);
+               sfbrowser->reset (audio_track_cnt, midi_track_cnt);
        }
 
        sfbrowser->show_all ();
@@ -150,15 +162,15 @@ Editor::external_audio_dialog ()
 
                /* lets do it */
 
-               vector<ustring> upaths = sfbrowser->get_paths ();
-                for (vector<ustring>::iterator x = upaths.begin(); x != upaths.end(); ++x) {
+               vector<string> upaths = sfbrowser->get_paths ();
+                for (vector<string>::iterator x = upaths.begin(); x != upaths.end(); ++x) {
                         paths.push_back (*x);
                 }
-                
+
                ImportPosition pos = sfbrowser->get_position ();
                ImportMode mode = sfbrowser->get_mode ();
                ImportDisposition chns = sfbrowser->get_channel_disposition ();
-               nframes64_t where;
+               framepos_t where;
 
                switch (pos) {
                        case ImportAtEditPoint:
@@ -220,19 +232,16 @@ Editor::check_whether_and_how_to_import(string path, bool all_or_nothing)
        string wave_name (Glib::path_get_basename(path));
 
        SourceMap all_sources = _session->get_sources();
-       bool wave_name_exists = false;
+       bool already_exists = false;
+       uint32_t existing;
 
-       for (SourceMap::iterator i = all_sources.begin(); i != all_sources.end(); ++i) {
-               string tmp (Glib::path_get_basename (i->second->path()));
-               if (tmp == wave_name) {
-                       wave_name_exists = true;
-                       break;
-               }
+       if ((existing = _session->count_sources_by_origin (path)) > 0) {
+               already_exists = true;
        }
 
        int function = 1;
 
-       if (wave_name_exists) {
+       if (already_exists) {
                string message;
                if (all_or_nothing) {
                        // updating is still disabled
@@ -327,19 +336,22 @@ Editor::get_nth_selected_midi_track (int nth) const
 }
 
 void
-Editor::do_import (vector<string> paths, ImportDisposition chns, ImportMode mode, SrcQuality quality, nframes64_t& pos)
+Editor::do_import (vector<string> paths, ImportDisposition chns, ImportMode mode, SrcQuality quality, framepos_t& pos)
 {
        boost::shared_ptr<Track> track;
        vector<string> to_import;
        int nth = 0;
-        bool use_timestamp = (pos == -1);
+       bool use_timestamp = (pos == -1);
 
        current_interthread_info = &import_status;
        import_status.current = 1;
        import_status.total = paths.size ();
+       import_status.all_done = false;
 
        ImportProgressWindow ipw (&import_status, _("Import"), _("Cancel Import"));
 
+       bool ok = true;
+
        if (chns == Editing::ImportMergeFiles) {
 
                /* create 1 region from all paths, add to 1 track,
@@ -355,19 +367,21 @@ Editor::do_import (vector<string> paths, ImportDisposition chns, ImportMode mode
                        }
                }
 
-               if (!cancel) {
-                       import_sndfiles (paths, mode, quality, pos, 1, 1, track, false);
+               if (cancel) {
+                       ok = false;
+               } else {
+                       ipw.show ();
+                       ok = (import_sndfiles (paths, mode, quality, pos, 1, 1, track, false) == 0);
                }
 
        } else {
 
-                bool replace = false;
-               bool ok = true;
+               bool replace = false;
 
                for (vector<string>::iterator a = paths.begin(); a != paths.end(); ++a) {
 
-                        const int check = check_whether_and_how_to_import (*a, true);
-                        
+                       const int check = check_whether_and_how_to_import (*a, true);
+
                        switch (check) {
                        case 2:
                                // user said skip
@@ -384,51 +398,59 @@ Editor::do_import (vector<string> paths, ImportDisposition chns, ImportMode mode
                                /* NOTREACHED*/
                        }
 
-                        /* have to reset this for every file we handle */
-                        
-                        if (use_timestamp) {
-                                pos = -1;
-                        }
+                       /* have to reset this for every file we handle */
 
-                        switch (chns) {
-                        case Editing::ImportDistinctFiles:
-                                
-                                to_import.clear ();
-                                to_import.push_back (*a);
-                                
-                                if (mode == Editing::ImportToTrack) {
-                                        track = get_nth_selected_audio_track (nth++);
-                                }
-                       
-                                ok = (import_sndfiles (to_import, mode, quality, pos, 1, -1, track, replace) == 0);
-                                break;
-                               
-                        case Editing::ImportDistinctChannels:
-                               
-                                to_import.clear ();
-                                to_import.push_back (*a);
-                               
-                                ok = (import_sndfiles (to_import, mode, quality, pos, -1, -1, track, replace) == 0);
-                                break;
-                               
-                        case Editing::ImportSerializeFiles:
-                               
-                                to_import.clear ();
-                                to_import.push_back (*a);
-
-                                ok = (import_sndfiles (to_import, mode, quality, pos, 1, 1, track, replace) == 0);
-                                break;
-
-                        case Editing::ImportMergeFiles:
-                                // Not entered, handled in earlier if() branch
-                                break;
-                        }
-                }
+                       if (use_timestamp) {
+                               pos = -1;
+                       }
+
+                       ipw.show ();
+
+                       switch (chns) {
+                       case Editing::ImportDistinctFiles:
+
+                               to_import.clear ();
+                               to_import.push_back (*a);
+
+                               if (mode == Editing::ImportToTrack) {
+                                       track = get_nth_selected_audio_track (nth++);
+                               }
+
+                               ok = (import_sndfiles (to_import, mode, quality, pos, 1, -1, track, replace) == 0);
+                               break;
+
+                       case Editing::ImportDistinctChannels:
+
+                               to_import.clear ();
+                               to_import.push_back (*a);
+
+                               ok = (import_sndfiles (to_import, mode, quality, pos, -1, -1, track, replace) == 0);
+                               break;
+
+                       case Editing::ImportSerializeFiles:
+
+                               to_import.clear ();
+                               to_import.push_back (*a);
+
+                               ok = (import_sndfiles (to_import, mode, quality, pos, 1, 1, track, replace) == 0);
+                               break;
+
+                       case Editing::ImportMergeFiles:
+                               // Not entered, handled in earlier if() branch
+                               break;
+                       }
+               }
        }
+
+       if (ok) {
+               _session->save_state ("");
+       }
+
+       import_status.all_done = true;
 }
 
 void
-Editor::do_embed (vector<string> paths, ImportDisposition chns, ImportMode mode, nframes64_t& pos)
+Editor::do_embed (vector<string> paths, ImportDisposition chns, ImportMode mode, framepos_t& pos)
 {
        boost::shared_ptr<Track> track;
        bool check_sample_rate = true;
@@ -436,11 +458,17 @@ Editor::do_embed (vector<string> paths, ImportDisposition chns, ImportMode mode,
        vector<string> to_embed;
        bool multi = paths.size() > 1;
        int nth = 0;
+       bool use_timestamp = (pos == -1);
 
        switch (chns) {
        case Editing::ImportDistinctFiles:
                for (vector<string>::iterator a = paths.begin(); a != paths.end(); ++a) {
 
+                       /* have to reset this for every file we handle */
+                       if (use_timestamp) {
+                               pos = -1;
+                       }
+
                        to_embed.clear ();
                        to_embed.push_back (*a);
 
@@ -457,6 +485,11 @@ Editor::do_embed (vector<string> paths, ImportDisposition chns, ImportMode mode,
        case Editing::ImportDistinctChannels:
                for (vector<string>::iterator a = paths.begin(); a != paths.end(); ++a) {
 
+                       /* have to reset this for every file we handle */
+                       if (use_timestamp) {
+                               pos = -1;
+                       }
+
                        to_embed.clear ();
                        to_embed.push_back (*a);
 
@@ -470,11 +503,16 @@ Editor::do_embed (vector<string> paths, ImportDisposition chns, ImportMode mode,
                if (embed_sndfiles (paths, multi, check_sample_rate, mode, pos, 1, 1, track) < -1) {
                        goto out;
                }
-                break;
+               break;
 
        case Editing::ImportSerializeFiles:
                for (vector<string>::iterator a = paths.begin(); a != paths.end(); ++a) {
 
+                       /* have to reset this for every file we handle */
+                       if (use_timestamp) {
+                               pos = -1;
+                       }
+
                        to_embed.clear ();
                        to_embed.push_back (*a);
 
@@ -494,14 +532,13 @@ Editor::do_embed (vector<string> paths, ImportDisposition chns, ImportMode mode,
 }
 
 int
-Editor::import_sndfiles (vector<string> paths, ImportMode mode, SrcQuality quality, nframes64_t& pos,
+Editor::import_sndfiles (vector<string> paths, ImportMode mode, SrcQuality quality, framepos_t& pos,
                         int target_regions, int target_tracks, boost::shared_ptr<Track>& track, bool replace)
 {
        import_status.paths = paths;
        import_status.done = false;
        import_status.cancel = false;
        import_status.freeze = false;
-       import_status.done = 0.0;
        import_status.quality = quality;
        import_status.replace_existing_source = replace;
 
@@ -512,7 +549,7 @@ Editor::import_sndfiles (vector<string> paths, ImportMode mode, SrcQuality quali
        import_status.track = track;
        import_status.replace = replace;
 
-       track_canvas->get_window()->set_cursor (Gdk::Cursor (Gdk::WATCH));
+       set_canvas_cursor (_cursors->wait);
        gdk_flush ();
 
        /* start import thread for this spec. this will ultimately call Session::import_audiofiles()
@@ -529,30 +566,31 @@ Editor::import_sndfiles (vector<string> paths, ImportMode mode, SrcQuality quali
 
        import_status.done = true;
 
+       int result = -1;
+
        if (!import_status.cancel && !import_status.sources.empty()) {
-               if (add_sources (import_status.paths,
-                                import_status.sources,
-                                import_status.pos,
-                                import_status.mode,
-                                import_status.target_regions,
-                                import_status.target_tracks,
-                                track, false) == 0) {
-                       _session->save_state ("");
-               }
+               result = add_sources (
+                       import_status.paths,
+                       import_status.sources,
+                       import_status.pos,
+                       import_status.mode,
+                       import_status.target_regions,
+                       import_status.target_tracks,
+                       track, false
+                       );
 
                /* update position from results */
 
                pos = import_status.pos;
        }
 
-
-       track_canvas->get_window()->set_cursor (*current_canvas_cursor);
-       return 0;
+       set_canvas_cursor (current_canvas_cursor);
+       return result;
 }
 
 int
 Editor::embed_sndfiles (vector<string> paths, bool multifile,
-                       bool& check_sample_rate, ImportMode mode, nframes64_t& pos, int target_regions, int target_tracks,
+                       bool& check_sample_rate, ImportMode mode, framepos_t& pos, int target_regions, int target_tracks,
                        boost::shared_ptr<Track>& track)
 {
        boost::shared_ptr<AudioFileSource> source;
@@ -560,56 +598,17 @@ Editor::embed_sndfiles (vector<string> paths, bool multifile,
        string linked_path;
        SoundFileInfo finfo;
        int ret = 0;
-       string path_to_use;
 
-       track_canvas->get_window()->set_cursor (Gdk::Cursor (Gdk::WATCH));
+       set_canvas_cursor (_cursors->wait);
        gdk_flush ();
 
        for (vector<string>::iterator p = paths.begin(); p != paths.end(); ++p) {
 
                string path = *p;
-
-               if (Config->get_try_link_for_embed()) {
-
-                       /* lets see if we can link it into the session */
-                       
-                       sys::path tmp = _session->session_directory().sound_path() / Glib::path_get_basename(path);
-                       linked_path = tmp.to_string();
-                       
-                       path_to_use = linked_path;
-                       
-                       if (link (path.c_str(), linked_path.c_str()) == 0) {
-                               
-                               /* there are many reasons why link(2) might have failed.
-                                  but if it succeeds, we now have a link in the
-                                  session sound dir that will protect against
-                                  unlinking of the original path. nice.
-                               */
-                               
-                               path = linked_path;
-                               path_to_use = Glib::path_get_basename (path);
-                               
-                       } else {
-
-                               /* one possible reason is that its already linked */
-                               
-                               if (errno == EEXIST) {
-                                       struct stat sb;
-                                       
-                                       if (stat (linked_path.c_str(), &sb) == 0) {
-                                               if (sb.st_nlink > 1) { // its a hard link, assume its the one we want
-                                                       path = linked_path;
-                                                       path_to_use = Glib::path_get_basename (path);
-                                               }
-                                       }
-                               }
-                       }
-               }
+               string error_msg;
 
                /* note that we temporarily truncated _id at the colon */
 
-               string error_msg;
-
                if (!AudioFileSource::get_soundfile_info (path, finfo, error_msg)) {
                        error << string_compose(_("Editor: cannot open file \"%1\", (%2)"), path, error_msg ) << endmsg;
                        goto out;
@@ -673,7 +672,7 @@ Editor::embed_sndfiles (vector<string> paths, bool multifile,
                        }
                }
 
-               track_canvas->get_window()->set_cursor (Gdk::Cursor (Gdk::WATCH));
+               set_canvas_cursor (_cursors->wait);
 
                for (int n = 0; n < finfo.channels; ++n) {
                        try {
@@ -686,7 +685,7 @@ Editor::embed_sndfiles (vector<string> paths, bool multifile,
 
                                        source = boost::dynamic_pointer_cast<AudioFileSource> (
                                                SourceFactory::createReadable (DataType::AUDIO, *_session,
-                                                                              path_to_use, n,
+                                                                              path, n,
                                                                               (mode == ImportAsTapeTrack
                                                                                ? Source::Destructive
                                                                                : Source::Flag (0)),
@@ -714,12 +713,12 @@ Editor::embed_sndfiles (vector<string> paths, bool multifile,
        ret = add_sources (paths, sources, pos, mode, target_regions, target_tracks, track, true);
 
   out:
-       track_canvas->get_window()->set_cursor (*current_canvas_cursor);
+       set_canvas_cursor (current_canvas_cursor);
        return ret;
 }
 
 int
-Editor::add_sources (vector<string> paths, SourceList& sources, nframes64_t& pos, ImportMode mode,
+Editor::add_sources (vector<string> paths, SourceList& sources, framepos_t& pos, ImportMode mode,
                     int target_regions, int target_tracks, boost::shared_ptr<Track>& track, bool /*add_channel_suffix*/)
 {
        vector<boost::shared_ptr<Region> > regions;
@@ -741,8 +740,14 @@ Editor::add_sources (vector<string> paths, SourceList& sources, nframes64_t& pos
 
                region_name = region_name_from_path (paths.front(), (sources.size() > 1), false);
 
-               PropertyList plist; 
-               
+               /* we checked in import_sndfiles() that there were not too many */
+
+               while (RegionFactory::region_by_name (region_name)) {
+                       region_name = bump_name_once (region_name, '.');
+               }
+
+               PropertyList plist;
+
                plist.add (ARDOUR::Properties::start, 0);
                plist.add (ARDOUR::Properties::length, sources[0]->length (pos));
                plist.add (ARDOUR::Properties::name, region_name);
@@ -772,12 +777,28 @@ Editor::add_sources (vector<string> paths, SourceList& sources, nframes64_t& pos
                        just_one.clear ();
                        just_one.push_back (*x);
 
-                       region_name = region_name_from_path ((*x)->path(), false, false, sources.size(), n);
+                       boost::shared_ptr<FileSource> fs = boost::dynamic_pointer_cast<FileSource> (*x);
+
+                       if (fs) {
+                               region_name = region_name_from_path (fs->path(), false, false, sources.size(), n);
+                       } else{
+                               region_name = (*x)->name();
+                       }
+
+                       PropertyList plist;
+
+                       /* Fudge region length to ensure it is non-zero; make it 1 beat at 120bpm
+                          for want of a better idea.  It can't be too small, otherwise if this
+                          is a MIDI region the conversion from frames -> beats -> frames will
+                          round it back down to 0 again.
+                       */
+                       framecnt_t len = (*x)->length (pos);
+                       if (len == 0) {
+                               len = (60 / 120) * _session->frame_rate ();
+                       }
 
-                       PropertyList plist; 
-                       
                        plist.add (ARDOUR::Properties::start, 0);
-                       plist.add (ARDOUR::Properties::length, (*x)->length (pos));
+                       plist.add (ARDOUR::Properties::length, len);
                        plist.add (ARDOUR::Properties::name, region_name);
                        plist.add (ARDOUR::Properties::layer, 0);
                        plist.add (ARDOUR::Properties::whole_file, true);
@@ -810,55 +831,53 @@ Editor::add_sources (vector<string> paths, SourceList& sources, nframes64_t& pos
        }
 
        int n = 0;
-        framepos_t rlen = 0;
+       framepos_t rlen = 0;
 
        for (vector<boost::shared_ptr<Region> >::iterator r = regions.begin(); r != regions.end(); ++r, ++n) {
-                
-                boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion> (*r);
-                
-                if (use_timestamp && ar) {
-                        
-                        cerr << "Using timestamp to place region " << (*r)->name() << endl;
-                        
-                        /* get timestamp for this region */
-
-                        const boost::shared_ptr<Source> s (ar->sources().front());
-                        const boost::shared_ptr<AudioSource> as = boost::dynamic_pointer_cast<AudioSource> (s);
-                        
-                        assert (as);
-                        
-                        if (as->natural_position() != 0) {
-                                pos = as->natural_position();
-                                cerr << "\tgot " << pos << " from source TC info\n";
-                        } else if (target_tracks == 1) {
-                                /* hmm, no timestamp available, put it after the previous region
-                                 */
-                                if (n == 0) {
-                                        pos = get_preferred_edit_position ();
-                                        cerr << "\tno timestamp, first file, use edit pos = " << pos << endl;
+               boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion> (*r);
+
+               if (use_timestamp) {
+                        if (ar) {
+
+                                /* get timestamp for this region */
+
+                                const boost::shared_ptr<Source> s (ar->sources().front());
+                                const boost::shared_ptr<AudioSource> as = boost::dynamic_pointer_cast<AudioSource> (s);
+
+                                assert (as);
+
+                                if (as->natural_position() != 0) {
+                                        pos = as->natural_position();
+                                } else if (target_tracks == 1) {
+                                        /* hmm, no timestamp available, put it after the previous region
+                                         */
+                                        if (n == 0) {
+                                                pos = get_preferred_edit_position ();
+                                        } else {
+                                                pos += rlen;
+                                        }
                                 } else {
-                                        pos += rlen;
-                                        cerr << "\tpacked-sequence-shuffle to " << pos << endl;
+                                        pos = get_preferred_edit_position ();
                                 }
                         } else {
+                                /* should really get first position in MIDI file, but for now, use edit position*/
                                 pos = get_preferred_edit_position ();
-                                cerr << "\tmultitracks, using edit position = " << pos << endl;
                         }
-                                
                 }
 
+
                finish_bringing_in_material (*r, input_chan, output_chan, pos, mode, track);
 
-                rlen = (*r)->length();
-                
-                if (target_tracks != 1) {
-                        track.reset ();
-                } else { 
-                        if (!use_timestamp || !ar) {
-                                /* line each one up right after the other */
-                                pos += (*r)->length();
-                        }
-                }
+               rlen = (*r)->length();
+
+               if (target_tracks != 1) {
+                       track.reset ();
+               } else {
+                       if (!use_timestamp || !ar) {
+                               /* line each one up right after the other */
+                               pos += (*r)->length();
+                       }
+               }
        }
 
        /* setup peak file building in another thread */
@@ -871,7 +890,7 @@ Editor::add_sources (vector<string> paths, SourceList& sources, nframes64_t& pos
 }
 
 int
-Editor::finish_bringing_in_material (boost::shared_ptr<Region> region, uint32_t in_chans, uint32_t out_chans, nframes64_t& pos,
+Editor::finish_bringing_in_material (boost::shared_ptr<Region> region, uint32_t in_chans, uint32_t out_chans, framepos_t& pos,
                                  ImportMode mode, boost::shared_ptr<Track>& existing_track)
 {
        boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion>(region);
@@ -899,8 +918,8 @@ Editor::finish_bringing_in_material (boost::shared_ptr<Region> region, uint32_t
 
                boost::shared_ptr<Playlist> playlist = existing_track->playlist();
                boost::shared_ptr<Region> copy (RegionFactory::create (region, region->properties()));
-               begin_reversible_command (_("insert file"));
-                playlist->clear_changes ();
+               begin_reversible_command (Operations::insert_file);
+               playlist->clear_changes ();
                playlist->add_region (copy, pos);
                _session->add_command (new StatefulDiffCommand (playlist));
                commit_reversible_command ();
@@ -931,22 +950,31 @@ Editor::finish_bringing_in_material (boost::shared_ptr<Region> region, uint32_t
                        existing_track->set_name (region->name());
                }
 
+               boost::shared_ptr<Playlist> playlist = existing_track->playlist();
                boost::shared_ptr<Region> copy (RegionFactory::create (region));
-               existing_track->playlist()->add_region (copy, pos);
+               begin_reversible_command (Operations::insert_file);
+               playlist->clear_changes ();
+               playlist->add_region (copy, pos);
+               _session->add_command (new StatefulDiffCommand (playlist));
+               commit_reversible_command ();
                break;
        }
 
-
        case ImportAsTapeTrack:
        {
-               if (!ar)
+               if (!ar) {
                        return -1;
+               }
 
                list<boost::shared_ptr<AudioTrack> > at (_session->new_audio_track (in_chans, out_chans, Destructive));
                if (!at.empty()) {
+                       boost::shared_ptr<Playlist> playlist = at.front()->playlist();
                        boost::shared_ptr<Region> copy (RegionFactory::create (region));
-                       at.front()->set_name (basename_nosuffix (copy->name()));
-                       at.front()->playlist()->add_region (copy, pos);
+                       begin_reversible_command (Operations::insert_file);
+                       playlist->clear_changes ();
+                       playlist->add_region (copy, pos);
+                       _session->add_command (new StatefulDiffCommand (playlist));
+                       commit_reversible_command ();
                }
                break;
        }