/*
- 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>
#include "ardour/route_group.h"
#include "ardour/selection.h"
#include "ardour/session.h"
+#include "ardour/vca.h"
#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"
return;
}
+ PBD::Unwinder<bool> uw (_editor_track_selection_change_without_scroll, true);
+
RouteGroup* group = NULL;
if (clicked_routeview) {
group = clicked_routeview->route()->route_group();
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 */
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 */
} else if (op == Selection::Extend) {
list<Selectable*> results;
- framepos_t last_frame;
- framepos_t first_frame;
+ samplepos_t last_sample;
+ samplepos_t first_sample;
bool same_track = false;
/* 1. find the last selected regionview in the track that was clicked in */
- last_frame = 0;
- first_frame = max_framepos;
+ last_sample = 0;
+ first_sample = max_samplepos;
for (RegionSelection::iterator x = selection->regions.begin(); x != selection->regions.end(); ++x) {
if (&(*x)->get_time_axis_view() == &clicked_regionview->get_time_axis_view()) {
- if ((*x)->region()->last_frame() > last_frame) {
- last_frame = (*x)->region()->last_frame();
+ if ((*x)->region()->last_sample() > last_sample) {
+ last_sample = (*x)->region()->last_sample();
}
- if ((*x)->region()->first_frame() < first_frame) {
- first_frame = (*x)->region()->first_frame();
+ if ((*x)->region()->first_sample() < first_sample) {
+ first_sample = (*x)->region()->first_sample();
}
same_track = true;
/* 2. figure out the boundaries for our search for new objects */
- switch (clicked_regionview->region()->coverage (first_frame, last_frame)) {
+ switch (clicked_regionview->region()->coverage (first_sample, last_sample)) {
case Evoral::OverlapNone:
- if (last_frame < clicked_regionview->region()->first_frame()) {
- first_frame = last_frame;
- last_frame = clicked_regionview->region()->last_frame();
+ if (last_sample < clicked_regionview->region()->first_sample()) {
+ first_sample = last_sample;
+ last_sample = clicked_regionview->region()->last_sample();
} else {
- last_frame = first_frame;
- first_frame = clicked_regionview->region()->first_frame();
+ last_sample = first_sample;
+ first_sample = clicked_regionview->region()->first_sample();
}
break;
case Evoral::OverlapExternal:
- if (last_frame < clicked_regionview->region()->first_frame()) {
- first_frame = last_frame;
- last_frame = clicked_regionview->region()->last_frame();
+ if (last_sample < clicked_regionview->region()->first_sample()) {
+ first_sample = last_sample;
+ last_sample = clicked_regionview->region()->last_sample();
} else {
- last_frame = first_frame;
- first_frame = clicked_regionview->region()->first_frame();
+ last_sample = first_sample;
+ first_sample = clicked_regionview->region()->first_sample();
}
break;
case Evoral::OverlapInternal:
- if (last_frame < clicked_regionview->region()->first_frame()) {
- first_frame = last_frame;
- last_frame = clicked_regionview->region()->last_frame();
+ if (last_sample < clicked_regionview->region()->first_sample()) {
+ first_sample = last_sample;
+ last_sample = clicked_regionview->region()->last_sample();
} else {
- last_frame = first_frame;
- first_frame = clicked_regionview->region()->first_frame();
+ last_sample = first_sample;
+ first_sample = clicked_regionview->region()->first_sample();
}
break;
*/
- first_frame = clicked_regionview->region()->position();
- last_frame = clicked_regionview->region()->last_frame();
+ first_sample = clicked_regionview->region()->position();
+ last_sample = clicked_regionview->region()->last_sample();
for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
- if ((*i)->region()->position() < first_frame) {
- first_frame = (*i)->region()->position();
+ if ((*i)->region()->position() < first_sample) {
+ first_sample = (*i)->region()->position();
}
- if ((*i)->region()->last_frame() + 1 > last_frame) {
- last_frame = (*i)->region()->last_frame();
+ if ((*i)->region()->last_sample() + 1 > last_sample) {
+ last_sample = (*i)->region()->last_sample();
}
}
}
*/
for (set<RouteTimeAxisView*>::iterator t = relevant_tracks.begin(); t != relevant_tracks.end(); ++t) {
- (*t)->get_selectables (first_frame, last_frame, -1.0, -1.0, results);
+ (*t)->get_selectables (first_sample, last_sample, -1.0, -1.0, results);
}
/* 4. convert to a vector of regions */
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)
{
+ uint32_t n_tracks = 0;
+ uint32_t n_busses = 0;
+ uint32_t n_vcas = 0;
+ uint32_t n_routes = 0;
+ uint32_t n_stripables = 0;
+
/* We cannot ensure ordering of the handlers for
* PresentationInfo::Changed, so we have to do everything in order
* here, as a single handler.
break;
default:
set_selected_mixer_strip (*(selection->tracks.back()));
- if (!_track_selection_change_without_scroll) {
+ if (!_track_selection_change_without_scroll && !_editor_track_selection_change_without_scroll) {
ensure_time_axis_view_is_visible (*(selection->tracks.back()), false);
}
break;
continue;
}
+ n_stripables++;
+
+ if (boost::dynamic_pointer_cast<Track> ((*i).stripable)) {
+ n_tracks++;
+ n_routes++;
+ } else if (boost::dynamic_pointer_cast<Route> ((*i).stripable)) {
+ n_busses++;
+ n_routes++;
+ } else if (boost::dynamic_pointer_cast<VCA> ((*i).stripable)) {
+ n_vcas++;
+ }
+
TimeAxisView* tav = dynamic_cast<TimeAxisView*> (av);
if (!tav) {
stripables->push_back ((*i).stripable);
}
- ActionManager::set_sensitive (ActionManager::track_selection_sensitive_actions, !selection->tracks.empty());
+ ActionManager::set_sensitive (ActionManager::stripable_selection_sensitive_actions, (n_stripables > 0));
+ ActionManager::set_sensitive (ActionManager::track_selection_sensitive_actions, (n_tracks > 0));
+ ActionManager::set_sensitive (ActionManager::bus_selection_sensitive_actions, (n_busses > 0));
+ ActionManager::set_sensitive (ActionManager::route_selection_sensitive_actions, (n_routes > 0));
+ ActionManager::set_sensitive (ActionManager::vca_selection_sensitive_actions, (n_vcas > 0));
sensitize_the_right_region_actions (false);
}
}
+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 ()
{
if (_session && !_drags->active()) {
if (selection->time.length() != 0) {
- _session->set_range_selection (selection->time.start(), selection->time.end_frame());
+ _session->set_range_selection (selection->time.start(), selection->time.end_sample());
} else {
_session->clear_range_selection ();
}
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
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.
}
} else {
RegionSelection at_edit_point;
- framepos_t const where = get_preferred_edit_position (Editing::EDIT_IGNORE_NONE, false, !within_track_canvas);
+ samplepos_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;
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);
_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);
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_frame());
- } 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
begin_reversible_selection_op (X_("Select All in Track"));
- clicked_routeview->get_selectables (0, max_framepos, 0, DBL_MAX, touched);
+ clicked_routeview->get_selectables (0, max_samplepos, 0, DBL_MAX, touched);
switch (op) {
case Selection::Toggle:
if ((*iter)->hidden()) {
continue;
}
- (*iter)->get_selectables (0, max_framepos, 0, DBL_MAX, touched);
+ (*iter)->get_selectables (0, max_samplepos, 0, DBL_MAX, touched);
}
begin_reversible_selection_op (X_("select all"));
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 ();
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 frames.
- * @param end End time in session frames.
+/** @param start Start time in session samples.
+ * @param end End time in session samples.
* @param top Top (lower) y limit in trackview coordinates (ie 0 at the top of the track view)
* @param bottom Bottom (higher) y limit in trackview coordinates (ie 0 at the top of the track view)
* @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.
*/
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)
+Editor::select_all_within (samplepos_t start, samplepos_t end, double top, double bot, const TrackViewList& tracklist, Selection::Operation op, bool preserve_if_selected)
{
list<Selectable*> found;
/* select range (this will clear the region selection) */
- selection->set (selection->regions.start(), selection->regions.end_frame());
+ selection->set (selection->regions.start(), selection->regions.end_sample());
/* and select the tracks */
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);
}
}
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) {
return;
}
- framepos_t start = selection->time[clicked_selection].start;
- framepos_t end = selection->time[clicked_selection].end;
+ samplepos_t start = selection->time[clicked_selection].start;
+ samplepos_t end = selection->time[clicked_selection].end;
if (end - start < 1) {
return;
void
Editor::select_all_selectables_using_cursor (EditorCursor *cursor, bool after)
{
- framepos_t start;
- framepos_t end;
+ samplepos_t start;
+ samplepos_t end;
list<Selectable *> touched;
if (after) {
- start = cursor->current_frame();
- end = _session->current_end_frame();
+ start = cursor->current_sample();
+ end = _session->current_end_sample();
} else {
- if (cursor->current_frame() > 0) {
+ if (cursor->current_sample() > 0) {
start = 0;
- end = cursor->current_frame() - 1;
+ end = cursor->current_sample() - 1;
} else {
return;
}
void
Editor::select_all_selectables_using_edit (bool after, bool from_context_menu)
{
- framepos_t start;
- framepos_t end;
+ samplepos_t start;
+ samplepos_t end;
list<Selectable *> touched;
if (after) {
start = get_preferred_edit_position(EDIT_IGNORE_NONE, from_context_menu);
- end = _session->current_end_frame();
+ end = _session->current_end_sample();
} else {
if ((end = get_preferred_edit_position(EDIT_IGNORE_NONE, from_context_menu)) > 1) {
start = 0;
void
Editor::select_all_selectables_between (bool within)
{
- framepos_t start;
- framepos_t end;
+ samplepos_t start;
+ samplepos_t end;
list<Selectable *> touched;
if (!get_edit_op_range (start, end)) {
void
Editor::select_range_between ()
{
- framepos_t start;
- framepos_t end;
+ samplepos_t start;
+ samplepos_t end;
- if ( !selection->time.empty() ) {
+ if (!selection->time.empty()) {
selection->clear_time ();
}
}
bool
-Editor::get_edit_op_range (framepos_t& start, framepos_t& end) const
+Editor::get_edit_op_range (samplepos_t& start, samplepos_t& end) const
{
-// framepos_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_frame();
+ end = selection->time.end_sample();
return true;
} else {
start = 0;
end = 0;
return false;
}
-
-// 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++;
-
-// return true;
}
void
}
long
-Editor::select_range (framepos_t s, framepos_t e)
+Editor::select_range (samplepos_t s, samplepos_t e)
{
begin_reversible_selection_op (X_("Select Range"));
selection->add (clicked_axisview);