more games with selection
[ardour.git] / gtk2_ardour / editor_ops.cc
index d55180d82b45c502f749359a5932ead647445b76..e500175cbc37a26068789e724aa12356830d5cd6 100644 (file)
@@ -44,6 +44,7 @@
 #include <ardour/audio_track.h>
 #include <ardour/audioplaylist.h>
 #include <ardour/region_factory.h>
+#include <ardour/playlist_factory.h>
 #include <ardour/reverse.h>
 
 #include "ardour_ui.h"
@@ -56,7 +57,6 @@
 #include "rgb_macros.h"
 #include "selection_templates.h"
 #include "selection.h"
-#include "sfdb_ui.h"
 #include "editing.h"
 #include "gtk-custom-hruler.h"
 #include "gui_thread.h"
@@ -122,7 +122,7 @@ Editor::split_regions_at (nframes_t where, RegionSelection& regions)
                tmp = a;
                ++tmp;
 
-               Playlist* pl = (*a)->region()->playlist();
+               boost::shared_ptr<Playlist> pl = (*a)->region()->playlist();
 
                AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*a);
                if (arv)
@@ -149,7 +149,7 @@ Editor::remove_clicked_region ()
                return;
        }
 
-       Playlist* playlist = clicked_audio_trackview->playlist();
+       boost::shared_ptr<Playlist> playlist = clicked_audio_trackview->playlist();
        
        begin_reversible_command (_("remove region"));
         XMLNode &before = playlist->get_state();
@@ -232,7 +232,7 @@ Editor::select_region_for_operation (int dir, TimeAxisView **tv)
                RouteTimeAxisView* rtv;
 
                if ((rtv = dynamic_cast<RouteTimeAxisView*> (*tv)) != 0) {
-                       Playlist *pl;
+                       boost::shared_ptr<Playlist> pl;
                        
                        if ((pl = rtv->playlist()) == 0) {
                                return region;
@@ -756,6 +756,39 @@ Editor::cursor_to_selection_end (Cursor *cursor)
        }
 }
 
+void
+Editor::scroll_playhead (bool forward)
+{
+       nframes_t pos = playhead_cursor->current_frame;
+       nframes_t delta = (nframes_t) floor (current_page_frames() / 0.8);
+
+       if (forward) {
+               if (pos == max_frames) {
+                       return;
+               }
+
+               if (pos < max_frames - delta) {
+                       pos += delta ;
+               } else {
+                       pos = max_frames;
+               } 
+
+       } else {
+
+               if (pos == 0) {
+                       return;
+               } 
+
+               if (pos > delta) {
+                       pos -= delta;
+               } else {
+                       pos = 0;
+               }
+       }
+
+       session->request_locate (pos);
+}
+
 void
 Editor::playhead_backward ()
 {
@@ -926,7 +959,7 @@ Editor::scroll_backward (float pages)
                frame = leftmost_frame - cnt;
        }
 
-       reposition_x_origin (frame);
+       reset_x_origin (frame);
 }
 
 void
@@ -954,7 +987,7 @@ Editor::scroll_forward (float pages)
                frame = leftmost_frame + cnt;
        }
 
-       reposition_x_origin (frame);
+       reset_x_origin (frame);
 }
 
 void
