Fix crash when X11 is not available for VST UIs
[ardour.git] / gtk2_ardour / editor_selection.cc
index 79cda34f1551f37682f5595652d2ace50b1c493e..1e28d77e0b01743f80bb0383ee6b735d04393c1b 100644 (file)
@@ -1,21 +1,26 @@
 /*
-    Copyright (C) 2000-2006 Paul Davis
-
-    This program is free software; you can redistribute it and/or modify
-    it under the terms of the GNU General Public License as published by
-    the Free Software Foundation; either version 2 of the License, or
-    (at your option) any later version.
-
-    This program is distributed in the hope that it will be useful,
-    but WITHOUT ANY WARRANTY; without even the implied warranty of
-    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-    GNU General Public License for more details.
-
-    You should have received a copy of the GNU General Public License
-    along with this program; if not, write to the Free Software
-    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-
-*/
+ * Copyright (C) 2007-2012 Carl Hetherington <carl@carlh.net>
+ * Copyright (C) 2007-2015 David Robillard <d@drobilla.net>
+ * Copyright (C) 2007-2018 Paul Davis <paul@linuxaudiosystems.com>
+ * Copyright (C) 2013-2017 Nick Mainsbridge <mainsbridge@gmail.com>
+ * Copyright (C) 2013-2019 Robin Gareus <robin@gareus.org>
+ * Copyright (C) 2014-2019 Ben Loftis <ben@harrisonconsoles.com>
+ * Copyright (C) 2015 AndrĂ© Nusser <andre.nusser@googlemail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
 
 #include <algorithm>
 #include <cstdlib>
@@ -35,6 +40,7 @@
 #include "editor.h"
 #include "editor_drag.h"
 #include "editor_routes.h"
+#include "editor_sources.h"
 #include "actions.h"
 #include "audio_time_axis.h"
 #include "audio_region_view.h"
