+
+/** 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 */
+}
+
+XMLNode&
+Selection::get_state () const
+{
+ /* XXX: not complete; just sufficient to get track selection state
+ so that re-opening plugin windows for editor mixer strips works
+ */
+
+ XMLNode* node = new XMLNode (X_("Selection"));
+
+ for (TrackSelection::const_iterator i = tracks.begin(); i != tracks.end(); ++i) {
+ RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
+ AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (*i);
+ if (rtv) {
+ XMLNode* t = node->add_child (X_("RouteView"));
+ t->add_property (X_("id"), atoi (rtv->route()->id().to_s().c_str()));
+ } else if (atv) {
+ XMLNode* t = node->add_child (X_("AutomationView"));
+ t->add_property (X_("id"), atoi (atv->parent_route()->id().to_s().c_str()));
+ t->add_property (X_("parameter"), EventTypeMap::instance().to_symbol (atv->control()->parameter ()));
+ }
+ }
+
+ return *node;
+}
+
+int
+Selection::set_state (XMLNode const & node, int)
+{
+ if (node.name() != X_("Selection")) {
+ return -1;
+ }
+
+ XMLNodeList children = node.children ();
+ for (XMLNodeList::const_iterator i = children.begin(); i != children.end(); ++i) {
+ if ((*i)->name() == X_("RouteView")) {
+
+ XMLProperty* prop_id = (*i)->property (X_("id"));
+ assert (prop_id);
+ PBD::ID id (prop_id->value ());
+ RouteTimeAxisView* rtv = editor->get_route_view_by_route_id (id);
+ if (rtv) {
+ add (rtv);
+ }
+
+ } else if ((*i)->name() == X_("AutomationView")) {
+
+ XMLProperty* prop_id = (*i)->property (X_("id"));
+ XMLProperty* prop_parameter = (*i)->property (X_("parameter"));
+
+ assert (prop_id);
+ assert (prop_parameter);
+
+ PBD::ID id (prop_id->value ());
+ RouteTimeAxisView* rtv = editor->get_route_view_by_route_id (id);
+
+ if (rtv) {
+ boost::shared_ptr<AutomationTimeAxisView> atv = rtv->automation_child (EventTypeMap::instance().new_parameter (prop_parameter->value ()));
+
+ /* the automation could be for an entity that was never saved
+ in the session file. Don't freak out if we can't find
+ it.
+ */
+
+ if (atv) {
+ add (atv.get());
+ }
+ }
+ }
+ }
+
+ return 0;
+}
+
+void
+Selection::remove_regions (TimeAxisView* t)
+{
+ RegionSelection::iterator i = regions.begin();
+ while (i != regions.end ()) {
+ RegionSelection::iterator tmp = i;
+ ++tmp;
+
+ if (&(*i)->get_time_axis_view() == t) {
+ remove (*i);
+ }
+
+ i = tmp;
+ }
+}