Connect Script Manager "Edit" -> Lua Scripting Window
[ardour.git] / gtk2_ardour / editor_selection.cc
index 3a6277ececf16b2d4d19df1435caa481f196b08b..d214d48a320b42127c3a8c761276e270b451fe33 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"
@@ -43,7 +44,7 @@
 #include "midi_region_view.h"
 #include "sfdb_ui.h"
 
-#include "i18n.h"
+#include "pbd/i18n.h"
 
 using namespace std;
 using namespace ARDOUR;
@@ -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);
 }
 
@@ -192,17 +194,10 @@ Editor::set_selected_track_as_side_effect (Selection::Operation op)
                group = clicked_routeview->route()->route_group();
        }
 
-       bool had_tracks = !selection->tracks.empty();
-       RouteGroup& arg (_session->all_route_group());
-
        switch (op) {
        case Selection::Toggle:
                if (selection->selected (clicked_axisview)) {
-                       if (arg.is_select() && arg.is_active()) {
-                               for (TrackViewList::iterator i = track_views.begin(); i != track_views.end (); ++i) {
-                                       selection->remove(*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) {
                                                selection->remove(*i);
@@ -212,11 +207,7 @@ Editor::set_selected_track_as_side_effect (Selection::Operation op)
                                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) {
                                                selection->add(*i);
@@ -229,14 +220,7 @@ Editor::set_selected_track_as_side_effect (Selection::Operation op)
                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 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) {
                                        selection->add(*i);
@@ -249,14 +233,7 @@ Editor::set_selected_track_as_side_effect (Selection::Operation op)
 
        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()) {
+               if (group && group->is_active()) {
                        for (TrackViewList::iterator i  = track_views.begin(); i != track_views.end (); ++i) {
                                if ((*i)->route_group() == group) {
                                        selection->add(*i);
@@ -327,15 +304,27 @@ Editor::set_selected_control_point_from_click (bool press, Selection::Operation
        if (!clicked_control_point) {
                return false;
        }
+
        bool ret = false;
 
        switch (op) {
        case Selection::Set:
-               if (press) {
+               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:
                if (press) {
                        selection->add (clicked_control_point);
@@ -634,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);
                                        }
@@ -652,7 +641,7 @@ 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 {
@@ -662,7 +651,7 @@ Editor::set_selected_regionview_from_click (bool press, Selection::Operation op)
                                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::select.property_id);
+                                               get_equivalent_regions(clicked_regionview, all_equivalent_regions, ARDOUR::Properties::group_select.property_id);
                                                selection->set(all_equivalent_regions);
                                                commit = true;
                                        }
@@ -804,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 ();
+                                       int key = rtv->route()->presentation_info().order ();
 
                                        for (RegionSelection::iterator x = selection->regions.begin(); x != selection->regions.end(); ++x) {
 
@@ -819,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 ();
+                                                               int d = artv->route()->presentation_info().order ();
 
                                                                d -= key;
 
@@ -835,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 ();
+                                               int okey = closest->route()->presentation_info().order ();
 
                                                if (okey > key) {
                                                        swap (okey, key);
@@ -845,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 ();
+                                                               int k = artv->route()->presentation_info().order ();
 
                                                                if (k >= okey && k <= key) {
 
@@ -892,7 +881,12 @@ 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:
@@ -970,11 +964,18 @@ 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) {
 
@@ -998,15 +999,18 @@ Editor::track_selection_changed ()
                        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::TrackSelectionChanged (routes);
+       ControlProtocol::StripableSelectionChanged (stripables);
 
        if (sfbrowser && _session && !_session->deletion_in_progress()) {
                uint32_t audio_track_cnt = 0;
@@ -1038,10 +1042,6 @@ Editor::track_selection_changed ()
 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.
         *
@@ -1092,23 +1092,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 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)
 {
+       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);
+       }
 
-       RegionSelection rs = get_regions_from_selection_and_entered ();
-       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;
@@ -1133,6 +1197,7 @@ Editor::sensitize_the_right_region_actions ()
        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) {
 
@@ -1188,6 +1253,10 @@ Editor::sensitize_the_right_region_actions ()
                        have_not_at_natural_position = true;
                }
 
+               if (r->has_transients ()){
+                       have_transients = true;
+               }
+
                if (ar) {
                        if (ar->envelope_active()) {
                                have_envelope_active = true;
@@ -1213,6 +1282,8 @@ Editor::sensitize_the_right_region_actions ()
                }
        }
 
+       _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);
@@ -1251,13 +1322,7 @@ Editor::sensitize_the_right_region_actions ()
                /* 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);
@@ -1275,7 +1340,8 @@ Editor::sensitize_the_right_region_actions ()
 
        } 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-active")->set_sensitive (false);
                _region_actions->get_action("pitch-shift-region")->set_sensitive (false);
@@ -1303,8 +1369,12 @@ Editor::sensitize_the_right_region_actions ()
        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);
 
-       if (have_position_lock_style_music && have_position_lock_style_audio) {
-               // a->set_inconsistent ();
+       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"));
@@ -1358,7 +1428,6 @@ Editor::sensitize_the_right_region_actions ()
        _all_region_actions_sensitized = false;
 }
 
-
 void
 Editor::region_selection_changed ()
 {
@@ -1380,16 +1449,7 @@ Editor::region_selection_changed ()
        _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);
-       }
-
-       if (_session && !_session->transport_rolling() && !selection->regions.empty()) {
-               maybe_locate_with_edit_preroll (selection->regions.start());
-       }
+       sensitize_the_right_region_actions (false);
 
        /* propagate into backend */
 
@@ -1573,7 +1633,7 @@ Editor::select_all_within (framepos_t start, framepos_t end, double top, double
 
        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;
                }
 
@@ -1627,9 +1687,7 @@ Editor::set_selection_from_region ()
 
        selection->set (tvl);
 
-       if (!Profile->get_sae()) {
-               set_mouse_mode (Editing::MouseRange, false);
-       }
+       set_mouse_mode (Editing::MouseRange, false);
 }
 
 void
@@ -1662,9 +1720,7 @@ Editor::set_selection_from_range (Location& loc)
        selection->set (loc.start(), loc.end());
        commit_reversible_selection_op ();
 
-       if (!Profile->get_sae()) {
-               set_mouse_mode (Editing::MouseRange, false);
-       }
+       set_mouse_mode (Editing::MouseRange, false);
 }
 
 void