Connect Script Manager "Edit" -> Lua Scripting Window
[ardour.git] / gtk2_ardour / editor_selection.cc
index ca3197c533d2730ab0679408c577cb2769d2cc51..d214d48a320b42127c3a8c761276e270b451fe33 100644 (file)
 #include <cstdlib>
 
 #include "pbd/stacktrace.h"
+#include "pbd/unwind.h"
 
-#include "ardour/session.h"
+#include "ardour/midi_region.h"
 #include "ardour/playlist.h"
-#include "ardour/route_group.h"
 #include "ardour/profile.h"
-#include "ardour/midi_region.h"
+#include "ardour/route_group.h"
+#include "ardour/session.h"
 
+#include "control_protocol/control_protocol.h"
+
+#include "editor_drag.h"
 #include "editor.h"
 #include "actions.h"
 #include "audio_time_axis.h"
 #include "automation_line.h"
 #include "control_point.h"
 #include "editor_regions.h"
+#include "editor_cursors.h"
+#include "midi_region_view.h"
+#include "sfdb_ui.h"
 
-#include "i18n.h"
+#include "pbd/i18n.h"
 
 using namespace std;
 using namespace ARDOUR;
@@ -49,9 +56,9 @@ using namespace Editing;
 
 struct TrackViewByPositionSorter
 {
-    bool operator() (const TimeAxisView* a, const TimeAxisView *b) {
-           return a->y_position() < b->y_position();
-    }
+       bool operator() (const TimeAxisView* a, const TimeAxisView *b) {
+               return a->y_position() < b->y_position();
+       }
 };
 
 bool
@@ -168,6 +175,7 @@ Editor::select_all_tracks ()
                        visible_views.push_back (*i);
                }
        }
+       PBD::Unwinder<bool> uw (_track_selection_change_without_scroll, true);
        selection->set (visible_views);
 }
 
@@ -175,111 +183,78 @@ Editor::select_all_tracks ()
  *  tracks, in which case nothing will happen unless `force' is true.
  */
 void
-Editor::set_selected_track_as_side_effect (Selection::Operation op, bool /*force*/)
+Editor::set_selected_track_as_side_effect (Selection::Operation op)
 {
        if (!clicked_axisview) {
                return;
        }
 
-#if 1
-        if (!clicked_routeview) {
-                return;
-        }
+       RouteGroup* group = NULL;
+       if (clicked_routeview) {
+               group = clicked_routeview->route()->route_group();
+       }
 
-        bool had_tracks = !selection->tracks.empty();
-        RouteGroup* group = clicked_routeview->route()->route_group();
-        RouteGroup& arg (_session->all_route_group());
-        
        switch (op) {
-       case Selection::Toggle: 
+       case Selection::Toggle:
                if (selection->selected (clicked_axisview)) {
-                       if (arg.is_select() && arg.is_active()) {
+                       if (group && group->is_active()) {
                                for (TrackViewList::iterator i = track_views.begin(); i != track_views.end (); ++i) {
-                                        selection->remove(*i);
-                               }
-                       } else if (group && group->is_active()) {
-                               for (TrackViewList::iterator i = track_views.begin(); i != track_views.end (); ++i) {
-                                       if ((*i)->route_group() == group)
+                                       if ((*i)->route_group() == group) {
                                                selection->remove(*i);
+                                       }
                                }
                        } else {
                                selection->remove (clicked_axisview);
-                        }
+                       }
                } else {
-                       if (arg.is_select() && arg.is_active()) {
-                               for (TrackViewList::iterator i = track_views.begin(); i != track_views.end (); ++i) {
-                                               selection->add(*i);
-                               }
-                       } else if (group && group->is_active()) {
+                       if (group && group->is_active()) {
                                for (TrackViewList::iterator i = track_views.begin(); i != track_views.end (); ++i) {
-                                       if ( (*i)->route_group() == group)
+                                       if ((*i)->route_group() == group) {
                                                selection->add(*i);
+                                       }
                                }
-                        } else {
-                                selection->add (clicked_axisview);
-                        }
-               }
-                break;
-       
-       case Selection::Add: 
-               if (!had_tracks && arg.is_select() && arg.is_active()) {
-                        /* nothing was selected already, and all group is active etc. so use
-                           all tracks.
-                        */
-                       for (TrackViewList::iterator i = track_views.begin(); i != track_views.end (); ++i) {
-                                       selection->add(*i);
+                       } else {
+                               selection->add (clicked_axisview);
                        }
-               } else if (group && group->is_active()) {
+               }
+               break;
+
+       case Selection::Add:
+               if (group && group->is_active()) {
                        for (TrackViewList::iterator i  = track_views.begin(); i != track_views.end (); ++i) {
-                               if ((*i)->route_group() == group)
+                               if ((*i)->route_group() == group) {
                                        selection->add(*i);
+                               }
                        }
-                } else {
+               } else {
                        selection->add (clicked_axisview);
-                }
-                break;
-               
+               }
+               break;
+
        case Selection::Set:
-                selection->clear();
-               if (!had_tracks && arg.is_select() && arg.is_active()) {
-                        /* nothing was selected already, and all group is active etc. so use
-                           all tracks.
-                        */
-                       for (TrackViewList::iterator i = track_views.begin(); i != track_views.end (); ++i) {
-                                       selection->add(*i);
-                       }
-               } else if (group && group->is_active()) {
+               selection->clear();
+               if (group && group->is_active()) {
                        for (TrackViewList::iterator i  = track_views.begin(); i != track_views.end (); ++i) {
-                               if ((*i)->route_group() == group)
+                               if ((*i)->route_group() == group) {
                                        selection->add(*i);
+                               }
                        }
-                } else {
+               } else {
                        selection->set (clicked_axisview);
-                }
-                break;
-       
-       case Selection::Extend: 
-               selection->clear();
-               cerr << ("Editor::set_selected_track_as_side_effect  case  Selection::Add  not yet implemented\n");
-                break;
-       }
-
-#else // the older version
-
-       if (!selection->tracks.empty()) {
-               if (!selection->selected (clicked_axisview)) {
-                       selection->add (clicked_axisview);
                }
+               break;
 
-       } else if (force) {
-               selection->set (clicked_axisview);
+       case Selection::Extend:
+               selection->clear();
+               break;
        }
-#endif
 }
 
 void
 Editor::set_selected_track (TimeAxisView& view, Selection::Operation op, bool no_remove)
 {
+       begin_reversible_selection_op (X_("Set Selected Track"));
+
        switch (op) {
        case Selection::Toggle:
                if (selection->selected (&view)) {
@@ -305,6 +280,8 @@ Editor::set_selected_track (TimeAxisView& view, Selection::Operation op, bool no
                extend_selection_to_track (view);
                break;
        }
+
+       commit_reversible_selection_op ();
 }
 
 void
@@ -322,38 +299,80 @@ Editor::set_selected_track_from_click (bool press, Selection::Operation op, bool
 }
 
 bool
-Editor::set_selected_control_point_from_click (Selection::Operation op, bool /*no_remove*/)
+Editor::set_selected_control_point_from_click (bool press, Selection::Operation op)
 {
        if (!clicked_control_point) {
                return false;
        }
-       
+
+       bool ret = false;
+
        switch (op) {
        case Selection::Set:
-               selection->set (clicked_control_point);
+               if (!selection->selected (clicked_control_point)) {
+                       selection->set (clicked_control_point);
+                       ret = true;
+               } else {
+                       /* clicked on an already selected point */
+                       if (press) {
+                               break;
+                       } else {
+                               if (selection->points.size() > 1) {
+                                       selection->set (clicked_control_point);
+                                       ret = true;
+                               }
+                       }
+               }
                break;
+
        case Selection::Add:
-               selection->add (clicked_control_point);
+               if (press) {
+                       selection->add (clicked_control_point);
+                       ret = true;
+               }
                break;
        case Selection::Toggle:
-               selection->toggle (clicked_control_point);
+
+               /* This is a bit of a hack; if we Primary-Click-Drag a control
+                  point (for push drag) we want the point we clicked on to be
+                  selected, otherwise we end up confusingly dragging an
+                  unselected point.  So here we ensure that the point is selected
+                  after the press, and if we subsequently get a release (meaning no
+                  drag occurred) we set things up so that the toggle has happened.
+               */
+               if (press && !selection->selected (clicked_control_point)) {
+                       /* This is the button press, and the control point is not selected; make it so,
+                          in case this press leads to a drag.  Also note that having done this, we don't
+                          need to toggle again on release.
+                       */
+                       selection->toggle (clicked_control_point);
+                       _control_point_toggled_on_press = true;
+                       ret = true;
+               } else if (!press && !_control_point_toggled_on_press) {
+                       /* This is the release, and the point wasn't toggled on the press, so do it now */
+                       selection->toggle (clicked_control_point);
+                       ret = true;
+               } else {
+                       /* Reset our flag */
+                       _control_point_toggled_on_press = false;
+               }
                break;
        case Selection::Extend:
                /* XXX */
                break;
        }
 
-       return true;
+       return ret;
 }
 
 void
 Editor::get_onscreen_tracks (TrackViewList& tvl)
 {
-       for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
-               if ((*i)->y_position() < _canvas_height) {
-                       tvl.push_back (*i);
-               }
-       }
+       for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
+               if ((*i)->y_position() < _visible_canvas_height) {
+                       tvl.push_back (*i);
+               }
+       }
 }
 
 /** Call a slot for a given `basis' track and also for any track that is in the same
