2 Copyright (C) 2002-2003 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
25 #include "pbd/stl_delete.h"
26 #include "pbd/memento_command.h"
27 #include "pbd/stacktrace.h"
29 #include "ardour/automation_list.h"
30 #include "ardour/dB.h"
31 #include "evoral/Curve.hpp"
33 #include "simplerect.h"
34 #include "automation_line.h"
35 #include "control_point.h"
36 #include "gui_thread.h"
37 #include "rgb_macros.h"
38 #include "ardour_ui.h"
39 #include "public_editor.h"
41 #include "selection.h"
42 #include "time_axis_view.h"
43 #include "point_selection.h"
44 #include "automation_selectable.h"
45 #include "automation_time_axis.h"
46 #include "public_editor.h"
48 #include "ardour/event_type_map.h"
49 #include "ardour/session.h"
54 using namespace ARDOUR;
56 using namespace Editing;
57 using namespace Gnome; // for Canvas
59 static const Evoral::IdentityConverter<double, sframes_t> default_converter;
61 AutomationLine::AutomationLine (const string& name, TimeAxisView& tv, ArdourCanvas::Group& parent,
62 boost::shared_ptr<AutomationList> al,
63 const Evoral::TimeConverter<double, sframes_t>* converter)
67 , _parent_group (parent)
68 , _time_converter (converter ? (*converter) : default_converter)
70 _interpolation = al->interpolation();
71 points_visible = false;
72 update_pending = false;
73 _uses_gain_mapping = false;
76 terminal_points_can_slide = true;
79 group = new ArdourCanvas::Group (parent);
80 group->property_x() = 0.0;
81 group->property_y() = 0.0;
83 line = new ArdourCanvas::Line (*group);
84 line->property_width_pixels() = (guint)1;
85 line->set_data ("line", this);
87 line->signal_event().connect (sigc::mem_fun (*this, &AutomationLine::event_handler));
89 alist->StateChanged.connect (_state_connection, boost::bind (&AutomationLine::list_changed, this), gui_context());
91 trackview.session()->register_with_memento_command_factory(alist->id(), this);
93 if (alist->parameter().type() == GainAutomation ||
94 alist->parameter().type() == EnvelopeAutomation) {
95 set_uses_gain_mapping (true);
98 set_interpolation(alist->interpolation());
101 AutomationLine::~AutomationLine ()
103 vector_delete (&control_points);
108 AutomationLine::event_handler (GdkEvent* event)
110 return PublicEditor::instance().canvas_line_event (event, line, this);
114 AutomationLine::queue_reset ()
116 if (!update_pending) {
117 update_pending = true;
118 Gtkmm2ext::UI::instance()->call_slot (boost::bind (&AutomationLine::reset, this));
123 AutomationLine::show ()
125 if (_interpolation != AutomationList::Discrete) {
129 if (points_visible) {
130 for (vector<ControlPoint*>::iterator i = control_points.begin(); i != control_points.end(); ++i) {
139 AutomationLine::hide ()
142 for (vector<ControlPoint*>::iterator i = control_points.begin(); i != control_points.end(); ++i) {
149 AutomationLine::control_point_box_size ()
151 if (_interpolation == AutomationList::Discrete) {
152 return max((_height*4.0) / (double)(alist->parameter().max() - alist->parameter().min()),
156 if (_height > TimeAxisView::hLarger) {
158 } else if (_height > (guint32) TimeAxisView::hNormal) {
165 AutomationLine::set_height (guint32 h)
170 double bsz = control_point_box_size();
172 for (vector<ControlPoint*>::iterator i = control_points.begin(); i != control_points.end(); ++i) {
173 (*i)->set_size (bsz);
181 AutomationLine::set_line_color (uint32_t color)
184 line->property_fill_color_rgba() = color;
188 AutomationLine::set_uses_gain_mapping (bool yn)
190 if (yn != _uses_gain_mapping) {
191 _uses_gain_mapping = yn;
197 AutomationLine::nth (uint32_t n)
199 if (n < control_points.size()) {
200 return control_points[n];
207 AutomationLine::modify_point_y (ControlPoint& cp, double y)
209 /* clamp y-coord appropriately. y is supposed to be a normalized fraction (0.0-1.0),
210 and needs to be converted to a canvas unit distance.
215 y = _height - (y * _height);
217 double const x = trackview.editor().frame_to_unit (_time_converter.to((*cp.model())->when));
219 trackview.editor().session()->begin_reversible_command (_("automation event move"));
220 trackview.editor().session()->add_command (new MementoCommand<AutomationList>(*alist.get(), &get_state(), 0));
222 cp.move_to (x, y, ControlPoint::Full);
223 reset_line_coords (cp);
225 if (line_points.size() > 1) {
226 line->property_points() = line_points;
230 sync_model_with_view_point (cp, false, 0);
233 update_pending = false;
235 trackview.editor().session()->add_command (new MementoCommand<AutomationList>(*alist.get(), 0, &alist->get_state()));
236 trackview.editor().session()->commit_reversible_command ();
237 trackview.editor().session()->set_dirty ();
242 AutomationLine::modify_view_point (ControlPoint& cp, double x, double y, bool with_push)
245 uint32_t last_movable = UINT_MAX;
246 double x_limit = DBL_MAX;
248 /* this just changes the current view. it does not alter
249 the model in any way at all.
252 /* clamp y-coord appropriately. y is supposed to be a normalized fraction (0.0-1.0),
253 and needs to be converted to a canvas unit distance.
258 y = _height - (y * _height);
260 if (cp.can_slide()) {
262 /* x-coord cannot move beyond adjacent points or the start/end, and is
263 already in frames. it needs to be converted to canvas units.
266 x = trackview.editor().frame_to_unit (x);
268 /* clamp x position using view coordinates */
270 ControlPoint *before;
273 if (cp.view_index()) {
274 before = nth (cp.view_index() - 1);
275 x = max (x, before->get_x()+1.0);
282 if (cp.view_index() < control_points.size() - 1) {
284 after = nth (cp.view_index() + 1);
286 /*if it is a "spike" leave the x alone */
288 if (after->get_x() - before->get_x() < 2) {
292 x = min (x, after->get_x()-1.0);
302 /* find the first point that can't move */
304 for (uint32_t n = cp.view_index() + 1; (after = nth (n)) != 0; ++n) {
305 if (!after->can_slide()) {
306 x_limit = after->get_x() - 1.0;
307 last_movable = after->view_index();
312 delta = x - cp.get_x();
317 /* leave the x-coordinate alone */
319 x = trackview.editor().frame_to_unit (_time_converter.to((*cp.model())->when));
325 cp.move_to (x, y, ControlPoint::Full);
326 reset_line_coords (cp);
330 uint32_t limit = min (control_points.size(), (size_t)last_movable);
332 /* move the current point to wherever the user told it to go, subject
336 cp.move_to (min (x, x_limit), y, ControlPoint::Full);
337 reset_line_coords (cp);
339 /* now move all subsequent control points, to reflect the motion.
342 for (uint32_t i = cp.view_index() + 1; i < limit; ++i) {
343 ControlPoint *p = nth (i);
346 if (p->can_slide()) {
347 new_x = min (p->get_x() + delta, x_limit);
348 p->move_to (new_x, p->get_y(), ControlPoint::Full);
349 reset_line_coords (*p);
356 AutomationLine::reset_line_coords (ControlPoint& cp)
358 if (cp.view_index() < line_points.size()) {
359 line_points[cp.view_index()].set_x (cp.get_x());
360 line_points[cp.view_index()].set_y (cp.get_y());
365 AutomationLine::sync_model_with_view_line (uint32_t start, uint32_t end)
369 update_pending = true;
371 for (uint32_t i = start; i <= end; ++i) {
373 sync_model_with_view_point (*p, false, 0);
378 AutomationLine::model_representation (ControlPoint& cp, ModelRepresentation& mr)
380 /* part one: find out where the visual control point is.
381 initial results are in canvas units. ask the
382 line to convert them to something relevant.
385 mr.xval = cp.get_x();
386 mr.yval = 1.0 - (cp.get_y() / _height);
388 /* if xval has not changed, set it directly from the model to avoid rounding errors */
390 if (mr.xval == trackview.editor().frame_to_unit(_time_converter.to((*cp.model())->when))) {
391 mr.xval = (*cp.model())->when;
393 mr.xval = trackview.editor().unit_to_frame (mr.xval);
396 /* convert to model units
399 view_to_model_coord (mr.xval, mr.yval);
401 /* part 2: find out where the model point is now
404 mr.xpos = (*cp.model())->when;
405 mr.ypos = (*cp.model())->value;
407 /* part 3: get the position of the visual control
408 points before and after us.
411 ControlPoint* before;
414 if (cp.view_index()) {
415 before = nth (cp.view_index() - 1);
420 after = nth (cp.view_index() + 1);
423 mr.xmin = (*before->model())->when;
424 mr.ymin = (*before->model())->value;
425 mr.start = before->model();
430 mr.start = cp.model();
434 mr.end = after->model();
444 AutomationLine::determine_visible_control_points (ALPoints& points)
446 uint32_t view_index, pi, n;
447 AutomationList::iterator model;
449 uint32_t this_rx = 0;
450 uint32_t prev_rx = 0;
451 uint32_t this_ry = 0;
452 uint32_t prev_ry = 0;
456 /* hide all existing points, and the line */
458 for (vector<ControlPoint*>::iterator i = control_points.begin(); i != control_points.end(); ++i) {
464 if (points.empty()) {
468 npoints = points.size();
470 /* compute derivative/slope for the entire line */
472 slope = new double[npoints];
474 for (n = 0; n < npoints - 1; ++n) {
475 double xdelta = points[n+1].x - points[n].x;
476 double ydelta = points[n+1].y - points[n].y;
477 slope[n] = ydelta/xdelta;
480 box_size = (uint32_t) control_point_box_size ();
482 /* read all points and decide which ones to show as control points */
486 for (model = alist->begin(), pi = 0; pi < npoints; ++model, ++pi) {
488 double tx = points[pi].x;
489 double ty = points[pi].y;
491 if (isnan (tx) || isnan (ty)) {
492 warning << string_compose (_("Ignoring illegal points on AutomationLine \"%1\""),
497 /* now ensure that the control_points vector reflects the current curve
498 state, but don't plot control points too close together. also, don't
499 plot a series of points all with the same value.
501 always plot the first and last points, of course.
504 if (invalid_point (points, pi)) {
505 /* for some reason, we are supposed to ignore this point,
506 but still keep track of the model index.
511 if (pi > 0 && pi < npoints - 1) {
512 if (slope[pi] == slope[pi-1]) {
514 /* no reason to display this point */
520 /* need to round here. the ultimate coordinates are integer
521 pixels, so tiny deltas in the coords will be eliminated
522 and we end up with "colinear" line segments. since the
523 line rendering code in libart doesn't like this very
524 much, we eliminate them here. don't do this for the first and last
528 this_rx = (uint32_t) rint (tx);
529 this_ry = (uint32_t) rint (ty);
531 if (view_index && pi != npoints && /* not the first, not the last */
532 (((this_rx == prev_rx) && (this_ry == prev_ry)) || /* same point */
533 (((this_rx - prev_rx) < (box_size + 2)) && /* not identical, but still too close horizontally */
534 (abs ((int)(this_ry - prev_ry)) < (int) (box_size + 2))))) { /* too close vertically */
538 /* ok, we should display this point */
540 add_visible_control_point (view_index, pi, tx, ty, model, npoints);
548 /* discard extra CP's to avoid confusing ourselves */
550 while (control_points.size() > view_index) {
551 ControlPoint* cp = control_points.back();
552 control_points.pop_back ();
556 if (!terminal_points_can_slide) {
557 control_points.back()->set_can_slide(false);
562 if (view_index > 1) {
564 npoints = view_index;
566 /* reset the line coordinates */
568 while (line_points.size() < npoints) {
569 line_points.push_back (Art::Point (0,0));
572 while (line_points.size() > npoints) {
573 line_points.pop_back ();
576 for (view_index = 0; view_index < npoints; ++view_index) {
577 line_points[view_index].set_x (control_points[view_index]->get_x());
578 line_points[view_index].set_y (control_points[view_index]->get_y());
581 line->property_points() = line_points;
583 if (_visible && _interpolation != AutomationList::Discrete) {
589 set_selected_points (trackview.editor().get_selection().points);
594 AutomationLine::get_verbose_cursor_string (double fraction) const
596 std::string s = fraction_to_string (fraction);
597 if (_uses_gain_mapping) {
605 * @param fraction y fraction
606 * @return string representation of this value, using dB if appropriate.
609 AutomationLine::fraction_to_string (double fraction) const
613 if (_uses_gain_mapping) {
614 if (fraction == 0.0) {
615 snprintf (buf, sizeof (buf), "-inf");
617 snprintf (buf, sizeof (buf), "%.1f", accurate_coefficient_to_dB (slider_position_to_gain (fraction)));
621 view_to_model_coord (dummy, fraction);
622 if (EventTypeMap::instance().is_integer (alist->parameter())) {
623 snprintf (buf, sizeof (buf), "%d", (int)fraction);
625 snprintf (buf, sizeof (buf), "%.2f", fraction);
634 * @param s Value string in the form as returned by fraction_to_string.
635 * @return Corresponding y fraction.
638 AutomationLine::string_to_fraction (string const & s) const
645 sscanf (s.c_str(), "%lf", &v);
647 if (_uses_gain_mapping) {
648 v = gain_to_slider_position (dB_to_coefficient (v));
651 model_to_view_coord (dummy, v);
658 AutomationLine::invalid_point (ALPoints& p, uint32_t index)
660 return p[index].x == max_frames && p[index].y == DBL_MAX;
664 AutomationLine::invalidate_point (ALPoints& p, uint32_t index)
666 p[index].x = max_frames;
667 p[index].y = DBL_MAX;
671 AutomationLine::start_drag (ControlPoint* cp, nframes_t x, float fraction)
673 if (trackview.editor().session() == 0) { /* how? */
680 str = _("automation event move");
682 str = _("automation range drag");
685 trackview.editor().session()->begin_reversible_command (str);
686 trackview.editor().session()->add_command (new MementoCommand<AutomationList>(*alist.get(), &get_state(), 0));
690 first_drag_fraction = fraction;
691 last_drag_fraction = fraction;
697 AutomationLine::point_drag (ControlPoint& cp, nframes_t x, float fraction, bool with_push)
700 drag_distance += (x - drag_x);
702 drag_distance -= (drag_x - x);
707 modify_view_point (cp, x, fraction, with_push);
709 if (line_points.size() > 1) {
710 line->property_points() = line_points;
714 did_push = with_push;
718 AutomationLine::line_drag (uint32_t i1, uint32_t i2, float fraction, bool with_push)
720 double ydelta = fraction - last_drag_fraction;
722 did_push = with_push;
724 last_drag_fraction = fraction;
729 //check if one of the control points on the line is in a selected range
730 bool range_found = false;
733 for (uint32_t i = i1 ; i <= i2; i++) {
735 if (cp->selected()) {
741 for (vector<ControlPoint*>::iterator i = control_points.begin(); i != control_points.end(); ++i) {
742 if ((*i)->selected()) {
743 modify_view_point (*(*i), trackview.editor().unit_to_frame ((*i)->get_x()), ((_height - (*i)->get_y()) /_height) + ydelta, with_push);
748 for (uint32_t i = i1 ; i <= i2; i++) {
750 modify_view_point (*cp, trackview.editor().unit_to_frame (cp->get_x()), ((_height - cp->get_y()) /_height) + ydelta, with_push);
754 if (line_points.size() > 1) {
755 line->property_points() = line_points;
762 AutomationLine::end_drag (ControlPoint* cp)
771 sync_model_with_view_point (*cp, did_push, drag_distance);
773 sync_model_with_view_line (line_drag_cp1, line_drag_cp2);
778 update_pending = false;
780 trackview.editor().session()->add_command (new MementoCommand<AutomationList>(*alist.get(), 0, &alist->get_state()));
781 trackview.editor().session()->commit_reversible_command ();
782 trackview.editor().session()->set_dirty ();
787 AutomationLine::sync_model_with_view_point (ControlPoint& cp, bool did_push, int64_t distance)
789 ModelRepresentation mr;
792 model_representation (cp, mr);
794 /* how much are we changing the central point by */
796 ydelta = mr.yval - mr.ypos;
799 apply the full change to the central point, and interpolate
800 on both axes to cover all model points represented
801 by the control point.
804 /* change all points before the primary point */
806 for (AutomationList::iterator i = mr.start; i != cp.model(); ++i) {
808 double fract = ((*i)->when - mr.xmin) / (mr.xpos - mr.xmin);
809 double y_delta = ydelta * fract;
810 double x_delta = distance * fract;
814 if (y_delta || x_delta) {
815 alist->modify (i, (*i)->when + x_delta, mr.ymin + y_delta);
819 /* change the primary point */
821 update_pending = true;
822 alist->modify (cp.model(), mr.xval, mr.yval);
825 /* change later points */
827 AutomationList::iterator i = cp.model();
831 while (i != mr.end) {
833 double delta = ydelta * (mr.xmax - (*i)->when) / (mr.xmax - mr.xpos);
835 /* all later points move by the same distance along the x-axis as the main point */
838 alist->modify (i, (*i)->when + distance, (*i)->value + delta);
846 /* move all points after the range represented by the view by the same distance
847 as the main point moved.
850 alist->slide (mr.end, drag_distance);
856 AutomationLine::control_points_adjacent (double xval, uint32_t & before, uint32_t& after)
858 ControlPoint *bcp = 0;
859 ControlPoint *acp = 0;
862 unit_xval = trackview.editor().frame_to_unit (xval);
864 for (vector<ControlPoint*>::iterator i = control_points.begin(); i != control_points.end(); ++i) {
866 if ((*i)->get_x() <= unit_xval) {
868 if (!bcp || (*i)->get_x() > bcp->get_x()) {
870 before = bcp->view_index();
873 } else if ((*i)->get_x() > unit_xval) {
875 after = acp->view_index();
884 AutomationLine::is_last_point (ControlPoint& cp)
886 ModelRepresentation mr;
888 model_representation (cp, mr);
890 // If the list is not empty, and the point is the last point in the list
892 if (!alist->empty() && mr.end == alist->end()) {
900 AutomationLine::is_first_point (ControlPoint& cp)
902 ModelRepresentation mr;
904 model_representation (cp, mr);
906 // If the list is not empty, and the point is the first point in the list
908 if (!alist->empty() && mr.start == alist->begin()) {
915 // This is copied into AudioRegionGainLine
917 AutomationLine::remove_point (ControlPoint& cp)
919 ModelRepresentation mr;
921 model_representation (cp, mr);
923 trackview.editor().session()->begin_reversible_command (_("remove control point"));
924 XMLNode &before = alist->get_state();
926 alist->erase (mr.start, mr.end);
928 trackview.editor().session()->add_command(new MementoCommand<AutomationList>(
929 *alist.get(), &before, &alist->get_state()));
930 trackview.editor().session()->commit_reversible_command ();
931 trackview.editor().session()->set_dirty ();
935 AutomationLine::get_selectables (nframes_t& start, nframes_t& end,
936 double botfrac, double topfrac, list<Selectable*>& results)
943 bool collecting = false;
945 /* Curse X11 and its inverted coordinate system! */
947 bot = (1.0 - topfrac) * _height;
948 top = (1.0 - botfrac) * _height;
953 for (vector<ControlPoint*>::iterator i = control_points.begin(); i != control_points.end(); ++i) {
954 double when = (*(*i)->model())->when;
956 if (when >= start && when <= end) {
958 if ((*i)->get_y() >= bot && (*i)->get_y() <= top) {
961 (*i)->set_visible(true);
963 nstart = min (nstart, when);
964 nend = max (nend, when);
970 results.push_back (new AutomationSelectable (nstart, nend, botfrac, topfrac, &trackview));
980 results.push_back (new AutomationSelectable (nstart, nend, botfrac, topfrac, &trackview));
986 AutomationLine::get_inverted_selectables (Selection&, list<Selectable*>& /*results*/)
992 AutomationLine::set_selected_points (PointSelection& points)
997 for (vector<ControlPoint*>::iterator i = control_points.begin(); i != control_points.end(); ++i) {
998 (*i)->set_selected(false);
1001 if (points.empty()) {
1005 for (PointSelection::iterator r = points.begin(); r != points.end(); ++r) {
1007 if ((*r).track != &trackview) {
1011 /* Curse X11 and its inverted coordinate system! */
1013 bot = (1.0 - (*r).high_fract) * _height;
1014 top = (1.0 - (*r).low_fract) * _height;
1016 for (vector<ControlPoint*>::iterator i = control_points.begin(); i != control_points.end(); ++i) {
1018 double rstart, rend;
1020 rstart = trackview.editor().frame_to_unit ((*r).start);
1021 rend = trackview.editor().frame_to_unit ((*r).end);
1023 if ((*i)->get_x() >= rstart && (*i)->get_x() <= rend) {
1025 if ((*i)->get_y() >= bot && (*i)->get_y() <= top) {
1027 (*i)->set_selected(true);
1035 for (vector<ControlPoint*>::iterator i = control_points.begin(); i != control_points.end(); ++i) {
1036 (*i)->show_color (false, !points_visible);
1041 void AutomationLine::set_colors() {
1042 set_line_color( ARDOUR_UI::config()->canvasvar_AutomationLine.get() );
1043 for (vector<ControlPoint*>::iterator i = control_points.begin(); i != control_points.end(); ++i) {
1044 (*i)->show_color (false, !points_visible);
1049 AutomationLine::show_selection ()
1051 TimeSelection& time (trackview.editor().get_selection().time);
1053 for (vector<ControlPoint*>::iterator i = control_points.begin(); i != control_points.end(); ++i) {
1055 (*i)->set_selected(false);
1057 for (list<AudioRange>::iterator r = time.begin(); r != time.end(); ++r) {
1058 double rstart, rend;
1060 rstart = trackview.editor().frame_to_unit ((*r).start);
1061 rend = trackview.editor().frame_to_unit ((*r).end);
1063 if ((*i)->get_x() >= rstart && (*i)->get_x() <= rend) {
1064 (*i)->set_selected(true);
1069 (*i)->show_color (false, !points_visible);
1074 AutomationLine::hide_selection ()
1076 // show_selection ();
1080 AutomationLine::list_changed ()
1086 AutomationLine::reset_callback (const Evoral::ControlList& events)
1088 ALPoints tmp_points;
1089 uint32_t npoints = events.size();
1092 for (vector<ControlPoint*>::iterator i = control_points.begin(); i != control_points.end(); ++i) {
1095 control_points.clear ();
1100 AutomationList::const_iterator ai;
1102 for (ai = events.begin(); ai != events.end(); ++ai) {
1104 double translated_x = (*ai)->when;
1105 double translated_y = (*ai)->value;
1106 model_to_view_coord (translated_x, translated_y);
1108 add_model_point (tmp_points, (*ai)->when, translated_y);
1111 determine_visible_control_points (tmp_points);
1116 AutomationLine::add_model_point (ALPoints& tmp_points, double frame, double yfract)
1118 tmp_points.push_back (ALPoint (trackview.editor().frame_to_unit (_time_converter.to(frame)),
1119 _height - (yfract * _height)));
1123 AutomationLine::reset ()
1125 update_pending = false;
1131 alist->apply_to_points (*this, &AutomationLine::reset_callback);
1135 AutomationLine::clear ()
1137 /* parent must create command */
1138 XMLNode &before = get_state();
1140 trackview.editor().session()->add_command (
1141 new MementoCommand<AutomationLine>(*this, &before, &get_state()));
1142 trackview.editor().session()->commit_reversible_command ();
1143 trackview.editor().session()->set_dirty ();
1147 AutomationLine::change_model (AutomationList::iterator /*i*/, double /*x*/, double /*y*/)
1152 AutomationLine::set_list(boost::shared_ptr<ARDOUR::AutomationList> list)
1159 AutomationLine::show_all_control_points ()
1161 points_visible = true;
1163 for (vector<ControlPoint*>::iterator i = control_points.begin(); i != control_points.end(); ++i) {
1164 (*i)->show_color((_interpolation != AutomationList::Discrete), false);
1166 (*i)->set_visible (true);
1171 AutomationLine::hide_all_but_selected_control_points ()
1173 if (alist->interpolation() == AutomationList::Discrete) {
1177 points_visible = false;
1179 for (vector<ControlPoint*>::iterator i = control_points.begin(); i != control_points.end(); ++i) {
1180 if (!(*i)->selected()) {
1181 (*i)->set_visible (false);
1187 AutomationLine::track_entered()
1189 if (alist->interpolation() != AutomationList::Discrete) {
1190 show_all_control_points();
1195 AutomationLine::track_exited()
1197 if (alist->interpolation() != AutomationList::Discrete) {
1198 hide_all_but_selected_control_points();
1203 AutomationLine::get_state (void)
1205 /* function as a proxy for the model */
1206 return alist->get_state();
1210 AutomationLine::set_state (const XMLNode &node, int version)
1212 /* function as a proxy for the model */
1213 return alist->set_state (node, version);
1217 AutomationLine::view_to_model_coord (double& x, double& y) const
1219 /* TODO: This should be more generic ... */
1220 if (alist->parameter().type() == GainAutomation ||
1221 alist->parameter().type() == EnvelopeAutomation) {
1222 y = slider_position_to_gain (y);
1225 } else if (alist->parameter().type() == PanAutomation) {
1226 // vertical coordinate axis reversal
1228 } else if (alist->parameter().type() == PluginAutomation) {
1229 y = y * (double)(alist->get_max_y()- alist->get_min_y()) + alist->get_min_y();
1231 y = (int)(y * alist->parameter().max());
1234 x = _time_converter.from(x);
1238 AutomationLine::model_to_view_coord (double& x, double& y) const
1240 /* TODO: This should be more generic ... */
1241 if (alist->parameter().type() == GainAutomation ||
1242 alist->parameter().type() == EnvelopeAutomation) {
1243 y = gain_to_slider_position (y);
1244 } else if (alist->parameter().type() == PanAutomation) {
1245 // vertical coordinate axis reversal
1247 } else if (alist->parameter().type() == PluginAutomation) {
1248 y = (y - alist->get_min_y()) / (double)(alist->get_max_y()- alist->get_min_y());
1250 y = y / (double)alist->parameter().max(); /* ... like this */
1253 x = _time_converter.to(x);
1258 AutomationLine::set_interpolation(AutomationList::InterpolationStyle style)
1260 _interpolation = style;
1262 if (style == AutomationList::Discrete) {
1263 show_all_control_points();
1266 hide_all_but_selected_control_points();
1272 AutomationLine::add_visible_control_point (uint32_t view_index, uint32_t pi, double tx, double ty, AutomationList::iterator model, uint32_t npoints)
1274 if (view_index >= control_points.size()) {
1276 /* make sure we have enough control points */
1278 ControlPoint* ncp = new ControlPoint (*this);
1279 ncp->set_size (control_point_box_size ());
1281 control_points.push_back (ncp);
1284 ControlPoint::ShapeType shape;
1286 if (!terminal_points_can_slide) {
1288 control_points[view_index]->set_can_slide(false);
1290 shape = ControlPoint::Start;
1292 shape = ControlPoint::Full;
1294 } else if (pi == npoints - 1) {
1295 control_points[view_index]->set_can_slide(false);
1296 shape = ControlPoint::End;
1298 control_points[view_index]->set_can_slide(true);
1299 shape = ControlPoint::Full;
1302 control_points[view_index]->set_can_slide(true);
1303 shape = ControlPoint::Full;
1306 control_points[view_index]->reset (tx, ty, model, view_index, shape);
1308 /* finally, control visibility */
1310 if (_visible && points_visible) {
1311 control_points[view_index]->show ();
1312 control_points[view_index]->set_visible (true);
1314 if (!points_visible) {
1315 control_points[view_index]->set_visible (false);