summaryrefslogtreecommitdiff
path: root/gtk2_ardour/selection.cc
diff options
context:
space:
mode:
Diffstat (limited to 'gtk2_ardour/selection.cc')
-rw-r--r--gtk2_ardour/selection.cc139
1 files changed, 114 insertions, 25 deletions
diff --git a/gtk2_ardour/selection.cc b/gtk2_ardour/selection.cc
index 2e5be77cde..786749ce72 100644
--- a/gtk2_ardour/selection.cc
+++ b/gtk2_ardour/selection.cc
@@ -33,6 +33,7 @@
#include "time_axis_view.h"
#include "automation_time_axis.h"
#include "public_editor.h"
+#include "control_point.h"
#include "i18n.h"
@@ -817,34 +818,35 @@ Selection::empty (bool internal_selection)
}
void
-Selection::toggle (const vector<AutomationSelectable*>& autos)
+Selection::toggle (ControlPoint* cp)
{
- for (vector<AutomationSelectable*>::const_iterator x = autos.begin(); x != autos.end(); ++x) {
- if ((*x)->get_selected()) {
- points.remove (**x);
- } else {
- points.push_back (**x);
- }
+ cp->set_selected (!cp->get_selected ());
+ set_point_selection_from_line (cp->line ());
+}
- delete *x;
+void
+Selection::toggle (vector<ControlPoint*> const & cps)
+{
+ for (vector<ControlPoint*>::const_iterator i = cps.begin(); i != cps.end(); ++i) {
+ (*i)->set_selected (!(*i)->get_selected ());
}
- PointsChanged (); /* EMIT SIGNAL */
+ set_point_selection_from_line (cps.front()->line ());
}
void
Selection::toggle (list<Selectable*> const & selectables)
{
RegionView* rv;
- AutomationSelectable* as;
+ ControlPoint* cp;
vector<RegionView*> rvs;
- vector<AutomationSelectable*> autos;
+ vector<ControlPoint*> cps;
for (std::list<Selectable*>::const_iterator i = selectables.begin(); i != selectables.end(); ++i) {
if ((rv = dynamic_cast<RegionView*> (*i)) != 0) {
rvs.push_back (rv);
- } else if ((as = dynamic_cast<AutomationSelectable*> (*i)) != 0) {
- autos.push_back (as);
+ } else if ((cp = dynamic_cast<ControlPoint*> (*i)) != 0) {
+ cps.push_back (cp);
} else {
fatal << _("programming error: ")
<< X_("unknown selectable type passed to Selection::toggle()")
@@ -857,8 +859,8 @@ Selection::toggle (list<Selectable*> const & selectables)
toggle (rvs);
}
- if (!autos.empty()) {
- toggle (autos);
+ if (!cps.empty()) {
+ toggle (cps);
}
}
@@ -875,15 +877,15 @@ void
Selection::add (list<Selectable*> const & selectables)
{
RegionView* rv;
- AutomationSelectable* as;
+ ControlPoint* cp;
vector<RegionView*> rvs;
- vector<AutomationSelectable*> autos;
+ vector<ControlPoint*> cps;
for (std::list<Selectable*>::const_iterator i = selectables.begin(); i != selectables.end(); ++i) {
if ((rv = dynamic_cast<RegionView*> (*i)) != 0) {
rvs.push_back (rv);
- } else if ((as = dynamic_cast<AutomationSelectable*> (*i)) != 0) {
- autos.push_back (as);
+ } else if ((cp = dynamic_cast<ControlPoint*> (*i)) != 0) {
+ cps.push_back (cp);
} else {
fatal << _("programming error: ")
<< X_("unknown selectable type passed to Selection::add()")
@@ -896,8 +898,8 @@ Selection::add (list<Selectable*> const & selectables)
add (rvs);
}
- if (!autos.empty()) {
- add (autos);
+ if (!cps.empty()) {
+ add (cps);
}
}
@@ -911,13 +913,33 @@ Selection::clear_points ()
}
void
-Selection::add (vector<AutomationSelectable*>& autos)
+Selection::add (ControlPoint* cp)
+{
+ cp->set_selected (true);
+ set_point_selection_from_line (cp->line ());
+}
+
+void
+Selection::add (vector<ControlPoint*> const & cps)
+{
+ for (vector<ControlPoint*>::const_iterator i = cps.begin(); i != cps.end(); ++i) {
+ (*i)->set_selected (true);
+ }
+
+ set_point_selection_from_line (cps.front()->line ());
+}
+
+void
+Selection::set (ControlPoint* cp)
{
- for (vector<AutomationSelectable*>::iterator i = autos.begin(); i != autos.end(); ++i) {
- points.push_back (**i);
+ if (cp->get_selected()) {
+ return;
}
- PointsChanged ();
+ points.clear ();
+ vector<ControlPoint*> cps;
+ cps.push_back (cp);
+ add (cps);
}
void
@@ -986,3 +1008,70 @@ MarkerSelection::range (nframes64_t& s, nframes64_t& e)
s = std::min (s, e);
e = std::max (s, e);
}
+
+/** Automation control point selection is mostly manipulated using the selected state
+ * of the ControlPoints themselves. For example, to add a point to a selection, its
+ * ControlPoint is marked as selected and then this method is called. It sets up
+ * our PointSelection from the selected ControlPoints of a given AutomationLine.
+ *
+ * We can't use ControlPoints directly in the selection, as we need to express a
+ * selection of not just a visible ControlPoint but also (possibly) some invisible
+ * points nearby. Hence the selection stores AutomationRanges, and these are synced
+ * with ControlPoint selection state using AutomationLine::set_selected_points.
+ */
+
+void
+Selection::set_point_selection_from_line (AutomationLine const & line)
+{
+ points.clear ();
+
+ AutomationRange current (DBL_MAX, 0, 1, 0, &line.trackview);
+
+ for (uint32_t i = 0; i < line.npoints(); ++i) {
+ ControlPoint const * cp = line.nth (i);
+
+ if (cp->get_selected()) {
+ /* x and y position of this control point in coordinates suitable for
+ an AutomationRange (ie model time and fraction of track height)
+ */
+ double const x = (*(cp->model()))->when;
+ double const y = 1 - (cp->get_y() / line.trackview.current_height ());
+
+ /* work out the position of a rectangle the size of a control point centred
+ on this point
+ */
+
+ double const size = cp->size ();
+ double const x_size = line.time_converter().from (line.trackview.editor().pixel_to_frame (size));
+ double const y_size = size / line.trackview.current_height ();
+
+ double const x1 = x - x_size / 2;
+ double const x2 = x + x_size / 2;
+ double const y1 = y - y_size / 2;
+ double const y2 = y + y_size / 2;
+
+ /* extend the current AutomationRange to put this point in */
+ current.start = min (current.start, x1);
+ current.end = max (current.end, x2);
+ current.low_fract = min (current.low_fract, y1);
+ current.high_fract = max (current.high_fract, y2);
+
+ } else {
+ /* this point isn't selected; if the current AutomationRange has some
+ stuff in it, push it onto the list and make a new one
+ */
+ if (current.start < DBL_MAX) {
+ points.push_back (current);
+ current = AutomationRange (DBL_MAX, 0, 1, 0, &line.trackview);
+ }
+ }
+ }
+
+ /* Maybe push the current AutomationRange, as above */
+ if (current.start < DBL_MAX) {
+ points.push_back (current);
+ current = AutomationRange (DBL_MAX, 0, 1, 0, &line.trackview);
+ }
+
+ PointsChanged (); /* EMIT SIGNAL */
+}