@@ -393,7 +412,64 @@ Editor::mapover_tracks (sigc::slot<void, RouteTimeAxisView&, uint32_t> sl, TimeA
 
        /* call the slots */
        uint32_t const sz = tracks.size ();
-       
+
+       for (set<RouteTimeAxisView*>::iterator i = tracks.begin(); i != tracks.end(); ++i) {
+               sl (**i, sz);
+       }
+}
+
+/** Call a slot for a given `basis' track and also for any track that is in the same
+ *  active route group with a particular set of properties.
+ *
+ *  @param sl Slot to call.
+ *  @param basis Basis track.
+ *  @param prop Properties that active edit groups must share to be included in the map.
+ */
+
+void
+Editor::mapover_tracks_with_unique_playlists (sigc::slot<void, RouteTimeAxisView&, uint32_t> sl, TimeAxisView* basis, PBD::PropertyID prop) const
+{
+       RouteTimeAxisView* route_basis = dynamic_cast<RouteTimeAxisView*> (basis);
+       set<boost::shared_ptr<Playlist> > playlists;
+
+       if (route_basis == 0) {
+               return;
+       }
+
+       set<RouteTimeAxisView*> tracks;
+       tracks.insert (route_basis);
+
+       RouteGroup* group = route_basis->route()->route_group(); // could be null, not a problem
+
+       if (group && group->enabled_property(prop) && group->enabled_property (Properties::active.property_id) ) {
+
+               /* the basis is a member of an active route group, with the appropriate
+                  properties; find other members */
+
+               for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
+                       RouteTimeAxisView* v = dynamic_cast<RouteTimeAxisView*> (*i);
+
+                       if (v && v->route()->route_group() == group) {
+
+                               boost::shared_ptr<Track> t = v->track();
+                               if (t) {
+                                       if (playlists.insert (t->playlist()).second) {
+                                               /* haven't seen this playlist yet */
+                                               tracks.insert (v);
+                                       }
+                               } else {
+                                       /* not actually a "Track", but a timeaxis view that
+                                          we should mapover anyway.
+                                       */
+                                       tracks.insert (v);
+                               }
+                       }
+               }
+       }
+
+       /* call the slots */
+       uint32_t const sz = tracks.size ();
+
        for (set<RouteTimeAxisView*>::iterator i = tracks.begin(); i != tracks.end(); ++i) {
                sl (**i, sz);
        }
