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 "rgb_macros.h"
37 #include "ardour_ui.h"
38 #include "public_editor.h"
40 #include "selection.h"
41 #include "time_axis_view.h"
42 #include "point_selection.h"
43 #include "automation_selectable.h"
44 #include "automation_time_axis.h"
45 #include "public_editor.h"
47 #include <ardour/session.h>
53 using namespace ARDOUR;
55 using namespace Editing;
56 using namespace Gnome; // for Canvas
58 AutomationLine::AutomationLine (const string& name, TimeAxisView& tv, ArdourCanvas::Group& parent, boost::shared_ptr<AutomationList> al)
62 _parent_group (parent)
64 _interpolation = al->interpolation();
65 points_visible = false;
66 update_pending = false;
67 _uses_gain_mapping = false;
70 terminal_points_can_slide = true;
73 group = new ArdourCanvas::Group (parent);
74 group->property_x() = 0.0;
75 group->property_y() = 0.0;
77 line = new ArdourCanvas::Line (*group);
78 line->property_width_pixels() = (guint)1;
79 line->set_data ("line", this);
81 line->signal_event().connect (mem_fun (*this, &AutomationLine::event_handler));
83 alist->StateChanged.connect (mem_fun(*this, &AutomationLine::list_changed));
85 trackview.session().register_with_memento_command_factory(alist->id(), this);
87 if (alist->parameter().type() == GainAutomation ||
88 alist->parameter().type() == EnvelopeAutomation) {
89 set_uses_gain_mapping (true);
92 set_interpolation(alist->interpolation());
95 AutomationLine::~AutomationLine ()
97 vector_delete (&control_points);
102 AutomationLine::event_handler (GdkEvent* event)
104 return PublicEditor::instance().canvas_line_event (event, line, this);
108 AutomationLine::queue_reset ()
110 if (!update_pending) {
111 update_pending = true;
112 Gtkmm2ext::UI::instance()->call_slot (mem_fun(*this, &AutomationLine::reset));
117 AutomationLine::show ()
119 if (_interpolation != AutomationList::Discrete) {
123 if (points_visible) {
124 for (vector<ControlPoint*>::iterator i = control_points.begin(); i != control_points.end(); ++i) {
133 AutomationLine::hide ()
136 for (vector<ControlPoint*>::iterator i = control_points.begin(); i != control_points.end(); ++i) {
143 AutomationLine::control_point_box_size ()
145 if (_interpolation == AutomationList::Discrete) {
146 return max((_height*4.0) / (double)(alist->parameter().max() - alist->parameter().min()),
150 if (_height > TimeAxisView::hLarger) {
152 } else if (_height > (guint32) TimeAxisView::hNormal) {
159 AutomationLine::set_height (guint32 h)
164 double bsz = control_point_box_size();
166 for (vector<ControlPoint*>::iterator i = control_points.begin(); i != control_points.end(); ++i) {
167 (*i)->set_size (bsz);
175 AutomationLine::set_line_color (uint32_t color)
178 line->property_fill_color_rgba() = color;
182 AutomationLine::set_uses_gain_mapping (bool yn)
184 if (yn != _uses_gain_mapping) {
185 _uses_gain_mapping = yn;
191 AutomationLine::nth (uint32_t n)
193 if (n < control_points.size()) {
194 return control_points[n];
201 AutomationLine::modify_point_y (ControlPoint& cp, double y)
203 /* clamp y-coord appropriately. y is supposed to be a normalized fraction (0.0-1.0),
204 and needs to be converted to a canvas unit distance.
209 y = _height - (y * _height);
211 double const x = trackview.editor().frame_to_unit ((*cp.model())->when);
213 trackview.editor().current_session()->begin_reversible_command (_("automation event move"));
214 trackview.editor().current_session()->add_command (new MementoCommand<AutomationList>(*alist.get(), &get_state(), 0));
216 cp.move_to (x, y, ControlPoint::Full);
217 reset_line_coords (cp);
219 if (line_points.size() > 1) {
220 line->property_points() = line_points;
224 sync_model_with_view_point (cp, false, 0);
227 update_pending = false;
229 trackview.editor().current_session()->add_command (new MementoCommand<AutomationList>(*alist.get(), 0, &alist->get_state()));
230 trackview.editor().current_session()->commit_reversible_command ();
231 trackview.editor().current_session()->set_dirty ();
236 AutomationLine::modify_view_point (ControlPoint& cp, double x, double y, bool with_push)
239 uint32_t last_movable = UINT_MAX;
240 double x_limit = DBL_MAX;
242 /* this just changes the current view. it does not alter
243 the model in any way at all.
246 /* clamp y-coord appropriately. y is supposed to be a normalized fraction (0.0-1.0),
247 and needs to be converted to a canvas unit distance.
252 y = _height - (y * _height);
254 if (cp.can_slide()) {
256 /* x-coord cannot move beyond adjacent points or the start/end, and is
257 already in frames. it needs to be converted to canvas units.
260 x = trackview.editor().frame_to_unit (x);
262 /* clamp x position using view coordinates */
264 ControlPoint *before;
267 if (cp.view_index()) {
268 before = nth (cp.view_index() - 1);
269 x = max (x, before->get_x()+1.0);
276 if (cp.view_index() < control_points.size() - 1) {
278 after = nth (cp.view_index() + 1);
280 /*if it is a "spike" leave the x alone */
282 if (after->get_x() - before->get_x() < 2) {
286 x = min (x, after->get_x()-1.0);
296 /* find the first point that can't move */
298 for (uint32_t n = cp.view_index() + 1; (after = nth (n)) != 0; ++n) {
299 if (!after->can_slide()) {
300 x_limit = after->get_x() - 1.0;
301 last_movable = after->view_index();
306 delta = x - cp.get_x();
311 /* leave the x-coordinate alone */
313 x = trackview.editor().frame_to_unit ((*cp.model())->when);
319 cp.move_to (x, y, ControlPoint::Full);
320 reset_line_coords (cp);
324 uint32_t limit = min (control_points.size(), (size_t)last_movable);
326 /* move the current point to wherever the user told it to go, subject
330 cp.move_to (min (x, x_limit), y, ControlPoint::Full);
331 reset_line_coords (cp);
333 /* now move all subsequent control points, to reflect the motion.
336 for (uint32_t i = cp.view_index() + 1; i < limit; ++i) {
337 ControlPoint *p = nth (i);
340 if (p->can_slide()) {
341 new_x = min (p->get_x() + delta, x_limit);
342 p->move_to (new_x, p->get_y(), ControlPoint::Full);
343 reset_line_coords (*p);
350 AutomationLine::reset_line_coords (ControlPoint& cp)
352 if (cp.view_index() < line_points.size()) {
353 line_points[cp.view_index()].set_x (cp.get_x());
354 line_points[cp.view_index()].set_y (cp.get_y());
359 AutomationLine::sync_model_with_view_line (uint32_t start, uint32_t end)
364 update_pending = true;
366 for (uint32_t i = start; i <= end; ++i) {
368 sync_model_with_view_point (*p, false, 0);
373 AutomationLine::model_representation (ControlPoint& cp, ModelRepresentation& mr)
375 /* part one: find out where the visual control point is.
376 initial results are in canvas units. ask the
377 line to convert them to something relevant.
380 mr.xval = (nframes_t) floor (cp.get_x());
381 mr.yval = 1.0 - (cp.get_y() / _height);
383 /* if xval has not changed, set it directly from the model to avoid rounding errors */
385 if (mr.xval == trackview.editor().frame_to_unit((*cp.model())->when)) {
386 mr.xval = (nframes_t) (*cp.model())->when;
388 mr.xval = trackview.editor().unit_to_frame (mr.xval);
391 /* virtual call: this will do the right thing
392 for whatever particular type of line we are.
395 view_to_model_y (mr.yval);
397 /* part 2: find out where the model point is now
400 mr.xpos = (nframes_t) (*cp.model())->when;
401 mr.ypos = (*cp.model())->value;
403 /* part 3: get the position of the visual control
404 points before and after us.
407 ControlPoint* before;
410 if (cp.view_index()) {
411 before = nth (cp.view_index() - 1);
416 after = nth (cp.view_index() + 1);
419 mr.xmin = (nframes_t) (*before->model())->when;
420 mr.ymin = (*before->model())->value;
421 mr.start = before->model();
426 mr.start = cp.model();
430 mr.end = after->model();
440 AutomationLine::determine_visible_control_points (ALPoints& points)
442 uint32_t view_index, pi, n;
443 AutomationList::iterator model;
445 double last_control_point_x = 0.0;
446 double last_control_point_y = 0.0;
447 uint32_t this_rx = 0;
448 uint32_t prev_rx = 0;
449 uint32_t this_ry = 0;
450 uint32_t prev_ry = 0;
455 /* hide all existing points, and the line */
459 for (vector<ControlPoint*>::iterator i = control_points.begin(); i != control_points.end(); ++i) {
466 if (points.empty()) {
470 npoints = points.size();
472 /* compute derivative/slope for the entire line */
474 slope = new double[npoints];
476 for (n = 0; n < npoints - 1; ++n) {
477 double xdelta = points[n+1].x - points[n].x;
478 double ydelta = points[n+1].y - points[n].y;
479 slope[n] = ydelta/xdelta;
482 box_size = (uint32_t) control_point_box_size ();
484 /* read all points and decide which ones to show as control points */
488 for (model = alist->begin(), pi = 0; pi < npoints; ++model, ++pi) {
490 double tx = points[pi].x;
491 double ty = points[pi].y;
493 if (isnan (tx) || isnan (ty)) {
494 warning << string_compose (_("Ignoring illegal points on AutomationLine \"%1\""),
499 /* now ensure that the control_points vector reflects the current curve
500 state, but don't plot control points too close together. also, don't
501 plot a series of points all with the same value.
503 always plot the first and last points, of course.
506 if (invalid_point (points, pi)) {
507 /* for some reason, we are supposed to ignore this point,
508 but still keep track of the model index.
513 if (pi > 0 && pi < npoints - 1) {
514 if (slope[pi] == slope[pi-1]) {
516 /* no reason to display this point */
522 /* need to round here. the ultimate coordinates are integer
523 pixels, so tiny deltas in the coords will be eliminated
524 and we end up with "colinear" line segments. since the
525 line rendering code in libart doesn't like this very
526 much, we eliminate them here. don't do this for the first and last
530 this_rx = (uint32_t) rint (tx);
531 this_ry = (uint32_t) rint (ty);
533 if (view_index && pi != npoints && /* not the first, not the last */
534 (((this_rx == prev_rx) && (this_ry == prev_ry)) || /* same point */
535 (((this_rx - prev_rx) < (box_size + 2)) && /* not identical, but still too close horizontally */
536 (abs ((int)(this_ry - prev_ry)) < (int) (box_size + 2))))) { /* too close vertically */
540 /* ok, we should display this point */
542 if (view_index >= cpsize) {
544 /* make sure we have enough control points */
546 ControlPoint* ncp = new ControlPoint (*this);
548 ncp->set_size (box_size);
550 control_points.push_back (ncp);
554 ControlPoint::ShapeType shape;
556 if (!terminal_points_can_slide) {
558 control_points[view_index]->set_can_slide(false);
560 shape = ControlPoint::Start;
562 shape = ControlPoint::Full;
564 } else if (pi == npoints - 1) {
565 control_points[view_index]->set_can_slide(false);
566 shape = ControlPoint::End;
568 control_points[view_index]->set_can_slide(true);
569 shape = ControlPoint::Full;
572 control_points[view_index]->set_can_slide(true);
573 shape = ControlPoint::Full;
576 last_control_point_x = tx;
577 last_control_point_y = ty;
579 control_points[view_index]->reset (tx, ty, model, view_index, shape);
584 /* finally, control visibility */
586 if (_visible && points_visible) {
587 control_points[view_index]->show ();
588 control_points[view_index]->set_visible (true);
590 if (!points_visible) {
591 control_points[view_index]->set_visible (false);
598 /* discard extra CP's to avoid confusing ourselves */
600 while (control_points.size() > view_index) {
601 ControlPoint* cp = control_points.back();
602 control_points.pop_back ();
606 if (!terminal_points_can_slide) {
607 control_points.back()->set_can_slide(false);
612 if (view_index > 1) {
614 npoints = view_index;
616 /* reset the line coordinates */
618 while (line_points.size() < npoints) {
619 line_points.push_back (Art::Point (0,0));
622 while (line_points.size() > npoints) {
623 line_points.pop_back ();
626 for (view_index = 0; view_index < npoints; ++view_index) {
627 line_points[view_index].set_x (control_points[view_index]->get_x());
628 line_points[view_index].set_y (control_points[view_index]->get_y());
631 line->property_points() = line_points;
633 if (_visible && _interpolation != AutomationList::Discrete) {
639 set_selected_points (trackview.editor().get_selection().points);
644 AutomationLine::get_verbose_cursor_string (double fraction) const
646 std::string s = fraction_to_string (fraction);
647 if (_uses_gain_mapping) {
655 * @param fraction y fraction
656 * @return string representation of this value, using dB if appropriate.
660 AutomationLine::fraction_to_string (double fraction) const
664 if (_uses_gain_mapping) {
665 if (fraction == 0.0) {
666 snprintf (buf, sizeof (buf), "-inf");
668 snprintf (buf, sizeof (buf), "%.1f", coefficient_to_dB (slider_position_to_gain (fraction)));
671 view_to_model_y (fraction);
672 if (EventTypeMap::instance().is_integer (alist->parameter())) {
673 snprintf (buf, sizeof (buf), "%d", (int)fraction);
675 snprintf (buf, sizeof (buf), "%.2f", fraction);
684 * @param s Value string in the form as returned by fraction_to_string.
685 * @return Corresponding y fraction.
689 AutomationLine::string_to_fraction (string const & s) const
696 sscanf (s.c_str(), "%lf", &v);
698 if (_uses_gain_mapping) {
699 v = gain_to_slider_position (dB_to_coefficient (v));
708 AutomationLine::invalid_point (ALPoints& p, uint32_t index)
710 return p[index].x == max_frames && p[index].y == DBL_MAX;
714 AutomationLine::invalidate_point (ALPoints& p, uint32_t index)
716 p[index].x = max_frames;
717 p[index].y = DBL_MAX;
721 AutomationLine::start_drag (ControlPoint* cp, nframes_t x, float fraction)
723 if (trackview.editor().current_session() == 0) { /* how? */
730 str = _("automation event move");
732 str = _("automation range drag");
735 trackview.editor().current_session()->begin_reversible_command (str);
736 trackview.editor().current_session()->add_command (new MementoCommand<AutomationList>(*alist.get(), &get_state(), 0));
740 first_drag_fraction = fraction;
741 last_drag_fraction = fraction;
747 AutomationLine::point_drag (ControlPoint& cp, nframes_t x, float fraction, bool with_push)
750 drag_distance += (x - drag_x);
752 drag_distance -= (drag_x - x);
757 modify_view_point (cp, x, fraction, with_push);
759 if (line_points.size() > 1) {
760 line->property_points() = line_points;
764 did_push = with_push;
768 AutomationLine::line_drag (uint32_t i1, uint32_t i2, float fraction, bool with_push)
770 double ydelta = fraction - last_drag_fraction;
772 did_push = with_push;
774 last_drag_fraction = fraction;
781 for (uint32_t i = i1 ; i <= i2; i++) {
783 modify_view_point (*cp, trackview.editor().unit_to_frame (cp->get_x()), ((_height - cp->get_y()) /_height) + ydelta, with_push);
786 if (line_points.size() > 1) {
787 line->property_points() = line_points;
794 AutomationLine::end_drag (ControlPoint* cp)
803 sync_model_with_view_point (*cp, did_push, drag_distance);
805 sync_model_with_view_line (line_drag_cp1, line_drag_cp2);
810 update_pending = false;
812 trackview.editor().current_session()->add_command (new MementoCommand<AutomationList>(*alist.get(), 0, &alist->get_state()));
813 trackview.editor().current_session()->commit_reversible_command ();
814 trackview.editor().current_session()->set_dirty ();
819 AutomationLine::sync_model_with_view_point (ControlPoint& cp, bool did_push, int64_t distance)
821 ModelRepresentation mr;
824 model_representation (cp, mr);
826 /* how much are we changing the central point by */
828 ydelta = mr.yval - mr.ypos;
831 apply the full change to the central point, and interpolate
832 on both axes to cover all model points represented
833 by the control point.
836 /* change all points before the primary point */
838 for (AutomationList::iterator i = mr.start; i != cp.model(); ++i) {
840 double fract = ((*i)->when - mr.xmin) / (mr.xpos - mr.xmin);
841 double y_delta = ydelta * fract;
842 double x_delta = distance * fract;
846 if (y_delta || x_delta) {
847 alist->modify (i, (*i)->when + x_delta, mr.ymin + y_delta);
851 /* change the primary point */
853 update_pending = true;
854 alist->modify (cp.model(), mr.xval, mr.yval);
857 /* change later points */
859 AutomationList::iterator i = cp.model();
863 while (i != mr.end) {
865 double delta = ydelta * (mr.xmax - (*i)->when) / (mr.xmax - mr.xpos);
867 /* all later points move by the same distance along the x-axis as the main point */
870 alist->modify (i, (*i)->when + distance, (*i)->value + delta);
878 /* move all points after the range represented by the view by the same distance
879 as the main point moved.
882 alist->slide (mr.end, drag_distance);
888 AutomationLine::control_points_adjacent (double xval, uint32_t & before, uint32_t& after)
890 ControlPoint *bcp = 0;
891 ControlPoint *acp = 0;
894 /* xval is in frames */
896 unit_xval = trackview.editor().frame_to_unit (xval);
898 for (vector<ControlPoint*>::iterator i = control_points.begin(); i != control_points.end(); ++i) {
900 if ((*i)->get_x() <= unit_xval) {
902 if (!bcp || (*i)->get_x() > bcp->get_x()) {
904 before = bcp->view_index();
907 } else if ((*i)->get_x() > unit_xval) {
909 after = acp->view_index();
918 AutomationLine::is_last_point (ControlPoint& cp)
920 ModelRepresentation mr;
922 model_representation (cp, mr);
924 // If the list is not empty, and the point is the last point in the list
926 if (!alist->empty() && mr.end == alist->end()) {
934 AutomationLine::is_first_point (ControlPoint& cp)
936 ModelRepresentation mr;
938 model_representation (cp, mr);
940 // If the list is not empty, and the point is the first point in the list
942 if (!alist->empty() && mr.start == alist->begin()) {
949 // This is copied into AudioRegionGainLine
951 AutomationLine::remove_point (ControlPoint& cp)
953 ModelRepresentation mr;
955 model_representation (cp, mr);
957 trackview.editor().current_session()->begin_reversible_command (_("remove control point"));
958 XMLNode &before = alist->get_state();
960 alist->erase (mr.start, mr.end);
962 trackview.editor().current_session()->add_command(new MementoCommand<AutomationList>(
963 *alist.get(), &before, &alist->get_state()));
964 trackview.editor().current_session()->commit_reversible_command ();
965 trackview.editor().current_session()->set_dirty ();
969 AutomationLine::get_selectables (nframes_t& start, nframes_t& end,
970 double botfrac, double topfrac, list<Selectable*>& results)
977 bool collecting = false;
979 /* Curse X11 and its inverted coordinate system! */
981 bot = (1.0 - topfrac) * _height;
982 top = (1.0 - botfrac) * _height;
987 for (vector<ControlPoint*>::iterator i = control_points.begin(); i != control_points.end(); ++i) {
989 nframes_t when = (nframes_t) (*(*i)->model())->when;
991 if (when >= start && when <= end) {
993 if ((*i)->get_y() >= bot && (*i)->get_y() <= top) {
996 (*i)->set_visible(true);
998 nstart = min (nstart, when);
999 nend = max (nend, when);
1005 results.push_back (new AutomationSelectable (nstart, nend, botfrac, topfrac, trackview));
1007 nstart = max_frames;
1015 results.push_back (new AutomationSelectable (nstart, nend, botfrac, topfrac, trackview));
1021 AutomationLine::get_inverted_selectables (Selection&, list<Selectable*>& results)
1027 AutomationLine::set_selected_points (PointSelection& points)
1032 for (vector<ControlPoint*>::iterator i = control_points.begin(); i != control_points.end(); ++i) {
1033 (*i)->set_selected(false);
1036 if (points.empty()) {
1040 for (PointSelection::iterator r = points.begin(); r != points.end(); ++r) {
1042 if (&(*r).track != &trackview) {
1046 /* Curse X11 and its inverted coordinate system! */
1048 bot = (1.0 - (*r).high_fract) * _height;
1049 top = (1.0 - (*r).low_fract) * _height;
1051 for (vector<ControlPoint*>::iterator i = control_points.begin(); i != control_points.end(); ++i) {
1053 double rstart, rend;
1055 rstart = trackview.editor().frame_to_unit ((*r).start);
1056 rend = trackview.editor().frame_to_unit ((*r).end);
1058 if ((*i)->get_x() >= rstart && (*i)->get_x() <= rend) {
1060 if ((*i)->get_y() >= bot && (*i)->get_y() <= top) {
1062 (*i)->set_selected(true);
1070 for (vector<ControlPoint*>::iterator i = control_points.begin(); i != control_points.end(); ++i) {
1071 (*i)->show_color (false, !points_visible);
1076 void AutomationLine::set_colors() {
1077 set_line_color( ARDOUR_UI::config()->canvasvar_AutomationLine.get() );
1078 for (vector<ControlPoint*>::iterator i = control_points.begin(); i != control_points.end(); ++i) {
1079 (*i)->show_color (false, !points_visible);
1084 AutomationLine::show_selection ()
1086 TimeSelection& time (trackview.editor().get_selection().time);
1088 for (vector<ControlPoint*>::iterator i = control_points.begin(); i != control_points.end(); ++i) {
1090 (*i)->set_selected(false);
1092 for (list<AudioRange>::iterator r = time.begin(); r != time.end(); ++r) {
1093 double rstart, rend;
1095 rstart = trackview.editor().frame_to_unit ((*r).start);
1096 rend = trackview.editor().frame_to_unit ((*r).end);
1098 if ((*i)->get_x() >= rstart && (*i)->get_x() <= rend) {
1099 (*i)->set_selected(true);
1104 (*i)->show_color (false, !points_visible);
1109 AutomationLine::hide_selection ()
1111 // show_selection ();
1115 AutomationLine::list_changed ()
1121 AutomationLine::reset_callback (const Evoral::ControlList& events)
1123 ALPoints tmp_points;
1124 uint32_t npoints = events.size();
1127 for (vector<ControlPoint*>::iterator i = control_points.begin(); i != control_points.end(); ++i) {
1130 control_points.clear ();
1135 AutomationList::const_iterator ai;
1137 for (ai = events.begin(); ai != events.end(); ++ai) {
1139 double translated_y = (*ai)->value;
1140 model_to_view_y (translated_y);
1142 add_model_point (tmp_points, (*ai)->when, translated_y);
1145 determine_visible_control_points (tmp_points);
1150 AutomationLine::add_model_point (ALPoints& tmp_points, double frame, double yfract)
1152 tmp_points.push_back (ALPoint (trackview.editor().frame_to_unit (frame),
1153 _height - (yfract * _height)));
1157 AutomationLine::reset ()
1159 update_pending = false;
1165 alist->apply_to_points (*this, &AutomationLine::reset_callback);
1169 AutomationLine::clear ()
1171 /* parent must create command */
1172 XMLNode &before = get_state();
1174 trackview.editor().current_session()->add_command (new MementoCommand<AutomationLine>(*this, &before, &get_state()));
1175 trackview.editor().current_session()->commit_reversible_command ();
1176 trackview.editor().current_session()->set_dirty ();
1180 AutomationLine::change_model (AutomationList::iterator i, double x, double y)
1185 AutomationLine::set_list(boost::shared_ptr<ARDOUR::AutomationList> list)
1192 AutomationLine::show_all_control_points ()
1194 points_visible = true;
1196 for (vector<ControlPoint*>::iterator i = control_points.begin(); i != control_points.end(); ++i) {
1197 (*i)->show_color((_interpolation != AutomationList::Discrete), false);
1199 (*i)->set_visible (true);
1204 AutomationLine::hide_all_but_selected_control_points ()
1206 if (alist->interpolation() == AutomationList::Discrete) {
1210 points_visible = false;
1212 for (vector<ControlPoint*>::iterator i = control_points.begin(); i != control_points.end(); ++i) {
1213 if (!(*i)->selected()) {
1214 (*i)->set_visible (false);
1220 AutomationLine::track_entered()
1222 if (alist->interpolation() != AutomationList::Discrete) {
1223 show_all_control_points();
1228 AutomationLine::track_exited()
1230 if (alist->interpolation() != AutomationList::Discrete) {
1231 hide_all_but_selected_control_points();
1236 AutomationLine::get_state (void)
1238 /* function as a proxy for the model */
1239 return alist->get_state();
1243 AutomationLine::set_state (const XMLNode &node)
1245 /* function as a proxy for the model */
1246 return alist->set_state (node);
1250 AutomationLine::view_to_model_y (double& y) const
1252 /* TODO: This should be more generic ... */
1253 if (alist->parameter().type() == GainAutomation ||
1254 alist->parameter().type() == EnvelopeAutomation) {
1255 y = slider_position_to_gain (y);
1258 } else if (alist->parameter().type() == PanAutomation) {
1259 // vertical coordinate axis reversal
1261 } else if (alist->parameter().type() == PluginAutomation) {
1262 y = y * (double)(alist->get_max_y()- alist->get_min_y()) + alist->get_min_y();
1264 y = (int)(y * alist->parameter().max());
1269 AutomationLine::model_to_view_y (double& y) const
1271 /* TODO: This should be more generic ... */
1272 if (alist->parameter().type() == GainAutomation ||
1273 alist->parameter().type() == EnvelopeAutomation) {
1274 y = gain_to_slider_position (y);
1275 } else if (alist->parameter().type() == PanAutomation) {
1276 // vertical coordinate axis reversal
1278 } else if (alist->parameter().type() == PluginAutomation) {
1279 y = (y - alist->get_min_y()) / (double)(alist->get_max_y()- alist->get_min_y());
1281 y = y / (double)alist->parameter().max(); /* ... like this */
1287 AutomationLine::set_interpolation(AutomationList::InterpolationStyle style)
1289 _interpolation = style;
1291 if (style == AutomationList::Discrete) {
1292 show_all_control_points();
1295 hide_all_but_selected_control_points();