Fix nested VCA assigns and mixer-layout:
[ardour.git] / gtk2_ardour / editor_selection.cc
index 2bcc8f1f2f638b81880be12697ec65ff0ffb01ec..f0ba5916d77ddd97b3b8c4f41607c2fb075b1ec0 100644 (file)
@@ -21,6 +21,7 @@
 #include <cstdlib>
 
 #include "pbd/stacktrace.h"
+#include "pbd/unwind.h"
 
 #include "ardour/midi_region.h"
 #include "ardour/playlist.h"
@@ -88,10 +89,6 @@ Editor::extend_selection_to_track (TimeAxisView& view)
 
        sorted.sort (cmp);
 
-       if (!selection->selected (&view)) {
-               to_be_added.push_back (&view);
-       }
-
        /* figure out if we should go forward or backwards */
 
        for (TrackViewList::iterator i = sorted.begin(); i != sorted.end(); ++i) {
@@ -157,6 +154,10 @@ Editor::extend_selection_to_track (TimeAxisView& view)
                }
        }
 
+       if (!selection->selected (&view)) {
+               to_be_added.push_back (&view);
+       }
+
        if (!to_be_added.empty()) {
                selection->add (to_be_added);
                return true;
@@ -174,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);
 }
 
@@ -891,6 +893,30 @@ out:
        return commit;
 }
 
+void
+Editor::set_selection (std::list<Selectable*> s, Selection::Operation op)
+{
+       if (s.empty()) {
+               return;
+       }
+       begin_reversible_selection_op (X_("set selection"));
+       switch (op) {
+               case Selection::Toggle:
+                       selection->toggle (s);
+                       break;
+               case Selection::Set:
+                       selection->set (s);
+                       break;
+               case Selection::Extend:
+                       selection->add (s);
+                       break;
+               case Selection::Add:
+                       selection->add (s);
+                       break;
+       }
+
+       commit_reversible_selection_op () ;
+}
 
 void
 Editor::set_selected_regionview_from_region_list (boost::shared_ptr<Region> region, Selection::Operation op)
@@ -955,18 +981,37 @@ Editor::set_selected_regionview_from_map_event (GdkEventAny* /*ev*/, StreamView*
        return true;
 }
 
+struct SelectionOrderSorter {
+       bool operator() (TimeAxisView const * const a, TimeAxisView const * const b) const  {
+               boost::shared_ptr<Stripable> sa = a->stripable ();
+               boost::shared_ptr<Stripable> sb = b->stripable ();
+               if (!sa && !sb) {
+                       return a < b;
+               }
+               if (!sa) {
+                       return false;
+               }
+               if (!sb) {
+                       return true;
+               }
+               return sa->presentation_info().selection_cnt() < sb->presentation_info().selection_cnt();
+       }
+};
+
 void
 Editor::track_selection_changed ()
 {
+       SelectionOrderSorter cmp;
+       selection->tracks.sort (cmp);
+
        switch (selection->tracks.size()) {
        case 0:
                break;
        default:
-               /* last element in selection list is the most recently
-                * selected, because we always append to that list.
-                */
                set_selected_mixer_strip (*(selection->tracks.back()));
-               ensure_time_axis_view_is_visible (*(selection->tracks.back()), false);
+               if (!_track_selection_change_without_scroll) {
+                       ensure_time_axis_view_is_visible (*(selection->tracks.back()), false);
+               }
                break;
        }
 
@@ -1002,6 +1047,8 @@ Editor::track_selection_changed ()
 
        ActionManager::set_sensitive (ActionManager::track_selection_sensitive_actions, !selection->tracks.empty());
 
+       sensitize_the_right_region_actions (false);
+
        /* notify control protocols */
 
        ControlProtocol::StripableSelectionChanged (stripables);
@@ -1086,25 +1133,87 @@ 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 is called from three places:
- *
- *    1. just before the top level Region menu is shown
- *    2. whenever the region selection changes
- *    3. just before popping up a track context menu
+/** 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 (bool from_context_menu, bool from_outside_canvas)
+Editor::sensitize_the_right_region_actions (bool because_canvas_crossing)
 {
-       PBD::stacktrace (cerr, 20);
-       RegionSelection rs = get_regions_from_selection_and_edit_point (Editing::EDIT_IGNORE_NONE, from_context_menu, from_outside_canvas);
+       bool have_selection = false;
+       bool have_entered = false;
+       bool have_edit_point = false;
+       RegionSelection rs;
+
+       // 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);
+       }
 
-       sensitize_all_region_actions (!rs.empty ());
+       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());
+                       }
+               }
+       }
+
+       //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;
@@ -1254,70 +1363,6 @@ Editor::sensitize_the_right_region_actions (bool from_context_menu, bool from_ou
                /* others were already marked sensitive */
        }
 