@@ -1197,6 +1230,8 @@ Editor::temporal_zoom_to_frame (bool coarser, nframes_t frame)
 void
 Editor::add_location_from_selection ()
 {
+       string rangename;
+
        if (selection->time.empty()) {
                return;
        }
@@ -1208,7 +1243,8 @@ Editor::add_location_from_selection ()
        nframes_t start = selection->time[clicked_selection].start;
        nframes_t end = selection->time[clicked_selection].end;
 
-       Location *location = new Location (start, end, "selection");
+       session->locations()->next_available_name(rangename,"selection");
+       Location *location = new Location (start, end, rangename, Location::IsRangeMarker);
 
        session->begin_reversible_command (_("add marker"));
         XMLNode &before = session->locations()->get_state();
@@ -1221,9 +1257,12 @@ Editor::add_location_from_selection ()
 void
 Editor::add_location_from_playhead_cursor ()
 {
+       string markername;
+
        nframes_t where = session->audible_frame();
        
-       Location *location = new Location (where, where, "mark", Location::IsMark);
+       session->locations()->next_available_name(markername,"mark");
+       Location *location = new Location (where, where, markername, Location::IsMark);
        session->begin_reversible_command (_("add marker"));
         XMLNode &before = session->locations()->get_state();
        session->locations()->add (location, true);
@@ -1242,7 +1281,7 @@ Editor::add_location_from_audio_region ()
        RegionView* rv = *(selection->regions.begin());
        boost::shared_ptr<Region> region = rv->region();
        
-       Location *location = new Location (region->position(), region->last_frame(), region->name());
+       Location *location = new Location (region->position(), region->last_frame(), region->name(), Location::IsRangeMarker);
        session->begin_reversible_command (_("add marker"));
         XMLNode &before = session->locations()->get_state();
        session->locations()->add (location, true);
@@ -1292,6 +1331,8 @@ Editor::select_all (Selection::Operation op)
        begin_reversible_command (_("select all"));
        switch (op) {
        case Selection::Add:
+               selection->add (touched);
+               break;
        case Selection::Toggle:
                selection->add (touched);
                break;
@@ -1336,36 +1377,57 @@ Editor::invert_selection ()
 bool
 Editor::select_all_within (nframes_t start, nframes_t end, double top, double bot, Selection::Operation op)
 {
-       list<Selectable *> touched;
-       
+       list<Selectable*> touched;
+       list<Selectable*>::size_type n = 0;
+       TrackViewList touched_tracks;
+
        for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
                if ((*iter)->hidden()) {
                        continue;
                }
+
+               n = touched.size();
+
                (*iter)->get_selectables (start, end, top, bot, touched);
-       }
 
-       cerr << "select all within found " << touched.size() << endl;
+               if (n != touched.size()) {
+                       touched_tracks.push_back (*iter);
+               }
+       }
 
+       if (!touched_tracks.empty()) {
+               switch (op) {
+               case Selection::Add:
+                       selection->add (touched_tracks);
+                       break;
+               case Selection::Toggle:
+                       selection->toggle (touched_tracks);
+                       break;
+               case Selection::Set:
+                       selection->set (touched_tracks);
+                       break;
+               case Selection::Extend:
+                       /* not defined yet */
+                       break;
+               }
+       }
+               
        begin_reversible_command (_("select all within"));
        switch (op) {
        case Selection::Add:
-       case Selection::Toggle:
-               cerr << "toggle\n";
                selection->add (touched);
                break;
+       case Selection::Toggle:
+               selection->toggle (touched);
+               break;
        case Selection::Set:
-               cerr << "set\n";
                selection->set (touched);
                break;
        case Selection::Extend:
-               cerr << "extend\n";
                /* not defined yet */
                break;
        }
 
-       cerr << "selection now has " << selection->points.size() << endl;
-
        commit_reversible_command ();
        return !touched.empty();
 }
@@ -1649,6 +1711,7 @@ Editor::set_mark ()
        nframes_t pos;
        float prefix;
        bool was_floating;
+       string markername;
 
        if (get_prefix (prefix, was_floating)) {
                pos = session->audible_frame ();
@@ -1660,7 +1723,8 @@ Editor::set_mark ()
                }
        }
 
-       session->locations()->add (new Location (pos, 0, "mark", Location::IsMark), true);
+       session->locations()->next_available_name(markername,"mark");
+       session->locations()->add (new Location (pos, 0, markername, Location::IsMark), true);
 }
 
 void
@@ -1709,6 +1773,28 @@ Editor::clear_locations ()
        session->locations()->clear ();
 }
 