@@ -399,7 +405,7 @@ Editor::mapover_tracks (sigc::slot<void, RouteTimeAxisView&, uint32_t> sl, TimeA
 
        RouteGroup* group = route_basis->route()->route_group();
 
-       if (group && group->enabled_property(prop) && group->enabled_property (Properties::active.property_id) ) {
+       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 */
@@ -443,7 +449,7 @@ Editor::mapover_tracks_with_unique_playlists (sigc::slot<void, RouteTimeAxisView
 
        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) ) {
+       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 */
@@ -983,23 +989,6 @@ 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::presentation_info_changed (PropertyChange const & what_changed)
 {
@@ -1155,6 +1144,17 @@ Editor::presentation_info_changed (PropertyChange const & what_changed)
        }
 }
 
+void
+Editor::track_selection_changed ()
+{
+       /* reset paste count, so the plaste location doesn't get incremented
+        * if we want to paste in the same place, but different track. */
+       paste_count = 0;
+
+       if ( _session->solo_selection_active() )
+               play_solo_selection(false);
+}
+
 void
 Editor::time_selection_changed ()
 {
@@ -1220,6 +1220,7 @@ Editor::sensitize_the_right_region_actions (bool because_canvas_crossing)
        bool have_selection = false;
        bool have_entered = false;
        bool have_edit_point = false;
+       bool have_selected_source = false;
        RegionSelection rs;
 
        // std::cerr << "STRRA: crossing ? " << because_canvas_crossing << " within ? " << within_track_canvas
@@ -1235,6 +1236,10 @@ Editor::sensitize_the_right_region_actions (bool because_canvas_crossing)
                rs.add (entered_regionview);
        }
 
+       if ( _sources->get_single_selection() ) {
+               have_selected_source = true;
+       }
+
        if (rs.empty() && !selection->tracks.empty()) {
 
                /* no selected regions, but some selected tracks.
@@ -1284,6 +1289,8 @@ Editor::sensitize_the_right_region_actions (bool because_canvas_crossing)
                        sensitive = true;
                } else if ((tgt & EditPointRegions) && have_edit_point) {
                        sensitive = true;
+               } else if ((tgt & ListSelection) && have_selected_source ) {
+                       sensitive = true;
                }
 
                x->second.action->set_sensitive (sensitive);
@@ -1509,12 +1516,15 @@ Editor::sensitize_the_right_region_actions (bool because_canvas_crossing)
                _region_actions->get_action("naturalize-region")->set_sensitive (false);
        }
 
+       /* Todo: insert-region-from-source-list */
        /* XXX: should also check that there is a track of the appropriate type for the selected region */
+#if 0
        if (_edit_point == EditAtMouse || _regions->get_single_selection() == 0 || selection->tracks.empty()) {
-               _region_actions->get_action("insert-region-from-region-list")->set_sensitive (false);
+               _region_actions->get_action("insert-region-from-source-list")->set_sensitive (false);
        } else {
-               _region_actions->get_action("insert-region-from-region-list")->set_sensitive (true);
+               _region_actions->get_action("insert-region-from-source-list")->set_sensitive (true);
        }
+#endif
 
        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);
@@ -1568,15 +1578,35 @@ Editor::region_selection_changed ()
        sensitize_the_right_region_actions (false);
 
        /* propagate into backend */
+       assert (_session);
 
-       if (_session) {
-               if (!selection->regions.empty()) {
-                       _session->set_object_selection (selection->regions.start(), selection->regions.end_sample());
-               } else {
-                       _session->clear_object_selection ();
-               }
+       if (!selection->regions.empty()) {
+               _session->set_object_selection (selection->regions.start(), selection->regions.end_sample());
+       } else {
+               _session->clear_object_selection ();
+       }
+
+       if (_session->solo_selection_active()) {
+               play_solo_selection(false);
+       }
+
+       /* set nudge button color */
+       if (! get_regions_from_selection_and_entered().empty()) {
+               /* nudge regions */
+               nudge_forward_button.set_name ("nudge button");
+               nudge_backward_button.set_name ("nudge button");
+       } else {
+               /* nudge marker or playhead */
+               nudge_forward_button.set_name ("generic button");
+               nudge_backward_button.set_name ("generic button");
        }
 
+       //there are a few global Editor->Select actions which select regions even if you aren't in Object mode.
+       //if regions are selected, we must always force the mouse mode to Object...
+       //... otherwise the user is confusingly left with selected regions that can't be manipulated.
+       if (!selection->regions.empty()) {
+               set_mouse_mode( MouseObject, false );
+       }
 }
 
 void
@@ -1700,10 +1730,9 @@ Editor::invert_selection_in_track ()
 void
 Editor::invert_selection ()
 {
-       list<Selectable *> touched;
 
        if (internal_editing()) {
-               for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
+               for (MidiRegionSelection::iterator i = selection->midi_regions.begin(); i != selection->midi_regions.end(); ++i) {
                        MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
                        if (mrv) {
                                mrv->invert_selection ();
@@ -1712,16 +1741,35 @@ Editor::invert_selection ()
                return;
        }
 
-       for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
-               if ((*iter)->hidden()) {
-                       continue;
+       if (!selection->tracks.empty()) {
+
+               TrackViewList inverted;
+
+               for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
+                       if (!(*iter)->selected()) {
+                               inverted.push_back (*iter);
+                       }
                }
-               (*iter)->get_inverted_selectables (*selection, touched);
-       }
 
-       begin_reversible_selection_op (X_("Invert Selection"));
-       selection->set (touched);
-       commit_reversible_selection_op ();
+               begin_reversible_selection_op (X_("Invert Track Selection"));
+               selection->set (inverted);
+               commit_reversible_selection_op ();
+
+       } else {
+
+               list<Selectable *> touched;
+
+               for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
+                       if ((*iter)->hidden()) {
+                               continue;
+                       }
+                       (*iter)->get_inverted_selectables (*selection, touched);
+               }
+
+               begin_reversible_selection_op (X_("Invert ObjectSelection"));
+               selection->set (touched);
+               commit_reversible_selection_op ();
+       }
 }
 
 /** @param start Start time in session samples.
@@ -1807,7 +1855,7 @@ Editor::set_selection_from_region ()
 
        selection->set (tvl);
 
-       if (!get_smart_mode () || !mouse_mode == Editing::MouseObject) {
+       if (!get_smart_mode () || !(mouse_mode == Editing::MouseObject) ) {
                set_mouse_mode (Editing::MouseRange, false);
        }
 }
@@ -1839,7 +1887,15 @@ void
 Editor::set_selection_from_range (Location& loc)
 {
        begin_reversible_selection_op (X_("set selection from range"));
+
        selection->set (loc.start(), loc.end());
+
+       // if no tracks are selected, enable all tracks
+       // (_something_ has to be selected for any range selection, otherwise the user won't see anything)
+       if (selection->tracks.empty()) {
+               select_all_tracks();
+       }
+
        commit_reversible_selection_op ();
 
        if (!get_smart_mode () || mouse_mode != Editing::MouseObject) {
@@ -2095,7 +2151,7 @@ Editor::select_range_between ()
        samplepos_t start;
        samplepos_t end;
 
-       if ( !selection->time.empty() ) {
+       if (!selection->time.empty()) {
                selection->clear_time ();
        }
 
@@ -2115,12 +2171,9 @@ Editor::select_range_between ()
 bool
 Editor::get_edit_op_range (samplepos_t& start, samplepos_t& end) const
 {
-//     samplepos_t m;
-//     bool ignored;
-
        /* if an explicit range exists, use it */
 
-       if ( (mouse_mode == MouseRange || get_smart_mode() ) &&  !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_sample();
@@ -2130,87 +2183,6 @@ Editor::get_edit_op_range (samplepos_t& start, samplepos_t& end) const
                end = 0;
                return false;
        }
-
-//     if (!mouse_sample (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_sample();
-
-//     } else {
-
-//             switch (_edit_point) {
-//             case EditAtPlayhead:
-//                     if (selection->markers.empty()) {
-//                             /* use mouse + playhead */
-//                             start = m;
-//                             end = _session->audible_sample();
-//                     } else {
-//                             /* use playhead + selected marker */
-//                             start = _session->audible_sample();
-//                             end = selection->markers.front()->position();
-//                     }
-//                     break;
-
-//             case EditAtMouse:
-//                     /* use mouse + selected marker */
-//                     if (selection->markers.empty()) {
-//                             start = m;
-//                             end = _session->audible_sample();
-//                     } 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++;
-
-//     return true;
 }
 
 void