-       /* these actions all require a single location, taken from the edit
-        * point.
-        */
-
-       vector<string> edit_point_actions;
-       edit_point_actions.push_back ("set-region-sync-position");
-       edit_point_actions.push_back ("trim-front");
-       edit_point_actions.push_back ("trim-back");
-       edit_point_actions.push_back ("place-transient");
-       edit_point_actions.push_back ("align-regions-start");
-       edit_point_actions.push_back ("align-regions-start-relative");
-       edit_point_actions.push_back ("align-regions-end");
-       edit_point_actions.push_back ("align-regions-end-relative");
-       edit_point_actions.push_back ("align-regions-sync");
-       edit_point_actions.push_back ("align-regions-sync-relative");
-
-       /* They all use Editor::get_regions_from_selection_and_edit_point(),
-        * which is the same method used at the start of this to determine if
-        * any regions are eligible/valid for editing.
-        *
-        * there are two possible reasons why these actions should still
-        * be sensitive.
-        *
-        *  1. regions are selected
-        *  2. the edit point is inside some regions on selected tracks
-        *
-        * the first condition is satisfied if selection->regions is not empty.
-        * the second condition is satisfied if selection->regions is empty,
-        *    but @param rs (set above by the call to get_regions_from_selection_and_edit_point()
-        *    is not empty.
-        *
-        * if the mouse is the edit point, then whether get_regions_from_selection_and_edit_point()
-        * will have identified any regions will depend on the edit point setting.
-        *
-        * Editing::EditIgnoreOption that we passed to it, which in turn will
-        * depend on whether or not the pointer is still inside the track
-        * canvas. If it was, then the pointer position will determine whether
-        * or not any non-selected regions are considered eligible. If it
-        * wasn't, then we will ignore the pointer position and fallback on a
-        * secondary edit point (at this writing, that will likely be the
-        * playhead but see get_regions_from_selection_and_edit_point() to see
-        * how this works).
-        *
-        * if the edit point is not the mouse, then
-        * get_regions_from_selection_and_edit_point() will just do the right
-        * thing.
-        *
-        * However, the implementation of the actions will still use the edit
-        * point, ...
-        * 
-        */
-
-       bool sensitivity;
-
-       if (!selection->regions.empty() || !rs.empty()) { /* conditions 1 and 2 above */
-               sensitivity = true;
-       } else {
-               sensitivity = false;
-       }
-
-       for (vector<string>::const_iterator a = edit_point_actions.begin(); a != edit_point_actions.end(); ++a) {
-               _region_actions->get_action (*a)->set_sensitive (sensitivity);
-       }
-
        /* ok, moving along... */
 
        if (have_compound_regions) {
@@ -1445,7 +1490,7 @@ Editor::region_selection_changed ()
        _regions->block_change_connection (false);
        editor_regions_selection_changed_connection.block(false);
 
-       sensitize_the_right_region_actions (false, false);
+       sensitize_the_right_region_actions (false);
 
        /* propagate into backend */