+void
+Editor::unhide_markers ()
+{
+       for (LocationMarkerMap::iterator i = location_markers.begin(); i != location_markers.end(); ++i) {
+               Location *l = (*i).first;
+               if (l->is_hidden() && l->is_mark()) {
+                       l->set_hidden(false, this);
+               }
+       }
+}
+
+void
+Editor::unhide_ranges ()
+{
+       for (LocationMarkerMap::iterator i = location_markers.begin(); i != location_markers.end(); ++i) {
+               Location *l = (*i).first;
+               if (l->is_hidden() && l->is_range_marker()) { 
+                       l->set_hidden(false, this);
+               }
+       }
+}
+
 /* INSERT/REPLACE */
 
 void
@@ -1719,7 +1805,7 @@ Editor::insert_region_list_drag (boost::shared_ptr<AudioRegion> region, int x, i
        TimeAxisView *tv;
        nframes_t where;
        AudioTimeAxisView *atv = 0;
-       Playlist *playlist;
+       boost::shared_ptr<Playlist> playlist;
        
        track_canvas.window_to_world (x, y, wx, wy);
        wx += horizontal_adjustment.get_value();
@@ -1749,20 +1835,26 @@ Editor::insert_region_list_drag (boost::shared_ptr<AudioRegion> region, int x, i
                return;
        }
        
+       cerr << "drop target playlist, UC  = " << playlist.use_count() << endl;
+
        snap_to (where);
        
        begin_reversible_command (_("insert dragged region"));
         XMLNode &before = playlist->get_state();
+       cerr << "pre add target playlist, UC  = " << playlist.use_count() << endl;
        playlist->add_region (RegionFactory::create (region), where, 1.0);
+       cerr << "post add target playlist, UC  = " << playlist.use_count() << endl;
        session->add_command(new MementoCommand<Playlist>(*playlist, &before, &playlist->get_state()));
        commit_reversible_command ();
+
+       cerr << "post drop target playlist, UC  = " << playlist.use_count() << endl;
 }
 
 void
 Editor::insert_region_list_selection (float times)
 {
        RouteTimeAxisView *tv = 0;
-       Playlist *playlist;
+       boost::shared_ptr<Playlist> playlist;
 
        if (clicked_audio_trackview != 0) {
                tv = clicked_audio_trackview;
@@ -2097,7 +2189,7 @@ Editor::region_from_selection ()
        for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
                boost::shared_ptr<AudioRegion> current;
                boost::shared_ptr<Region> current_r;
-               Playlist *pl;
+               boost::shared_ptr<Playlist> pl;
 
                nframes_t internal_start;
                string new_name;
@@ -2130,11 +2222,13 @@ Editor::create_region_from_selection (vector<boost::shared_ptr<AudioRegion> >& n
        nframes_t start = selection->time[clicked_selection].start;
        nframes_t end = selection->time[clicked_selection].end;
        
+       sort_track_selection ();
+
        for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
 
                boost::shared_ptr<AudioRegion> current;
                boost::shared_ptr<Region> current_r;
-               Playlist* playlist;
+               boost::shared_ptr<Playlist> playlist;
                nframes_t internal_start;
                string new_name;
 
@@ -2189,8 +2283,10 @@ Editor::separate_region_from_selection ()
                return;
        }
 
-       Playlist *playlist;
+       boost::shared_ptr<Playlist> playlist;
                
+       sort_track_selection ();
+
        for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
 
                AudioTimeAxisView* atv;
@@ -2235,7 +2331,7 @@ Editor::separate_regions_using_location (Location& loc)
                return;
        }
 
-       Playlist *playlist;
+       boost::shared_ptr<Playlist> playlist;
 
        /* XXX i'm unsure as to whether this should operate on selected tracks only 
           or the entire enchillada. uncomment the below line to correct the behaviour 
@@ -2284,8 +2380,8 @@ Editor::crop_region_to_selection ()
                return;
        }
 
-       vector<Playlist*> playlists;
-       Playlist *playlist;
+       vector<boost::shared_ptr<Playlist> > playlists;
+       boost::shared_ptr<Playlist> playlist;
 
        if (clicked_trackview != 0) {
 
@@ -2297,6 +2393,8 @@ Editor::crop_region_to_selection ()
 
        } else {
                
+               sort_track_selection ();
+
                for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
 
                        AudioTimeAxisView* atv;
@@ -2321,7 +2419,7 @@ Editor::crop_region_to_selection ()
 
                begin_reversible_command (_("trim to selection"));
 
-               for (vector<Playlist*>::iterator i = playlists.begin(); i != playlists.end(); ++i) {
+               for (vector<boost::shared_ptr<Playlist> >::iterator i = playlists.begin(); i != playlists.end(); ++i) {
                        
                        boost::shared_ptr<Region> region;
                        
@@ -2371,7 +2469,7 @@ Editor::region_fill_track ()
                if (!ar)
                        continue;
 
-               Playlist* pl = region->playlist();
+               boost::shared_ptr<Playlist> pl = region->playlist();
 
                if (end <= region->last_frame()) {
                        return;
@@ -2415,7 +2513,7 @@ Editor::region_fill_selection ()
        nframes_t start = selection->time[clicked_selection].start;
        nframes_t end = selection->time[clicked_selection].end;
 
-       Playlist *playlist; 
+       boost::shared_ptr<Playlist> playlist; 
 
        if (selection->tracks.empty()) {
                return;
@@ -2743,8 +2841,14 @@ Editor::freeze_route ()
        itt.done = false;
        itt.cancel = false;
        itt.progress = 0.0f;
+       
+       pthread_attr_t attr;
+       pthread_attr_init(&attr);
+       pthread_attr_setstacksize(&attr, 500000);
+
+       pthread_create (&itt.thread, &attr, _freeze_thread, this);
 
-       pthread_create (&itt.thread, 0, _freeze_thread, this);
+       pthread_attr_destroy(&attr);
 
        track_canvas.get_window()->set_cursor (Gdk::Cursor (Gdk::WATCH));
 
@@ -2781,7 +2885,7 @@ Editor::bounce_range_selection ()
                        continue;
                }
                
-               Playlist* playlist;
+               boost::shared_ptr<Playlist> playlist;
                
                if ((playlist = atv->playlist()) == 0) {
                        return;
@@ -2897,7 +3001,7 @@ Editor::cut_copy_points (CutCopyOp op)
 }
 
 struct PlaylistState {
-    Playlist* playlist;
+    boost::shared_ptr<Playlist> playlist;
     XMLNode*  before;
 };
 
@@ -2907,21 +3011,38 @@ struct lt_playlist {
     }
 };
        
+struct PlaylistMapping { 
+    TimeAxisView* tv;
+    boost::shared_ptr<AudioPlaylist> pl;
+
+    PlaylistMapping (TimeAxisView* tvp) : tv (tvp) {}
+};
+
 void
 Editor::cut_copy_regions (CutCopyOp op)
 {
-        typedef std::map<AudioPlaylist*,AudioPlaylist*> PlaylistMapping;
-       PlaylistMapping pmap;
-       nframes_t first_position = max_frames;
+       /* we can't use a std::map here because the ordering is important, and we can't trivially sort
+          a map when we want ordered access to both elements. i think.
+       */
+
+       vector<PlaylistMapping> pmap;
 
+       nframes_t first_position = max_frames;
+       
        set<PlaylistState, lt_playlist> freezelist;
        pair<set<PlaylistState, lt_playlist>::iterator,bool> insert_result;
+       
+       /* get ordering correct before we cut/copy */
+       
+       selection->regions.sort_by_position_and_track ();
 
        for (RegionSelection::iterator x = selection->regions.begin(); x != selection->regions.end(); ++x) {
+
                first_position = min ((*x)->region()->position(), first_position);
 
                if (op == Cut || op == Clear) {
-                       AudioPlaylist *pl = dynamic_cast<AudioPlaylist*>((*x)->region()->playlist());
+                       boost::shared_ptr<AudioPlaylist> pl = boost::dynamic_pointer_cast<AudioPlaylist>((*x)->region()->playlist());
+
                        if (pl) {
 
                                PlaylistState before;
@@ -2929,66 +3050,94 @@ Editor::cut_copy_regions (CutCopyOp op)
                                before.before = &pl->get_state();
                                
                                insert_result = freezelist.insert (before);
-
+                               
                                if (insert_result.second) {
                                        pl->freeze ();
                                }
                        }
                }
+
+               TimeAxisView* tv = &(*x)->get_trackview();
+               vector<PlaylistMapping>::iterator z;
+
+               for (z = pmap.begin(); z != pmap.end(); ++z) {
+                       if ((*z).tv == tv) {
+                               break;
+                       }
+               }
+               
+               if (z == pmap.end()) {
+                       pmap.push_back (PlaylistMapping (tv));
+               }
        }
 
        for (RegionSelection::iterator x = selection->regions.begin(); x != selection->regions.end(); ) {
 
-               AudioPlaylist *pl = dynamic_cast<AudioPlaylist*>((*x)->region()->playlist());
-               AudioPlaylist* npl;
+               boost::shared_ptr<AudioPlaylist> pl = boost::dynamic_pointer_cast<AudioPlaylist>((*x)->region()->playlist());
+               
+               if (!pl) {
+                       /* impossible, but this handles it for the future */
+                       continue;
+               }
+
+               TimeAxisView& tv = (*x)->get_trackview();
+               boost::shared_ptr<AudioPlaylist> npl;
                RegionSelection::iterator tmp;
                
                tmp = x;
                ++tmp;
 
-               if (pl) {
-
-                       PlaylistMapping::iterator pi = pmap.find (pl);
-                       
-                       if (pi == pmap.end()) {
-                               npl = new AudioPlaylist (*session, "cutlist", true);
-                               npl->freeze();
-                               pmap[pl] = npl;
-                       } else {
-                               npl = pi->second;
-                       }
-
-                       // FIXME
-                       boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion>((*x)->region());
-                       switch (op) {
-                       case Cut:
-                               if (!ar) break;
-
-                               npl->add_region (RegionFactory::create (ar), (*x)->region()->position() - first_position);
-                               pl->remove_region (((*x)->region()));
-                               break;
-
-                       case Copy:
-                               if (!ar) break;
-
-                               npl->add_region (RegionFactory::create (ar), (*x)->region()->position() - first_position);
-                               break;
-
-                       case Clear:
-                               pl->remove_region (((*x)->region()));
+               vector<PlaylistMapping>::iterator z;
+               
+               for (z = pmap.begin(); z != pmap.end(); ++z) {
+                       if ((*z).tv == &tv) {
                                break;
                        }
                }
+               
+               assert (z != pmap.end());
+               
+               if (!(*z).pl) {
+                       npl = boost::dynamic_pointer_cast<AudioPlaylist> (PlaylistFactory::create (*session, "cutlist", true));
+                       npl->freeze();
+                       (*z).pl = npl;
+               } else {
+                       npl = (*z).pl;
+               }
+               
+               boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion>((*x)->region());
+               
+               switch (op) {
+               case Cut:
+                       if (!ar) break;
+                       
+                       npl->add_region (RegionFactory::create (ar), (*x)->region()->position() - first_position);
+                       pl->remove_region (((*x)->region()));
+                       break;
+                       
+               case Copy:
+                       if (!ar) break;
+                       
+                       npl->add_region (RegionFactory::create (ar), (*x)->region()->position() - first_position);
+                       break;
+                       
+               case Clear:
+                       pl->remove_region (((*x)->region()));
+                       break;
+               }
 
                x = tmp;
        }
-
-       list<Playlist*> foo;
-
-       for (PlaylistMapping::iterator i = pmap.begin(); i != pmap.end(); ++i) {
-               foo.push_back (i->second);
+       
+       list<boost::shared_ptr<Playlist> > foo;
+       
+       /* the pmap is in the same order as the tracks in which selected regions occured */
+       
+       for (vector<PlaylistMapping>::iterator i = pmap.begin(); i != pmap.end(); ++i) {
+               (*i).pl->thaw();
+               foo.push_back ((*i).pl);
        }
-
+       
        if (!foo.empty()) {
                cut_buffer->set (foo);
        }
@@ -3052,15 +3201,19 @@ Editor::paste_internal (nframes_t position, float times)
        TrackSelection::iterator i;
        size_t nth;
 
+       /* get everything in the correct order */
+
+       sort_track_selection ();
+
        for (nth = 0, i = selection->tracks.begin(); i != selection->tracks.end(); ++i, ++nth) {
-               
+
                /* undo/redo is handled by individual tracks */
 
                if ((*i)->paste (position, times, *cut_buffer, nth)) {
                        commit = true;
                }
        }
-
+       
        if (commit) {
                commit_reversible_command ();
        }
@@ -3080,18 +3233,20 @@ Editor::paste_named_selection (float times)
        TreeModel::iterator i = selected->get_selected();
        NamedSelection* ns = (*i)[named_selection_columns.selection];
 
-       list<Playlist*>::iterator chunk;
-       list<Playlist*>::iterator tmp;
+       list<boost::shared_ptr<Playlist> >::iterator chunk;
+       list<boost::shared_ptr<Playlist> >::iterator tmp;
 
        chunk = ns->playlists.begin();
                
        begin_reversible_command (_("paste chunk"));
+       
+       sort_track_selection ();
 
        for (t = selection->tracks.begin(); t != selection->tracks.end(); ++t) {
                
                AudioTimeAxisView* atv;
-               Playlist* pl;
-               AudioPlaylist* apl;
+               boost::shared_ptr<Playlist> pl;
+               boost::shared_ptr<AudioPlaylist> apl;
 
                if ((atv = dynamic_cast<AudioTimeAxisView*> (*t)) == 0) {
                        continue;
@@ -3100,8 +3255,8 @@ Editor::paste_named_selection (float times)
                if ((pl = atv->playlist()) == 0) {
                        continue;
                }
-
-               if ((apl = dynamic_cast<AudioPlaylist*> (pl)) == 0) {
+               
+               if ((apl = boost::dynamic_pointer_cast<AudioPlaylist> (pl)) == 0) {
                        continue;
                }
 
@@ -3109,7 +3264,7 @@ Editor::paste_named_selection (float times)
                ++tmp;
 
                 XMLNode &before = apl->get_state();
-               apl->paste (**chunk, edit_cursor->current_frame, times);
+               apl->paste (*chunk, edit_cursor->current_frame, times);
                session->add_command(new MementoCommand<AudioPlaylist>(*apl, &before, &apl->get_state()));
 
                if (tmp != ns->playlists.end()) {
@@ -3123,7 +3278,7 @@ Editor::paste_named_selection (float times)
 void
 Editor::duplicate_some_regions (RegionSelection& regions, float times)
 {
-       Playlist *playlist; 
+       boost::shared_ptr<Playlist> playlist; 
        RegionSelection sel = regions; // clear (below) will clear the argument list
                
        begin_reversible_command (_("duplicate region"));
@@ -3161,7 +3316,7 @@ Editor::duplicate_selection (float times)
                return;
        }
 
-       Playlist *playlist; 
+       boost::shared_ptr<Playlist> playlist; 
        vector<boost::shared_ptr<AudioRegion> > new_regions;
        vector<boost::shared_ptr<AudioRegion> >::iterator ri;
                
@@ -3198,8 +3353,6 @@ Editor::reset_point_selection ()
 {
        /* reset all selected points to the relevant default value */
 
-       cerr << "point selection has " << selection->points.size() << " entries\n";
-       
        for (PointSelection::iterator i = selection->points.begin(); i != selection->points.end(); ++i) {
                
                AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*>(&(*i).track);
@@ -3227,20 +3380,20 @@ Editor::center_edit_cursor ()
 }
 
 void
-Editor::clear_playlist (Playlist& playlist)
+Editor::clear_playlist (boost::shared_ptr<Playlist> playlist)
 {
        begin_reversible_command (_("clear playlist"));
-        XMLNode &before = playlist.get_state();
-       playlist.clear ();
-        XMLNode &after = playlist.get_state();
-       session->add_command (new MementoCommand<Playlist>(playlist, &before, &after));
+        XMLNode &before = playlist->get_state();
+       playlist->clear ();
+        XMLNode &after = playlist->get_state();
+       session->add_command (new MementoCommand<Playlist>(*playlist.get(), &before, &after));
        commit_reversible_command ();
 }
 
 void
 Editor::nudge_track (bool use_edit_cursor, bool forwards)
 {
-       Playlist *playlist; 
+       boost::shared_ptr<Playlist> playlist; 
        nframes_t distance;
        nframes_t next_distance;
        nframes_t start;
@@ -3388,7 +3541,7 @@ Editor::apply_filter (AudioFilter& filter, string command)
                if (!arv)
                        continue;
 
-               Playlist* playlist = arv->region()->playlist();
+               boost::shared_ptr<Playlist> playlist = arv->region()->playlist();
 
                RegionSelection::iterator tmp;
                
@@ -3566,3 +3719,99 @@ Editor::toggle_region_opaque ()
                }
        }
 }
+
+void
+Editor::set_fade_in_shape (AudioRegion::FadeShape shape)
+{
+       begin_reversible_command (_("set fade in shape"));
+
+       for (RegionSelection::iterator x = selection->regions.begin(); x != selection->regions.end(); ++x) {
+               AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
+
+               if (!tmp) {
+                       return;
+               }
+
+               AutomationList& alist = tmp->audio_region()->fade_in();
+               XMLNode &before = alist.get_state();
+
+               tmp->audio_region()->set_fade_in_shape (shape);
+               
+               XMLNode &after = alist.get_state();
+               session->add_command(new MementoCommand<AutomationList>(alist, &before, &after));
+       }
+
+       commit_reversible_command ();
+}
+
+void
+Editor::set_fade_out_shape (AudioRegion::FadeShape shape)
+{
+       begin_reversible_command (_("set fade out shape"));
+
+       for (RegionSelection::iterator x = selection->regions.begin(); x != selection->regions.end(); ++x) {
+               AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
+
+               if (!tmp) {
+                       return;
+               }
+
+               AutomationList& alist = tmp->audio_region()->fade_out();
+               XMLNode &before = alist.get_state();
+
+               tmp->audio_region()->set_fade_out_shape (shape);
+               
+               XMLNode &after = alist.get_state();
+               session->add_command(new MementoCommand<AutomationList>(alist, &before, &after));
+       }
+
+       commit_reversible_command ();
+}
+
+void
+Editor::set_fade_in_active (bool yn)
+{
+       begin_reversible_command (_("set fade in active"));
+
+       for (RegionSelection::iterator x = selection->regions.begin(); x != selection->regions.end(); ++x) {
+               AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
+
+               if (!tmp) {
+                       return;
+               }
+
+
+               boost::shared_ptr<AudioRegion> ar (tmp->audio_region());
+
+               XMLNode &before = ar->get_state();
+
+               ar->set_fade_in_active (yn);
+               
+               XMLNode &after = ar->get_state();
+               session->add_command(new MementoCommand<AudioRegion>(*ar, &before, &after));
+       }
+}
+
+void
+Editor::set_fade_out_active (bool yn)
+{
+       begin_reversible_command (_("set fade out active"));
+
+       for (RegionSelection::iterator x = selection->regions.begin(); x != selection->regions.end(); ++x) {
+               AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
+
+               if (!tmp) {
+                       return;
+               }
+
+               boost::shared_ptr<AudioRegion> ar (tmp->audio_region());
+
+               XMLNode &before = ar->get_state();
+
+               ar->set_fade_out_active (yn);
+               
+               XMLNode &after = ar->get_state();
+               session->add_command(new MementoCommand<AudioRegion>(*ar, &before, &after));
+       }
+}
+