@@ -431,7 +507,7 @@ Editor::mapped_get_equivalent_regions (RouteTimeAxisView& tv, uint32_t, RegionVi
 void
 Editor::get_equivalent_regions (RegionView* basis, vector<RegionView*>& equivalent_regions, PBD::PropertyID property) const
 {
-       mapover_tracks (sigc::bind (sigc::mem_fun (*this, &Editor::mapped_get_equivalent_regions), basis, &equivalent_regions), &basis->get_time_axis_view(), property);
+       mapover_tracks_with_unique_playlists (sigc::bind (sigc::mem_fun (*this, &Editor::mapped_get_equivalent_regions), basis, &equivalent_regions), &basis->get_time_axis_view(), property);
 
        /* add clicked regionview since we skipped all other regions in the same track as the one it was in */
 
@@ -447,10 +523,9 @@ Editor::get_equivalent_regions (RegionSelection & basis, PBD::PropertyID prop) c
 
                vector<RegionView*> eq;
 
-               mapover_tracks (
+               mapover_tracks_with_unique_playlists (
                        sigc::bind (sigc::mem_fun (*this, &Editor::mapped_get_equivalent_regions), *i, &eq),
-                       &(*i)->get_time_axis_view(), prop
-                       );
+                       &(*i)->get_time_axis_view(), prop);
 
                for (vector<RegionView*>::iterator j = eq.begin(); j != eq.end(); ++j) {
                        equivalent.add (*j);
@@ -462,7 +537,6 @@ Editor::get_equivalent_regions (RegionSelection & basis, PBD::PropertyID prop) c
        return equivalent;
 }
 
-
 int
 Editor::get_regionview_count_from_region_list (boost::shared_ptr<Region> region)
 {
@@ -502,7 +576,7 @@ Editor::get_regionview_count_from_region_list (boost::shared_ptr<Region> region)
 
 
 bool
-Editor::set_selected_regionview_from_click (bool press, Selection::Operation op, bool /*no_track_remove*/)
+Editor::set_selected_regionview_from_click (bool press, Selection::Operation op)
 {
        vector<RegionView*> all_equivalent_regions;
        bool commit = false;
@@ -517,10 +591,8 @@ Editor::set_selected_regionview_from_click (bool press, Selection::Operation op,
 
        if (op == Selection::Toggle || op == Selection::Set) {
 
-
                switch (op) {
                case Selection::Toggle:
-
                        if (selection->selected (clicked_regionview)) {
                                if (press) {
 
@@ -531,7 +603,6 @@ Editor::set_selected_regionview_from_click (bool press, Selection::Operation op,
                                        button_release_can_deselect = true;
 
                                } else {
-
                                        if (button_release_can_deselect) {
 
                                                /* just remove this one region, but only on a permitted button release */
@@ -552,7 +623,7 @@ Editor::set_selected_regionview_from_click (bool press, Selection::Operation op,
                                if (press) {
 
                                        if (selection->selected (clicked_routeview)) {
-                                               get_equivalent_regions (clicked_regionview, all_equivalent_regions, ARDOUR::Properties::select.property_id);
+                                               get_equivalent_regions (clicked_regionview, all_equivalent_regions, ARDOUR::Properties::group_select.property_id);
                                        } else {
                                                all_equivalent_regions.push_back (clicked_regionview);
                                        }
@@ -570,12 +641,21 @@ Editor::set_selected_regionview_from_click (bool press, Selection::Operation op,
 
                case Selection::Set:
                        if (!selection->selected (clicked_regionview)) {
-                               get_equivalent_regions (clicked_regionview, all_equivalent_regions, ARDOUR::Properties::select.property_id);
+                               get_equivalent_regions (clicked_regionview, all_equivalent_regions, ARDOUR::Properties::group_select.property_id);
                                selection->set (all_equivalent_regions);
                                commit = true;
                        } else {
-                               /* no commit necessary: clicked on an already selected region */
-                               goto out;
+                               /* clicked on an already selected region */
+                               if (press)
+                                       goto out;
+                               else {
+                                       if (selection->regions.size() > 1) {
+                                               /* collapse region selection down to just this one region (and its equivalents) */
+                                               get_equivalent_regions(clicked_regionview, all_equivalent_regions, ARDOUR::Properties::group_select.property_id);
+                                               selection->set(all_equivalent_regions);
+                                               commit = true;
+                                       }
+                               }
                        }
                        break;
 
@@ -616,7 +696,7 @@ Editor::set_selected_regionview_from_click (bool press, Selection::Operation op,
                        /* 2. figure out the boundaries for our search for new objects */
 
                        switch (clicked_regionview->region()->coverage (first_frame, last_frame)) {
-                       case OverlapNone:
+                       case Evoral::OverlapNone:
                                if (last_frame < clicked_regionview->region()->first_frame()) {
                                        first_frame = last_frame;
                                        last_frame = clicked_regionview->region()->last_frame();
@@ -626,7 +706,7 @@ Editor::set_selected_regionview_from_click (bool press, Selection::Operation op,
                                }
                                break;
 
-                       case OverlapExternal:
+                       case Evoral::OverlapExternal:
                                if (last_frame < clicked_regionview->region()->first_frame()) {
                                        first_frame = last_frame;
                                        last_frame = clicked_regionview->region()->last_frame();
@@ -636,7 +716,7 @@ Editor::set_selected_regionview_from_click (bool press, Selection::Operation op,
                                }
                                break;
 
-                       case OverlapInternal:
+                       case Evoral::OverlapInternal:
                                if (last_frame < clicked_regionview->region()->first_frame()) {
                                        first_frame = last_frame;
                                        last_frame = clicked_regionview->region()->last_frame();
@@ -646,8 +726,8 @@ Editor::set_selected_regionview_from_click (bool press, Selection::Operation op,
                                }
                                break;
 
-                       case OverlapStart:
-                       case OverlapEnd:
+                       case Evoral::OverlapStart:
+                       case Evoral::OverlapEnd:
                                /* nothing to do except add clicked region to selection, since it
                                   overlaps with the existing selection in this track.
                                */
@@ -685,7 +765,7 @@ Editor::set_selected_regionview_from_click (bool press, Selection::Operation op,
                                relevant_tracks.insert (r);
                        }
                }
-               
+
                set<RouteTimeAxisView*> already_in_selection;
 
                if (relevant_tracks.empty()) {
@@ -713,7 +793,7 @@ Editor::set_selected_regionview_from_click (bool press, Selection::Operation op,
 
                                        RouteTimeAxisView* closest = 0;
                                        int distance = INT_MAX;
-                                       int key = rtv->route()->order_key ("editor");
+                                       int key = rtv->route()->presentation_info().order ();
 
                                        for (RegionSelection::iterator x = selection->regions.begin(); x != selection->regions.end(); ++x) {
 
@@ -728,7 +808,7 @@ Editor::set_selected_regionview_from_click (bool press, Selection::Operation op,
                                                        if (result.second) {
                                                                /* newly added to already_in_selection */
 
-                                                               int d = artv->route()->order_key ("editor");
+                                                               int d = artv->route()->presentation_info().order ();
 
                                                                d -= key;
 
@@ -744,7 +824,7 @@ Editor::set_selected_regionview_from_click (bool press, Selection::Operation op,
 
                                                /* now add all tracks between that one and this one */
 
-                                               int okey = closest->route()->order_key ("editor");
+                                               int okey = closest->route()->presentation_info().order ();
 
                                                if (okey > key) {
                                                        swap (okey, key);
@@ -754,7 +834,7 @@ Editor::set_selected_regionview_from_click (bool press, Selection::Operation op,
                                                        RouteTimeAxisView* artv = dynamic_cast<RouteTimeAxisView*>(*x);
                                                        if (artv && artv != rtv) {
 
-                                                               int k = artv->route()->order_key ("editor");
+                                                               int k = artv->route()->presentation_info().order ();
 
                                                                if (k >= okey && k <= key) {
 
@@ -765,8 +845,8 @@ Editor::set_selected_regionview_from_click (bool press, Selection::Operation op,
                                                                        */
 
                                                                        if (find (already_in_selection.begin(),
-                                                                                 already_in_selection.end(),
-                                                                                 artv) == already_in_selection.end()) {
+                                                                                 already_in_selection.end(),
+                                                                                 artv) == already_in_selection.end()) {
 
                                                                                relevant_tracks.insert (artv);
                                                                        }
@@ -779,7 +859,7 @@ Editor::set_selected_regionview_from_click (bool press, Selection::Operation op,
                }
 
                /* 3. find all selectable objects (regionviews in this case) between that one and the end of the
-                          one that was clicked.
+                  one that was clicked.
                */
 
                for (set<RouteTimeAxisView*>::iterator t = relevant_tracks.begin(); t != relevant_tracks.end(); ++t) {
@@ -801,10 +881,15 @@ Editor::set_selected_regionview_from_click (bool press, Selection::Operation op,
                if (!regions.empty()) {
                        selection->add (regions);
                        commit = true;
+               } else if (selection->regions.empty() && !selection->selected (clicked_regionview)) {
+                       /* ensure that at least the clicked regionview is selected. */
+                       selection->set (clicked_regionview);
+                       commit = true;
                }
+
        }
 
-  out:
+out:
        return commit;
 }
 
@@ -814,13 +899,13 @@ Editor::set_selected_regionview_from_region_list (boost::shared_ptr<Region> regi
 {
        vector<RegionView*> all_equivalent_regions;
 
-       get_regions_corresponding_to (region, all_equivalent_regions);
+       get_regions_corresponding_to (region, all_equivalent_regions, region->whole_file());
 
        if (all_equivalent_regions.empty()) {
                return;
        }
 
-       begin_reversible_command (_("set selected regions"));
+       begin_reversible_selection_op (X_("set selected regions"));
 
        switch (op) {
        case Selection::Toggle:
@@ -838,7 +923,7 @@ Editor::set_selected_regionview_from_region_list (boost::shared_ptr<Region> regi
                break;
        }
 
-       commit_reversible_command () ;
+       commit_reversible_selection_op () ;
 }
 
 bool
@@ -863,11 +948,11 @@ Editor::set_selected_regionview_from_map_event (GdkEventAny* /*ev*/, StreamView*
                return true;
        }
 
-       begin_reversible_command (_("set selected regions"));
+       begin_reversible_selection_op (X_("set selected regions"));
 
        selection->set (rv);
 
-       commit_reversible_command () ;
+       commit_reversible_selection_op () ;
 
        return true;
 }
@@ -879,39 +964,92 @@ Editor::track_selection_changed ()
        case 0:
                break;
        default:
-               set_selected_mixer_strip (*(selection->tracks.front()));
+               /* last element in selection list is the most recently
+                * selected, because we always append to that list.
+                */
+               set_selected_mixer_strip (*(selection->tracks.back()));
+               if (!_track_selection_change_without_scroll) {
+                       ensure_time_axis_view_is_visible (*(selection->tracks.back()), false);
+               }
                break;
        }
 
+       RouteNotificationListPtr routes (new RouteNotificationList);
+       StripableNotificationListPtr stripables (new StripableNotificationList);
+
        for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
 
-                bool yn = (find (selection->tracks.begin(), selection->tracks.end(), *i) != selection->tracks.end());
-                
-                (*i)->set_selected (yn);
-                
-                TimeAxisView::Children c = (*i)->get_child_list ();
-                for (TimeAxisView::Children::iterator j = c.begin(); j != c.end(); ++j) {
-                        (*j)->set_selected (find (selection->tracks.begin(), selection->tracks.end(), j->get()) != selection->tracks.end());
-                }
-
-                if (yn && 
-                    ((mouse_mode == MouseRange) || 
-                     ((mouse_mode == MouseObject) && (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT)))) {
-                        (*i)->reshow_selection (selection->time);
-                } else {
-                        (*i)->hide_selection ();
-                }
-        }
+               bool yn = (find (selection->tracks.begin(), selection->tracks.end(), *i) != selection->tracks.end());
+
+               (*i)->set_selected (yn);
+
+               TimeAxisView::Children c = (*i)->get_child_list ();
+               for (TimeAxisView::Children::iterator j = c.begin(); j != c.end(); ++j) {
+                       (*j)->set_selected (find (selection->tracks.begin(), selection->tracks.end(), j->get()) != selection->tracks.end());
+               }
+
+               if (yn) {
+                       (*i)->reshow_selection (selection->time);
+               } else {
+                       (*i)->hide_selection ();
+               }
+
+
+               if (yn) {
+                       RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*i);
+                       if (rtav) {
+                               routes->push_back (rtav->route());
+                               stripables->push_back (rtav->route());
+                       }
+               }
+       }
 
        ActionManager::set_sensitive (ActionManager::track_selection_sensitive_actions, !selection->tracks.empty());
+
+       sensitize_the_right_region_actions (false);
+
+       /* notify control protocols */
+
+       ControlProtocol::StripableSelectionChanged (stripables);
+
+       if (sfbrowser && _session && !_session->deletion_in_progress()) {
+               uint32_t audio_track_cnt = 0;
+               uint32_t midi_track_cnt = 0;
+
+               for (TrackSelection::iterator x = selection->tracks.begin(); x != selection->tracks.end(); ++x) {
+                       AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*>(*x);
+
+                       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++;
+                                       }
+                               }
+                       }
+               }
+
+               sfbrowser->reset (audio_track_cnt, midi_track_cnt);
+       }
 }
 
 void
 Editor::time_selection_changed ()
 {
-       if (Profile->get_sae()) {
-               return;
-       }
+       /* XXX this is superficially inefficient. Hide the selection in all
+        * tracks, then show it in all selected tracks.
+        *
+        * However, if you investigate what this actually does, it isn't
+        * anywhere nearly as bad as it may appear. Remember: nothing is
+        * redrawn or even recomputed during these two loops - that only
+        * happens when we next render ...
+        */
 
        for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
                (*i)->hide_selection ();
@@ -926,6 +1064,19 @@ Editor::time_selection_changed ()
        } else {
                ActionManager::set_sensitive (ActionManager::time_selection_sensitive_actions, true);
        }
+
+       /* propagate into backend, but only when there is no drag or we are at
+        * the end of a drag, otherwise this is too expensive (could case a
+        * locate per mouse motion event.
+        */
+
+       if (_session && !_drags->active()) {
+               if (selection->time.length() != 0) {
+                       _session->set_range_selection (selection->time.start(), selection->time.end_frame());
+               } else {
+                       _session->clear_range_selection ();
+               }
+       }
 }
 
 /** Set all region actions to have a given sensitivity */
@@ -941,43 +1092,96 @@ Editor::sensitize_all_region_actions (bool s)
        _all_region_actions_sensitized = s;
 }
 
-/** Sensitize region-based actions based on the selection ONLY, ignoring the entered_regionview.
- *  This method should be called just before displaying a Region menu.  When a Region menu is not
- *  currently being shown, all region actions are sensitized so that hotkey-triggered actions
- *  on entered_regionviews work without having to check sensitivity every time the selection or
- *  entered_regionview changes.
+/** Sensitize region-based actions.
  *
- *  This method also sets up toggle action state as appropriate.
+ *  This method is called from whenever we leave the canvas, either by moving
+ *  the pointer out of it, or by popping up a context menu. See
+ *  Editor::{entered,left}_track_canvas() for details there.
  */
 void
-Editor::sensitize_the_right_region_actions ()
+Editor::sensitize_the_right_region_actions (bool because_canvas_crossing)
 {
-       if ((mouse_mode == MouseRange) || (mouse_mode != MouseObject && _join_object_range_state == JOIN_OBJECT_RANGE_RANGE)) {
-               sensitize_all_region_actions (false);
-               if (!selection->time.empty()) {
-                       _region_actions->get_action("split-region")->set_sensitive (true);
-               }
-               
-               return;
+       bool have_selection = false;
+       bool have_entered = false;
+       bool have_edit_point = false;
+       RegionSelection rs;
 
-       } else if (mouse_mode != MouseObject) {
-               sensitize_all_region_actions (false);
-               return;
+       // std::cerr << "STRRA: crossing ? " << because_canvas_crossing << " within ? " << within_track_canvas
+       // << std::endl;
+
+       if (!selection->regions.empty()) {
+               have_selection = true;
+               rs = selection->regions;
+       }
+
+       if (entered_regionview) {
+               have_entered = true;
+               rs.add (entered_regionview);
+       }
+
+       if (rs.empty() && !selection->tracks.empty()) {
+
+               /* no selected regions, but some selected tracks.
+                */
+
+               if (_edit_point == EditAtMouse) {
+                       if (!within_track_canvas) {
+                               /* pointer is not in canvas, so edit point is meaningless */
+                               have_edit_point = false;
+                       } else {
+                               /* inside canvas. we don't know where the edit
+                                  point will be when an action is invoked, but
+                                  assume it could intersect with a region.
+                               */
+                               have_edit_point = true;
+                       }
+               } else {
+                       RegionSelection at_edit_point;
+                       framepos_t const where = get_preferred_edit_position (Editing::EDIT_IGNORE_NONE, false, !within_track_canvas);
+                       get_regions_at (at_edit_point, where, selection->tracks);
+                       if (!at_edit_point.empty()) {
+                               have_edit_point = true;
+                       }
+                       if (rs.empty()) {
+                               rs.insert (rs.end(), at_edit_point.begin(), at_edit_point.end());
+                       }
+               }
        }
 
-       /* We get here if we are in Object mode */
-                       
-       RegionSelection rs = get_regions_from_selection_and_entered ();
-       sensitize_all_region_actions (!rs.empty ());
+       //std::cerr << "\tfinal have selection: " << have_selection
+       // << " have entered " << have_entered
+       // << " have edit point " << have_edit_point
+       // << " EP = " << enum_2_string (_edit_point)
+       // << std::endl;
+
+       typedef std::map<std::string,RegionAction> RegionActionMap;
 
        _ignore_region_action = true;
-       
+
+       for (RegionActionMap::iterator x = region_action_map.begin(); x != region_action_map.end(); ++x) {
+               RegionActionTarget tgt = x->second.target;
+               bool sensitive = false;
+
+               if ((tgt & SelectedRegions) && have_selection) {
+                       sensitive = true;
+               } else if ((tgt & EnteredRegions) && have_entered) {
+                       sensitive = true;
+               } else if ((tgt & EditPointRegions) && have_edit_point) {
+                       sensitive = true;
+               }
+
+               x->second.action->set_sensitive (sensitive);
+       }
+
        /* Look through the regions that are selected and make notes about what we have got */
-       
+
        bool have_audio = false;
+       bool have_multichannel_audio = false;
        bool have_midi = false;
        bool have_locked = false;
        bool have_unlocked = false;
+       bool have_video_locked = false;
+       bool have_video_unlocked = false;
        bool have_position_lock_style_audio = false;
        bool have_position_lock_style_music = false;
        bool have_muted = false;
@@ -985,31 +1189,48 @@ Editor::sensitize_the_right_region_actions ()
        bool have_opaque = false;
        bool have_non_opaque = false;
        bool have_not_at_natural_position = false;
-       bool have_envelope_visible = false;
-       bool have_envelope_invisible = false;
        bool have_envelope_active = false;
        bool have_envelope_inactive = false;
        bool have_non_unity_scale_amplitude = false;
+       bool have_compound_regions = false;
+       bool have_inactive_fade_in = false;
+       bool have_inactive_fade_out = false;
+       bool have_active_fade_in = false;
+       bool have_active_fade_out = false;
+       bool have_transients = false;
 
        for (list<RegionView*>::const_iterator i = rs.begin(); i != rs.end(); ++i) {
 
                boost::shared_ptr<Region> r = (*i)->region ();
                boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion> (r);
-               
+
                if (ar) {
                        have_audio = true;
+                       if (ar->n_channels() > 1) {
+                               have_multichannel_audio = true;
+                       }
                }
-               
+
                if (boost::dynamic_pointer_cast<MidiRegion> (r)) {
                        have_midi = true;
                }
 
+               if (r->is_compound()) {
+                       have_compound_regions = true;
+               }
+
                if (r->locked()) {
                        have_locked = true;
                } else {
                        have_unlocked = true;
                }
 
+               if (r->video_locked()) {
+                       have_video_locked = true;
+               } else {
+                       have_video_unlocked = true;
+               }
+
                if (r->position_lock_style() == MusicTime) {
                        have_position_lock_style_music = true;
                } else {
@@ -1032,21 +1253,11 @@ Editor::sensitize_the_right_region_actions ()
                        have_not_at_natural_position = true;
                }
 
-               if (ar) {
-                        /* its a bit unfortunate that "envelope visible" is a view-only
-                           property. we have to find the regionview to able to check
-                           its current setting.
-                        */
-
-                        have_envelope_invisible = true;
-
-                        if (*i) {
-                                AudioRegionView* arv = dynamic_cast<AudioRegionView*> (*i);
-                                if (arv && arv->envelope_visible()) {
-                                        have_envelope_visible = true;
-                                }
-                       }
+               if (r->has_transients ()){
+                       have_transients = true;
+               }
 
+               if (ar) {
                        if (ar->envelope_active()) {
                                have_envelope_active = true;
                        } else {
@@ -1056,78 +1267,126 @@ Editor::sensitize_the_right_region_actions ()
                        if (ar->scale_amplitude() != 1) {
                                have_non_unity_scale_amplitude = true;
                        }
+
+                       if (ar->fade_in_active ()) {
+                               have_active_fade_in = true;
+                       } else {
+                               have_inactive_fade_in = true;
+                       }
+
+                       if (ar->fade_out_active ()) {
+                               have_active_fade_out = true;
+                       } else {
+                               have_inactive_fade_out = true;
+                       }
                }
        }
 
+       _region_actions->get_action("split-region-at-transients")->set_sensitive (have_transients);
+
        if (rs.size() > 1) {
                _region_actions->get_action("show-region-list-editor")->set_sensitive (false);
                _region_actions->get_action("show-region-properties")->set_sensitive (false);
                _region_actions->get_action("rename-region")->set_sensitive (false);
+               if (have_audio) {
+                       /* XXX need to check whether there is than 1 per
+                          playlist, because otherwise this makes no sense.
+                       */
+                       _region_actions->get_action("combine-regions")->set_sensitive (true);
+               } else {
+                       _region_actions->get_action("combine-regions")->set_sensitive (false);
+               }
        } else if (rs.size() == 1) {
                _region_actions->get_action("add-range-markers-from-region")->set_sensitive (false);
                _region_actions->get_action("close-region-gaps")->set_sensitive (false);
-       } 
+               _region_actions->get_action("combine-regions")->set_sensitive (false);
+       }
+
+       if (!have_multichannel_audio) {
+               _region_actions->get_action("split-multichannel-region")->set_sensitive (false);
+       }
 
        if (!have_midi) {
+               editor_menu_actions->get_action("RegionMenuMIDI")->set_sensitive (false);
                _region_actions->get_action("show-region-list-editor")->set_sensitive (false);
                _region_actions->get_action("quantize-region")->set_sensitive (false);
+               _region_actions->get_action("legatize-region")->set_sensitive (false);
+               _region_actions->get_action("remove-overlap")->set_sensitive (false);
+               _region_actions->get_action("transform-region")->set_sensitive (false);
                _region_actions->get_action("fork-region")->set_sensitive (false);
+               _region_actions->get_action("insert-patch-change-context")->set_sensitive (false);
+               _region_actions->get_action("insert-patch-change")->set_sensitive (false);
+               _region_actions->get_action("transpose-region")->set_sensitive (false);
+       } else {
+               editor_menu_actions->get_action("RegionMenuMIDI")->set_sensitive (true);
+               /* others were already marked sensitive */
        }
 
-       if (_edit_point == EditAtMouse) {
-               _region_actions->get_action("set-region-sync-position")->set_sensitive (false);
-               _region_actions->get_action("trim-front")->set_sensitive (false);
-               _region_actions->get_action("trim-back")->set_sensitive (false);
-               _region_actions->get_action("split-region")->set_sensitive (false);
-               _region_actions->get_action("place-transient")->set_sensitive (false);
+       /* ok, moving along... */
+
+       if (have_compound_regions) {
+               _region_actions->get_action("uncombine-regions")->set_sensitive (true);
+       } else {
+               _region_actions->get_action("uncombine-regions")->set_sensitive (false);
        }
 
        if (have_audio) {
-               
-               if (have_envelope_visible && !have_envelope_invisible) {
-                       Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-gain-envelope-visible"))->set_active ();
-               } else if (have_envelope_visible && have_envelope_invisible) {
-//                     _region_actions->get_action("toggle-region-gain-envelope-visible")->set_inconsistent ();
-               }
-               
+
                if (have_envelope_active && !have_envelope_inactive) {
                        Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-gain-envelope-active"))->set_active ();
                } else if (have_envelope_active && have_envelope_inactive) {
-//                     _region_actions->get_action("toggle-region-gain-envelope-active")->set_inconsistent ();
+                       // Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-gain-envelope-active"))->set_inconsistent ();
                }
-       
+
        } else {
-               
-               _region_actions->get_action("analyze-region")->set_sensitive (false);
+
+               _region_actions->get_action("loudness-analyze-region")->set_sensitive (false);
+               _region_actions->get_action("spectral-analyze-region")->set_sensitive (false);
                _region_actions->get_action("reset-region-gain-envelopes")->set_sensitive (false);
-               _region_actions->get_action("toggle-region-gain-envelope-visible")->set_sensitive (false);
                _region_actions->get_action("toggle-region-gain-envelope-active")->set_sensitive (false);
-               
+               _region_actions->get_action("pitch-shift-region")->set_sensitive (false);
+               _region_actions->get_action("strip-region-silence")->set_sensitive (false);
+               _region_actions->get_action("show-rhythm-ferret")->set_sensitive (false);
+
        }
 
        if (!have_non_unity_scale_amplitude || !have_audio) {
                _region_actions->get_action("reset-region-scale-amplitude")->set_sensitive (false);
        }
-               
-       Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-lock"))->set_active (have_locked && !have_unlocked);
+
+       Glib::RefPtr<ToggleAction> a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-lock"));
+       a->set_active (have_locked && !have_unlocked);
        if (have_locked && have_unlocked) {
-//             _region_actions->get_action("toggle-region-lock")->set_inconsistent ();
+               // a->set_inconsistent ();
        }
 
-       Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-lock-style"))->set_active (have_position_lock_style_music && !have_position_lock_style_audio);
-               
-       if (have_position_lock_style_music && have_position_lock_style_audio) {
-//             _region_actions->get_action("toggle-region-lock-style")->set_inconsistent ();
+       a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-video-lock"));
+       a->set_active (have_video_locked && !have_video_unlocked);
+       if (have_video_locked && have_video_unlocked) {
+               // a->set_inconsistent ();
        }
 
-       Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-mute"))->set_active (have_muted && !have_unmuted);
+       a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-lock-style"));
+       a->set_active (have_position_lock_style_music && !have_position_lock_style_audio);
+
+       vector<Widget*> proxies = a->get_proxies();
+       for (vector<Widget*>::iterator p = proxies.begin(); p != proxies.end(); ++p) {
+               Gtk::CheckMenuItem* cmi = dynamic_cast<Gtk::CheckMenuItem*> (*p);
+               if (cmi) {
+                       cmi->set_inconsistent (have_position_lock_style_music && have_position_lock_style_audio);
+               }
+       }
+
+       a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-mute"));
+       a->set_active (have_muted && !have_unmuted);
        if (have_muted && have_unmuted) {
-//             _region_actions->get_action("toggle-region-mute")->set_inconsistent ();
+               // a->set_inconsistent ();
        }
-        
-       Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-opaque-region"))->set_active (have_opaque && !have_non_opaque);
+
+       a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-opaque-region"));
+       a->set_active (have_opaque && !have_non_opaque);
        if (have_opaque && have_non_opaque) {
-//                     _region_actions->get_action("toggle-opaque-region")->set_inconsistent ();
+               // a->set_inconsistent ();
        }
 
        if (!have_not_at_natural_position) {
@@ -1141,12 +1400,34 @@ Editor::sensitize_the_right_region_actions ()
                _region_actions->get_action("insert-region-from-region-list")->set_sensitive (true);
        }
 
+       a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-fade-in"));
+       a->set_active (have_active_fade_in && !have_inactive_fade_in);
+       if (have_active_fade_in && have_inactive_fade_in) {
+               // a->set_inconsistent ();
+       }
+
+       a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-fade-out"));
+       a->set_active (have_active_fade_out && !have_inactive_fade_out);
+
+       if (have_active_fade_out && have_inactive_fade_out) {
+               // a->set_inconsistent ();
+       }
+
+       bool const have_active_fade = have_active_fade_in || have_active_fade_out;
+       bool const have_inactive_fade = have_inactive_fade_in || have_inactive_fade_out;
+
+       a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-fades"));
+       a->set_active (have_active_fade && !have_inactive_fade);
+
+       if (have_active_fade && have_inactive_fade) {
+               // a->set_inconsistent ();
+       }
+
        _ignore_region_action = false;
-       
+
        _all_region_actions_sensitized = false;
 }
 
-
 void
 Editor::region_selection_changed ()
 {
@@ -1161,19 +1442,25 @@ Editor::region_selection_changed ()
                (*i)->set_selected_regionviews (selection->regions);
        }
 
-        if (_region_selection_change_updates_region_list) {
-                _regions->set_selected (selection->regions);
-        }
+       if (_region_selection_change_updates_region_list) {
+               _regions->set_selected (selection->regions);
+       }
 
        _regions->block_change_connection (false);
        editor_regions_selection_changed_connection.block(false);
 
-       if (!_all_region_actions_sensitized) {
-               /* This selection change might have changed what region actions
-                  are allowed, so sensitize them all in case a key is pressed.
-               */
-               sensitize_all_region_actions (true);
+       sensitize_the_right_region_actions (false);
+
+       /* propagate into backend */
+
+       if (_session) {
+               if (!selection->regions.empty()) {
+                       _session->set_object_selection (selection->regions.start(), selection->regions.end_frame());
+               } else {
+                       _session->clear_object_selection ();
+               }
        }
+
 }
 
 void
@@ -1193,6 +1480,8 @@ Editor::select_all_in_track (Selection::Operation op)
                return;
        }
 
+       begin_reversible_selection_op (X_("Select All in Track"));
+
        clicked_routeview->get_selectables (0, max_framepos, 0, DBL_MAX, touched);
 
        switch (op) {
@@ -1209,20 +1498,53 @@ Editor::select_all_in_track (Selection::Operation op)
                selection->add (touched);
                break;
        }
+
+       commit_reversible_selection_op ();
+}
+
+bool
+Editor::select_all_internal_edit (Selection::Operation)
+{
+       bool selected = false;
+
+       for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
+               MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
+               if (mrv) {
+                       mrv->select_all_notes ();
+                       selected = true;
+               }
+       }
+
+       MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(entered_regionview);
+       if (mrv) {
+               mrv->select_all_notes ();
+               selected = true;
+       }
+
+       return selected;
 }
 
 void
-Editor::select_all (Selection::Operation op)
+Editor::select_all_objects (Selection::Operation op)
 {
        list<Selectable *> touched;
 
-       for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
+       TrackViewList ts  = track_views;
+
+       if (internal_editing() && select_all_internal_edit(op)) {
+               return;  // Selected notes
+       }
+
+       for (TrackViewList::iterator iter = ts.begin(); iter != ts.end(); ++iter) {
                if ((*iter)->hidden()) {
                        continue;
                }
                (*iter)->get_selectables (0, max_framepos, 0, DBL_MAX, touched);
+               selection->add (*iter);
        }
-       begin_reversible_command (_("select all"));
+
+
+       begin_reversible_selection_op (X_("select all"));
        switch (op) {
        case Selection::Add:
                selection->add (touched);
@@ -1237,8 +1559,9 @@ Editor::select_all (Selection::Operation op)
                /* meaningless, because we're selecting everything */
                break;
        }
-       commit_reversible_command ();
+       commit_reversible_selection_op ();
 }
+
 void
 Editor::invert_selection_in_track ()
 {
@@ -1248,8 +1571,10 @@ Editor::invert_selection_in_track ()
                return;
        }
 
+       begin_reversible_selection_op (X_("Invert Selection in Track"));
        clicked_routeview->get_inverted_selectables (*selection, touched);
        selection->set (touched);
+       commit_reversible_selection_op ();
 }
 
 void
@@ -1257,6 +1582,16 @@ Editor::invert_selection ()
 {
        list<Selectable *> touched;
 
+       if (internal_editing()) {
+               for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
+                       MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
+                       if (mrv) {
+                               mrv->invert_selection ();
+                       }
+               }
+               return;
+       }
+
        for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
                if ((*iter)->hidden()) {
                        continue;
@@ -1264,7 +1599,9 @@ Editor::invert_selection ()
                (*iter)->get_inverted_selectables (*selection, touched);
        }
 
+       begin_reversible_selection_op (X_("Invert Selection"));
        selection->set (touched);
+       commit_reversible_selection_op ();
 }
 
 /** @param start Start time in session frames.
@@ -1274,15 +1611,13 @@ Editor::invert_selection ()
  *  @param preserve_if_selected true to leave the current selection alone if we're adding to the selection and all of the selectables
  *  within the region are already selected.
  */
-bool
-Editor::select_all_within (
-       framepos_t start, framepos_t end, double top, double bot, const TrackViewList& tracklist, Selection::Operation op, bool preserve_if_selected
-       )
+void
+Editor::select_all_within (framepos_t start, framepos_t end, double top, double bot, const TrackViewList& tracklist, Selection::Operation op, bool preserve_if_selected)
 {
        list<Selectable*> found;
 
        for (TrackViewList::const_iterator iter = tracklist.begin(); iter != tracklist.end(); ++iter) {
-               
+
                if ((*iter)->hidden()) {
                        continue;
                }
@@ -1291,21 +1626,23 @@ Editor::select_all_within (
        }
 
        if (found.empty()) {
-               return false;
+               selection->clear_objects();
+               selection->clear_time ();
+               return;
        }
 
        if (preserve_if_selected && op != Selection::Toggle) {
                list<Selectable*>::iterator i = found.begin();
-               while (i != found.end() && (*i)->get_selected()) {
+               while (i != found.end() && (*i)->selected()) {
                        ++i;
                }
 
                if (i == found.end()) {
-                       return false;
+                       return;
                }
        }
 
-       begin_reversible_command (_("select all within"));
+       begin_reversible_selection_op (X_("select all within"));
        switch (op) {
        case Selection::Add:
                selection->add (found);
@@ -1321,9 +1658,7 @@ Editor::select_all_within (
                break;
        }
 
-       commit_reversible_command ();
-
-       return !found.empty();
+       commit_reversible_selection_op ();
 }
 
 void
@@ -1333,10 +1668,26 @@ Editor::set_selection_from_region ()
                return;
        }
 
-       selection->set (selection->regions.start(), selection->regions.end_frame());
-       if (!Profile->get_sae()) {
-               set_mouse_mode (Editing::MouseRange, false);
+       /* find all the tracks that have selected regions */
+
+       set<TimeAxisView*> tracks;
+
+       for (RegionSelection::const_iterator r = selection->regions.begin(); r != selection->regions.end(); ++r) {
+               tracks.insert (&(*r)->get_time_axis_view());
        }
+
+       TrackViewList tvl;
+       tvl.insert (tvl.end(), tracks.begin(), tracks.end());
+
+       /* select range (this will clear the region selection) */
+
+       selection->set (selection->regions.start(), selection->regions.end_frame());
+
+       /* and select the tracks */
+
+       selection->set (tvl);
+
+       set_mouse_mode (Editing::MouseRange, false);
 }
 
 void
@@ -1365,13 +1716,11 @@ Editor::set_selection_from_loop()
 void
 Editor::set_selection_from_range (Location& loc)
 {
-       begin_reversible_command (_("set selection from range"));
+       begin_reversible_selection_op (X_("set selection from range"));
        selection->set (loc.start(), loc.end());
-       commit_reversible_command ();
+       commit_reversible_selection_op ();
 
-       if (!Profile->get_sae()) {
-               set_mouse_mode (Editing::MouseRange, false);
-       }
+       set_mouse_mode (Editing::MouseRange, false);
 }
 
 void
@@ -1405,9 +1754,9 @@ Editor::select_all_selectables_using_time_selection ()
                (*iter)->get_selectables (start, end - 1, 0, DBL_MAX, touched);
        }
 
-       begin_reversible_command (_("select all from range"));
+       begin_reversible_selection_op (X_("select all from range"));
        selection->set (touched);
-       commit_reversible_command ();
+       commit_reversible_selection_op ();
 }
 
 
@@ -1436,9 +1785,9 @@ Editor::select_all_selectables_using_punch()
                }
                (*iter)->get_selectables (location->start(), location->end() - 1, 0, DBL_MAX, touched);
        }
-       begin_reversible_command (_("select all from punch"));
+       begin_reversible_selection_op (X_("select all from punch"));
        selection->set (touched);
-       commit_reversible_command ();
+       commit_reversible_selection_op ();
 
 }
 
@@ -1467,33 +1816,46 @@ Editor::select_all_selectables_using_loop()
                }
                (*iter)->get_selectables (location->start(), location->end() - 1, 0, DBL_MAX, touched);
        }
-       begin_reversible_command (_("select all from loop"));
+       begin_reversible_selection_op (X_("select all from loop"));
        selection->set (touched);
-       commit_reversible_command ();
+       commit_reversible_selection_op ();
 
 }
 
 void
 Editor::select_all_selectables_using_cursor (EditorCursor *cursor, bool after)
 {
-        framepos_t start;
+       framepos_t start;
        framepos_t end;
        list<Selectable *> touched;
 
        if (after) {
-               begin_reversible_command (_("select all after cursor"));
-               start = cursor->current_frame ;
+               start = cursor->current_frame();
                end = _session->current_end_frame();
        } else {
-               if (cursor->current_frame > 0) {
-                       begin_reversible_command (_("select all before cursor"));
+               if (cursor->current_frame() > 0) {
                        start = 0;
-                       end = cursor->current_frame - 1;
+                       end = cursor->current_frame() - 1;
                } else {
                        return;
                }
        }
 
+       if (internal_editing()) {
+               for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
+                       MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
+                       if (mrv) {
+                               mrv->select_range (start, end);
+                       }
+               }
+               return;
+       }
+
+       if (after) {
+               begin_reversible_selection_op (X_("select all after cursor"));
+       } else {
+               begin_reversible_selection_op (X_("select all before cursor"));
+       }
 
        TrackViewList* ts;
 
@@ -1510,23 +1872,21 @@ Editor::select_all_selectables_using_cursor (EditorCursor *cursor, bool after)
                (*iter)->get_selectables (start, end, 0, DBL_MAX, touched);
        }
        selection->set (touched);
-       commit_reversible_command ();
+       commit_reversible_selection_op ();
 }
 
 void
-Editor::select_all_selectables_using_edit (bool after)
+Editor::select_all_selectables_using_edit (bool after, bool from_context_menu)
 {
-        framepos_t start;
+       framepos_t start;
        framepos_t end;
        list<Selectable *> touched;
 
        if (after) {
-               begin_reversible_command (_("select all after edit"));
-               start = get_preferred_edit_position();
+               start = get_preferred_edit_position(EDIT_IGNORE_NONE, from_context_menu);
                end = _session->current_end_frame();
        } else {
-               if ((end = get_preferred_edit_position()) > 1) {
-                       begin_reversible_command (_("select all before edit"));
+               if ((end = get_preferred_edit_position(EDIT_IGNORE_NONE, from_context_menu)) > 1) {
                        start = 0;
                        end -= 1;
                } else {
@@ -1534,6 +1894,19 @@ Editor::select_all_selectables_using_edit (bool after)
                }
        }
 
+       if (internal_editing()) {
+               for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
+                       MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
+                       mrv->select_range (start, end);
+               }
+               return;
+       }
+
+       if (after) {
+               begin_reversible_selection_op (X_("select all after edit"));
+       } else {
+               begin_reversible_selection_op (X_("select all before edit"));
+       }
 
        TrackViewList* ts;
 
@@ -1550,13 +1923,13 @@ Editor::select_all_selectables_using_edit (bool after)
                (*iter)->get_selectables (start, end, 0, DBL_MAX, touched);
        }
        selection->set (touched);
-       commit_reversible_command ();
+       commit_reversible_selection_op ();
 }
 
 void
-Editor::select_all_selectables_between (bool /*within*/)
+Editor::select_all_selectables_between (bool within)
 {
-        framepos_t start;
+       framepos_t start;
        framepos_t end;
        list<Selectable *> touched;
 
@@ -1564,6 +1937,14 @@ Editor::select_all_selectables_between (bool /*within*/)
                return;
        }
 
+       if (internal_editing()) {
+               for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
+                       MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
+                       mrv->select_range (start, end);
+               }
+               return;
+       }
+
        TrackViewList* ts;
 
        if (selection->tracks.empty()) {
@@ -1576,141 +1957,150 @@ Editor::select_all_selectables_between (bool /*within*/)
                if ((*iter)->hidden()) {
                        continue;
                }
-               (*iter)->get_selectables (start, end, 0, DBL_MAX, touched);
+               (*iter)->get_selectables (start, end, 0, DBL_MAX, touched, within);
        }
 
+       begin_reversible_selection_op (X_("Select all Selectables Between"));
        selection->set (touched);
+       commit_reversible_selection_op ();
 }
 
 void
 Editor::select_range_between ()
 {
-        framepos_t start;
+       framepos_t start;
        framepos_t end;
 
-        if (mouse_mode == MouseRange && !selection->time.empty()) {
-                selection->clear_time ();
-        }
+       if ( !selection->time.empty() ) {
+               selection->clear_time ();
+       }
 
        if (!get_edit_op_range (start, end)) {
                return;
        }
 
+       begin_reversible_selection_op (X_("Select Range Between"));
        set_mouse_mode (MouseRange);
        selection->set (start, end);
+       commit_reversible_selection_op ();
 }
 
 bool
 Editor::get_edit_op_range (framepos_t& start, framepos_t& end) const
 {
-       framepos_t m;
-       bool ignored;
+//     framepos_t m;
+//     bool ignored;
 
-       /* in range mode, use any existing selection */
+       /* if an explicit range exists, use it */
 
-       if (mouse_mode == MouseRange && !selection->time.empty()) {
+       if ( (mouse_mode == MouseRange || get_smart_mode() ) &&  !selection->time.empty()) {
                /* we know that these are ordered */
                start = selection->time.start();
                end = selection->time.end_frame();
                return true;
-       }
-
-       if (!mouse_frame (m, ignored)) {
-               /* mouse is not in a canvas, try playhead+selected marker.
-                  this is probably most true when using menus.
-                */
-
-               if (selection->markers.empty()) {
-                       return false;
-               }
-
-               start = selection->markers.front()->position();
-               end = _session->audible_frame();
-
        } else {
-
-               switch (_edit_point) {
-               case EditAtPlayhead:
-                       if (selection->markers.empty()) {
-                               /* use mouse + playhead */
-                               start = m;
-                               end = _session->audible_frame();
-                       } else {
-                               /* use playhead + selected marker */
-                               start = _session->audible_frame();
-                               end = selection->markers.front()->position();
-                       }
-                       break;
-
-               case EditAtMouse:
-                       /* use mouse + selected marker */
-                       if (selection->markers.empty()) {
-                               start = m;
-                               end = _session->audible_frame();
-                       } else {
-                               start = selection->markers.front()->position();
-                               end = m;
-                       }
-                       break;
-
-               case EditAtSelectedMarker:
-                       /* use mouse + selected marker */
-                       if (selection->markers.empty()) {
-
-                               MessageDialog win (_("No edit range defined"),
-                                                  false,
-                                                  MESSAGE_INFO,
-                                                  BUTTONS_OK);
-
-                               win.set_secondary_text (
-                                       _("the edit point is Selected Marker\nbut there is no selected marker."));
-
-
-                               win.set_default_response (RESPONSE_CLOSE);
-                               win.set_position (Gtk::WIN_POS_MOUSE);
-                               win.show_all();
-
-                               win.run ();
-
-                               return false; // NO RANGE
-                       }
-                       start = selection->markers.front()->position();
-                       end = m;
-                       break;
-               }
-       }
-
-       if (start == end) {
+               start = 0;
+               end = 0;
                return false;
        }
 
-       if (start > end) {
-               swap (start, end);
-       }
+//     if (!mouse_frame (m, ignored)) {
+//             /* mouse is not in a canvas, try playhead+selected marker.
+//                this is probably most true when using menus.
+//             */
+//
+//             if (selection->markers.empty()) {
+//                     return false;
+//             }
+
+//             start = selection->markers.front()->position();
+//             end = _session->audible_frame();
+
+//     } else {
+
+//             switch (_edit_point) {
+//             case EditAtPlayhead:
+//                     if (selection->markers.empty()) {
+//                             /* use mouse + playhead */
+//                             start = m;
+//                             end = _session->audible_frame();
+//                     } else {
+//                             /* use playhead + selected marker */
+//                             start = _session->audible_frame();
+//                             end = selection->markers.front()->position();
+//                     }
+//                     break;
+
+//             case EditAtMouse:
+//                     /* use mouse + selected marker */
+//                     if (selection->markers.empty()) {
+//                             start = m;
+//                             end = _session->audible_frame();
+//                     } else {
+//                             start = selection->markers.front()->position();
+//                             end = m;
+//                     }
+//                     break;
+
+//             case EditAtSelectedMarker:
+//                     /* use mouse + selected marker */
+//                     if (selection->markers.empty()) {
+
+//                             MessageDialog win (_("No edit range defined"),
+//                                                false,
+//                                                MESSAGE_INFO,
+//                                                BUTTONS_OK);
+
+//                             win.set_secondary_text (
+//                                     _("the edit point is Selected Marker\nbut there is no selected marker."));
+
+
+//                             win.set_default_response (RESPONSE_CLOSE);
+//                             win.set_position (Gtk::WIN_POS_MOUSE);
+//                             win.show_all();
+
+//                             win.run ();
+
+//                             return false; // NO RANGE
+//                     }
+//                     start = selection->markers.front()->position();
+//                     end = m;
+//                     break;
+//             }
+//     }
+
+//     if (start == end) {
+//             return false;
+//     }
+
+//     if (start > end) {
+//             swap (start, end);
+//     }
 
        /* turn range into one delimited by start...end,
           not start...end-1
        */
 
-       end++;
+//     end++;
 
-       return true;
+//     return true;
 }
 
 void
 Editor::deselect_all ()
 {
+       begin_reversible_selection_op (X_("Deselect All"));
        selection->clear ();
+       commit_reversible_selection_op ();
 }
 
 long
-Editor::select_range_around_region (RegionView* rv)
+Editor::select_range (framepos_t s, framepos_t e)
 {
-       assert (rv);
-       
-       selection->set (&rv->get_time_axis_view());
-       
+       begin_reversible_selection_op (X_("Select Range"));
+       selection->add (clicked_axisview);
        selection->time.clear ();
-       boost::shared_ptr<Region> r = rv->region ();
-       return selection->set (r->position(), r->position() + r->length());
+       long ret = selection->set (s, e);
+       commit_reversible_selection_op ();
+       return ret;
 }