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)
122 if (points_visible) {
123 for (vector<ControlPoint*>::iterator i = control_points.begin(); i != control_points.end(); ++i) {
132 AutomationLine::hide ()
135 for (vector<ControlPoint*>::iterator i = control_points.begin(); i != control_points.end(); ++i) {
142 AutomationLine::control_point_box_size ()
144 if (_interpolation == AutomationList::Discrete) {
145 return max((_height*4.0) / (double)(alist->parameter().max() - alist->parameter().min()),
149 if (_height > TimeAxisView::hLarger) {
151 } else if (_height > (guint32) TimeAxisView::hNormal) {
158 AutomationLine::set_height (guint32 h)
163 double bsz = control_point_box_size();
165 for (vector<ControlPoint*>::iterator i = control_points.begin(); i != control_points.end(); ++i) {
166 (*i)->set_size (bsz);
174 AutomationLine::set_line_color (uint32_t color)
177 line->property_fill_color_rgba() = color;
181 AutomationLine::set_uses_gain_mapping (bool yn)
183 if (yn != _uses_gain_mapping) {
184 _uses_gain_mapping = yn;
190 AutomationLine::nth (uint32_t n)
192 if (n < control_points.size()) {
193 return control_points[n];
200 AutomationLine::modify_point_y (ControlPoint& cp, double y)
202 /* clamp y-coord appropriately. y is supposed to be a normalized fraction (0.0-1.0),
203 and needs to be converted to a canvas unit distance.
208 y = _height - (y * _height);
210 double const x = trackview.editor().frame_to_unit ((*cp.model())->when);
212 trackview.editor().current_session()->begin_reversible_command (_("automation event move"));
213 trackview.editor().current_session()->add_command (new MementoCommand<AutomationList>(*alist.get(), &get_state(), 0));
215 cp.move_to (x, y, ControlPoint::Full);
216 reset_line_coords (cp);
218 if (line_points.size() > 1) {
219 line->property_points() = line_points;
223 sync_model_with_view_point (cp, false, 0);
226 update_pending = false;
228 trackview.editor().current_session()->add_command (new MementoCommand<AutomationList>(*alist.get(), 0, &alist->get_state()));
229 trackview.editor().current_session()->commit_reversible_command ();
230 trackview.editor().current_session()->set_dirty ();
235 AutomationLine::modify_view_point (ControlPoint& cp, double x, double y, bool with_push)
238 uint32_t last_movable = UINT_MAX;
239 double x_limit = DBL_MAX;
241 /* this just changes the current view. it does not alter
242 the model in any way at all.
245 /* clamp y-coord appropriately. y is supposed to be a normalized fraction (0.0-1.0),
246 and needs to be converted to a canvas unit distance.
251 y = _height - (y * _height);
253 if (cp.can_slide()) {
255 /* x-coord cannot move beyond adjacent points or the start/end, and is
256 already in frames. it needs to be converted to canvas units.
259 x = trackview.editor().frame_to_unit (x);
261 /* clamp x position using view coordinates */
263 ControlPoint *before;
266 if (cp.view_index()) {
267 before = nth (cp.view_index() - 1);
268 x = max (x, before->get_x()+1.0);
275 if (cp.view_index() < control_points.size() - 1) {
277 after = nth (cp.view_index() + 1);
279 /*if it is a "spike" leave the x alone */
281 if (after->get_x() - before->get_x() < 2) {
285 x = min (x, after->get_x()-1.0);
295 /* find the first point that can't move */
297 for (uint32_t n = cp.view_index() + 1; (after = nth (n)) != 0; ++n) {
298 if (!after->can_slide()) {
299 x_limit = after->get_x() - 1.0;
300 last_movable = after->view_index();
305 delta = x - cp.get_x();
310 /* leave the x-coordinate alone */
312 x = trackview.editor().frame_to_unit ((*cp.model())->when);
318 cp.move_to (x, y, ControlPoint::Full);
319 reset_line_coords (cp);
323 uint32_t limit = min (control_points.size(), (size_t)last_movable);
325 /* move the current point to wherever the user told it to go, subject
329 cp.move_to (min (x, x_limit), y, ControlPoint::Full);
330 reset_line_coords (cp);
332 /* now move all subsequent control points, to reflect the motion.
335 for (uint32_t i = cp.view_index() + 1; i < limit; ++i) {
336 ControlPoint *p = nth (i);
339 if (p->can_slide()) {
340 new_x = min (p->get_x() + delta, x_limit);
341 p->move_to (new_x, p->get_y(), ControlPoint::Full);
342 reset_line_coords (*p);
349 AutomationLine::reset_line_coords (ControlPoint& cp)
351 if (cp.view_index() < line_points.size()) {
352 line_points[cp.view_index()].set_x (cp.get_x());
353 line_points[cp.view_index()].set_y (cp.get_y());
358 AutomationLine::sync_model_with_view_line (uint32_t start, uint32_t end)
363 update_pending = true;
365 for (uint32_t i = start; i <= end; ++i) {
367 sync_model_with_view_point (*p, false, 0);
372 AutomationLine::model_representation (ControlPoint& cp, ModelRepresentation& mr)
374 /* part one: find out where the visual control point is.
375 initial results are in canvas units. ask the
376 line to convert them to something relevant.
379 mr.xval = (nframes_t) floor (cp.get_x());
380 mr.yval = 1.0 - (cp.get_y() / _height);
382 /* if xval has not changed, set it directly from the model to avoid rounding errors */
384 if (mr.xval == trackview.editor().frame_to_unit((*cp.model())->when)) {
385 mr.xval = (nframes_t) (*cp.model())->when;
387 mr.xval = trackview.editor().unit_to_frame (mr.xval);
390 /* virtual call: this will do the right thing
391 for whatever particular type of line we are.
394 view_to_model_y (mr.yval);
396 /* part 2: find out where the model point is now
399 mr.xpos = (nframes_t) (*cp.model())->when;
400 mr.ypos = (*cp.model())->value;
402 /* part 3: get the position of the visual control
403 points before and after us.
406 ControlPoint* before;
409 if (cp.view_index()) {
410 before = nth (cp.view_index() - 1);
415 after = nth (cp.view_index() + 1);
418 mr.xmin = (nframes_t) (*before->model())->when;
419 mr.ymin = (*before->model())->value;
420 mr.start = before->model();
425 mr.start = cp.model();
429 mr.end = after->model();
439 AutomationLine::determine_visible_control_points (ALPoints& points)
441 uint32_t view_index, pi, n;
442 AutomationList::iterator model;
444 double last_control_point_x = 0.0;
445 double last_control_point_y = 0.0;
446 uint32_t this_rx = 0;
447 uint32_t prev_rx = 0;
448 uint32_t this_ry = 0;
449 uint32_t prev_ry = 0;
454 /* hide all existing points, and the line */
458 for (vector<ControlPoint*>::iterator i = control_points.begin(); i != control_points.end(); ++i) {
465 if (points.empty()) {
469 npoints = points.size();
471 /* compute derivative/slope for the entire line */
473 slope = new double[npoints];
475 for (n = 0; n < npoints - 1; ++n) {
476 double xdelta = points[n+1].x - points[n].x;
477 double ydelta = points[n+1].y - points[n].y;
478 slope[n] = ydelta/xdelta;
481 box_size = (uint32_t) control_point_box_size ();
483 /* read all points and decide which ones to show as control points */
487 for (model = alist->begin(), pi = 0; pi < npoints; ++model, ++pi) {
489 double tx = points[pi].x;
490 double ty = points[pi].y;
492 if (isnan (tx) || isnan (ty)) {
493 warning << string_compose (_("Ignoring illegal points on AutomationLine \"%1\""),
498 /* now ensure that the control_points vector reflects the current curve
499 state, but don't plot control points too close together. also, don't
500 plot a series of points all with the same value.
502 always plot the first and last points, of course.
505 if (invalid_point (points, pi)) {
506 /* for some reason, we are supposed to ignore this point,
507 but still keep track of the model index.
512 if (pi > 0 && pi < npoints - 1) {
513 if (slope[pi] == slope[pi-1]) {
515 /* no reason to display this point */
521 /* need to round here. the ultimate coordinates are integer
522 pixels, so tiny deltas in the coords will be eliminated
523 and we end up with "colinear" line segments. since the
524 line rendering code in libart doesn't like this very
525 much, we eliminate them here. don't do this for the first and last
529 this_rx = (uint32_t) rint (tx);
530 this_ry = (uint32_t) rint (ty);
532 if (view_index && pi != npoints && /* not the first, not the last */
533 (((this_rx == prev_rx) && (this_ry == prev_ry)) || /* same point */
534 (((this_rx - prev_rx) < (box_size + 2)) && /* not identical, but still too close horizontally */
535 (abs ((int)(this_ry - prev_ry)) < (int) (box_size + 2))))) { /* too close vertically */
539 /* ok, we should display this point */
541 if (view_index >= cpsize) {
543 /* make sure we have enough control points */
545 ControlPoint* ncp = new ControlPoint (*this);
547 ncp->set_size (box_size);
549 control_points.push_back (ncp);
553 ControlPoint::ShapeType shape;
555 if (!terminal_points_can_slide) {
557 control_points[view_index]->set_can_slide(false);
559 shape = ControlPoint::Start;
561 shape = ControlPoint::Full;
563 } else if (pi == npoints - 1) {
564 control_points[view_index]->set_can_slide(false);
565 shape = ControlPoint::End;
567 control_points[view_index]->set_can_slide(true);
568 shape = ControlPoint::Full;
571 control_points[view_index]->set_can_slide(true);
572 shape = ControlPoint::Full;
575 last_control_point_x = tx;
576 last_control_point_y = ty;
578 control_points[view_index]->reset (tx, ty, model, view_index, shape);
583 /* finally, control visibility */
585 if (_visible && points_visible) {
586 control_points[view_index]->show ();
587 control_points[view_index]->set_visible (true);
589 if (!points_visible) {
590 control_points[view_index]->set_visible (false);
597 /* discard extra CP's to avoid confusing ourselves */
599 while (control_points.size() > view_index) {
600 ControlPoint* cp = control_points.back();
601 control_points.pop_back ();
605 if (!terminal_points_can_slide) {
606 control_points.back()->set_can_slide(false);
611 if (view_index > 1) {
613 npoints = view_index;
615 /* reset the line coordinates */
617 while (line_points.size() < npoints) {
618 line_points.push_back (Art::Point (0,0));
621 while (line_points.size() > npoints) {
622 line_points.pop_back ();
625 for (view_index = 0; view_index < npoints; ++view_index) {
626 line_points[view_index].set_x (control_points[view_index]->get_x());
627 line_points[view_index].set_y (control_points[view_index]->get_y());
630 line->property_points() = line_points;
632 if (_visible && _interpolation != AutomationList::Discrete)
637 set_selected_points (trackview.editor().get_selection().points);
642 AutomationLine::get_verbose_cursor_string (double fraction) const
644 std::string s = fraction_to_string (fraction);
645 if (_uses_gain_mapping) {
653 * @param fraction y fraction
654 * @return string representation of this value, using dB if appropriate.
658 AutomationLine::fraction_to_string (double fraction) const
662 if (_uses_gain_mapping) {
663 if (fraction == 0.0) {
664 snprintf (buf, sizeof (buf), "-inf");
666 snprintf (buf, sizeof (buf), "%.1f", coefficient_to_dB (slider_position_to_gain (fraction)));
669 view_to_model_y (fraction);
670 if (EventTypeMap::instance().is_integer (alist->parameter())) {
671 snprintf (buf, sizeof (buf), "%d", (int)fraction);
673 snprintf (buf, sizeof (buf), "%.2f", fraction);
682 * @param s Value string in the form as returned by fraction_to_string.
683 * @return Corresponding y fraction.
687 AutomationLine::string_to_fraction (string const & s) const
694 sscanf (s.c_str(), "%lf", &v);
696 if (_uses_gain_mapping) {
697 v = gain_to_slider_position (dB_to_coefficient (v));
706 AutomationLine::invalid_point (ALPoints& p, uint32_t index)
708 return p[index].x == max_frames && p[index].y == DBL_MAX;
712 AutomationLine::invalidate_point (ALPoints& p, uint32_t index)
714 p[index].x = max_frames;
715 p[index].y = DBL_MAX;
719 AutomationLine::start_drag (ControlPoint* cp, nframes_t x, float fraction)
721 if (trackview.editor().current_session() == 0) { /* how? */
728 str = _("automation event move");
730 str = _("automation range drag");
733 trackview.editor().current_session()->begin_reversible_command (str);
734 trackview.editor().current_session()->add_command (new MementoCommand<AutomationList>(*alist.get(), &get_state(), 0));
738 first_drag_fraction = fraction;
739 last_drag_fraction = fraction;
745 AutomationLine::point_drag (ControlPoint& cp, nframes_t x, float fraction, bool with_push)
748 drag_distance += (x - drag_x);
750 drag_distance -= (drag_x - x);
755 modify_view_point (cp, x, fraction, with_push);
757 if (line_points.size() > 1) {
758 line->property_points() = line_points;
762 did_push = with_push;
766 AutomationLine::line_drag (uint32_t i1, uint32_t i2, float fraction, bool with_push)
768 double ydelta = fraction - last_drag_fraction;
770 did_push = with_push;
772 last_drag_fraction = fraction;
779 for (uint32_t i = i1 ; i <= i2; i++) {
781 modify_view_point (*cp, trackview.editor().unit_to_frame (cp->get_x()), ((_height - cp->get_y()) /_height) + ydelta, with_push);
784 if (line_points.size() > 1) {
785 line->property_points() = line_points;
792 AutomationLine::end_drag (ControlPoint* cp)
801 sync_model_with_view_point (*cp, did_push, drag_distance);
803 sync_model_with_view_line (line_drag_cp1, line_drag_cp2);
808 update_pending = false;
810 trackview.editor().current_session()->add_command (new MementoCommand<AutomationList>(*alist.get(), 0, &alist->get_state()));
811 trackview.editor().current_session()->commit_reversible_command ();
812 trackview.editor().current_session()->set_dirty ();
817 AutomationLine::sync_model_with_view_point (ControlPoint& cp, bool did_push, int64_t distance)
819 ModelRepresentation mr;
822 model_representation (cp, mr);
824 /* how much are we changing the central point by */
826 ydelta = mr.yval - mr.ypos;
829 apply the full change to the central point, and interpolate
830 on both axes to cover all model points represented
831 by the control point.
834 /* change all points before the primary point */
836 for (AutomationList::iterator i = mr.start; i != cp.model(); ++i) {
838 double fract = ((*i)->when - mr.xmin) / (mr.xpos - mr.xmin);
839 double y_delta = ydelta * fract;
840 double x_delta = distance * fract;
844 if (y_delta || x_delta) {
845 alist->modify (i, (*i)->when + x_delta, mr.ymin + y_delta);
849 /* change the primary point */
851 update_pending = true;
852 alist->modify (cp.model(), mr.xval, mr.yval);
855 /* change later points */
857 AutomationList::iterator i = cp.model();
861 while (i != mr.end) {
863 double delta = ydelta * (mr.xmax - (*i)->when) / (mr.xmax - mr.xpos);
865 /* all later points move by the same distance along the x-axis as the main point */
868 alist->modify (i, (*i)->when + distance, (*i)->value + delta);
876 /* move all points after the range represented by the view by the same distance
877 as the main point moved.
880 alist->slide (mr.end, drag_distance);
886 AutomationLine::control_points_adjacent (double xval, uint32_t & before, uint32_t& after)
888 ControlPoint *bcp = 0;
889 ControlPoint *acp = 0;
892 /* xval is in frames */
894 unit_xval = trackview.editor().frame_to_unit (xval);
896 for (vector<ControlPoint*>::iterator i = control_points.begin(); i != control_points.end(); ++i) {
898 if ((*i)->get_x() <= unit_xval) {
900 if (!bcp || (*i)->get_x() > bcp->get_x()) {
902 before = bcp->view_index();
905 } else if ((*i)->get_x() > unit_xval) {
907 after = acp->view_index();
916 AutomationLine::is_last_point (ControlPoint& cp)
918 ModelRepresentation mr;
920 model_representation (cp, mr);
922 // If the list is not empty, and the point is the last point in the list
924 if (!alist->empty() && mr.end == alist->end()) {
932 AutomationLine::is_first_point (ControlPoint& cp)
934 ModelRepresentation mr;
936 model_representation (cp, mr);
938 // If the list is not empty, and the point is the first point in the list
940 if (!alist->empty() && mr.start == alist->begin()) {
947 // This is copied into AudioRegionGainLine
949 AutomationLine::remove_point (ControlPoint& cp)
951 ModelRepresentation mr;
953 model_representation (cp, mr);
955 trackview.editor().current_session()->begin_reversible_command (_("remove control point"));
956 XMLNode &before = alist->get_state();
958 alist->erase (mr.start, mr.end);
960 trackview.editor().current_session()->add_command(new MementoCommand<AutomationList>(
961 *alist.get(), &before, &alist->get_state()));
962 trackview.editor().current_session()->commit_reversible_command ();
963 trackview.editor().current_session()->set_dirty ();
967 AutomationLine::get_selectables (nframes_t& start, nframes_t& end,
968 double botfrac, double topfrac, list<Selectable*>& results)
975 bool collecting = false;
977 /* Curse X11 and its inverted coordinate system! */
979 bot = (1.0 - topfrac) * _height;
980 top = (1.0 - botfrac) * _height;
985 for (vector<ControlPoint*>::iterator i = control_points.begin(); i != control_points.end(); ++i) {
987 nframes_t when = (nframes_t) (*(*i)->model())->when;
989 if (when >= start && when <= end) {
991 if ((*i)->get_y() >= bot && (*i)->get_y() <= top) {
994 (*i)->set_visible(true);
996 nstart = min (nstart, when);
997 nend = max (nend, when);
1003 results.push_back (new AutomationSelectable (nstart, nend, botfrac, topfrac, trackview));
1005 nstart = max_frames;
1013 results.push_back (new AutomationSelectable (nstart, nend, botfrac, topfrac, trackview));
1019 AutomationLine::get_inverted_selectables (Selection&, list<Selectable*>& results)
1025 AutomationLine::set_selected_points (PointSelection& points)
1030 for (vector<ControlPoint*>::iterator i = control_points.begin(); i != control_points.end(); ++i) {
1031 (*i)->set_selected(false);
1034 if (points.empty()) {
1038 for (PointSelection::iterator r = points.begin(); r != points.end(); ++r) {
1040 if (&(*r).track != &trackview) {
1044 /* Curse X11 and its inverted coordinate system! */
1046 bot = (1.0 - (*r).high_fract) * _height;
1047 top = (1.0 - (*r).low_fract) * _height;
1049 for (vector<ControlPoint*>::iterator i = control_points.begin(); i != control_points.end(); ++i) {
1051 double rstart, rend;
1053 rstart = trackview.editor().frame_to_unit ((*r).start);
1054 rend = trackview.editor().frame_to_unit ((*r).end);
1056 if ((*i)->get_x() >= rstart && (*i)->get_x() <= rend) {
1058 if ((*i)->get_y() >= bot && (*i)->get_y() <= top) {
1060 (*i)->set_selected(true);
1068 for (vector<ControlPoint*>::iterator i = control_points.begin(); i != control_points.end(); ++i) {
1069 (*i)->show_color (false, !points_visible);
1074 void AutomationLine::set_colors() {
1075 set_line_color( ARDOUR_UI::config()->canvasvar_AutomationLine.get() );
1076 for (vector<ControlPoint*>::iterator i = control_points.begin(); i != control_points.end(); ++i) {
1077 (*i)->show_color (false, !points_visible);
1082 AutomationLine::show_selection ()
1084 TimeSelection& time (trackview.editor().get_selection().time);
1086 for (vector<ControlPoint*>::iterator i = control_points.begin(); i != control_points.end(); ++i) {
1088 (*i)->set_selected(false);
1090 for (list<AudioRange>::iterator r = time.begin(); r != time.end(); ++r) {
1091 double rstart, rend;
1093 rstart = trackview.editor().frame_to_unit ((*r).start);
1094 rend = trackview.editor().frame_to_unit ((*r).end);
1096 if ((*i)->get_x() >= rstart && (*i)->get_x() <= rend) {
1097 (*i)->set_selected(true);
1102 (*i)->show_color (false, !points_visible);
1107 AutomationLine::hide_selection ()
1109 // show_selection ();
1113 AutomationLine::list_changed ()
1119 AutomationLine::reset_callback (const Evoral::ControlList& events)
1121 ALPoints tmp_points;
1122 uint32_t npoints = events.size();
1125 for (vector<ControlPoint*>::iterator i = control_points.begin(); i != control_points.end(); ++i) {
1128 control_points.clear ();
1133 AutomationList::const_iterator ai;
1135 for (ai = events.begin(); ai != events.end(); ++ai) {
1137 double translated_y = (*ai)->value;
1138 model_to_view_y (translated_y);
1140 add_model_point (tmp_points, (*ai)->when, translated_y);
1143 determine_visible_control_points (tmp_points);
1148 AutomationLine::add_model_point (ALPoints& tmp_points, double frame, double yfract)
1150 tmp_points.push_back (ALPoint (trackview.editor().frame_to_unit (frame),
1151 _height - (yfract * _height)));
1155 AutomationLine::reset ()
1157 update_pending = false;
1163 alist->apply_to_points (*this, &AutomationLine::reset_callback);
1167 AutomationLine::clear ()
1169 /* parent must create command */
1170 XMLNode &before = get_state();
1172 trackview.editor().current_session()->add_command (new MementoCommand<AutomationLine>(*this, &before, &get_state()));
1173 trackview.editor().current_session()->commit_reversible_command ();
1174 trackview.editor().current_session()->set_dirty ();
1178 AutomationLine::change_model (AutomationList::iterator i, double x, double y)
1183 AutomationLine::set_list(boost::shared_ptr<ARDOUR::AutomationList> list)
1190 AutomationLine::show_all_control_points ()
1192 points_visible = true;
1194 for (vector<ControlPoint*>::iterator i = control_points.begin(); i != control_points.end(); ++i) {
1195 (*i)->show_color((_interpolation != AutomationList::Discrete), false);
1197 (*i)->set_visible (true);
1202 AutomationLine::hide_all_but_selected_control_points ()
1204 if (alist->interpolation() == AutomationList::Discrete)
1207 points_visible = false;
1209 for (vector<ControlPoint*>::iterator i = control_points.begin(); i != control_points.end(); ++i) {
1210 if (!(*i)->selected()) {
1211 (*i)->set_visible (false);
1217 AutomationLine::track_entered()
1219 if (alist->interpolation() != AutomationList::Discrete)
1220 show_all_control_points();
1224 AutomationLine::track_exited()
1226 if (alist->interpolation() != AutomationList::Discrete) {
1227 hide_all_but_selected_control_points();
1232 AutomationLine::get_state (void)
1234 /* function as a proxy for the model */
1235 return alist->get_state();
1239 AutomationLine::set_state (const XMLNode &node)
1241 /* function as a proxy for the model */
1242 return alist->set_state (node);
1246 AutomationLine::view_to_model_y (double& y) const
1248 /* TODO: This should be more generic ... */
1249 if (alist->parameter().type() == GainAutomation ||
1250 alist->parameter().type() == EnvelopeAutomation) {
1251 y = slider_position_to_gain (y);
1254 } else if (alist->parameter().type() == PanAutomation) {
1255 // vertical coordinate axis reversal
1257 } else if (alist->parameter().type() == PluginAutomation) {
1258 y = y * (double)(alist->get_max_y()- alist->get_min_y()) + alist->get_min_y();
1260 y = (int)(y * alist->parameter().max());
1265 AutomationLine::model_to_view_y (double& y) const
1267 /* TODO: This should be more generic ... */
1268 if (alist->parameter().type() == GainAutomation ||
1269 alist->parameter().type() == EnvelopeAutomation) {
1270 y = gain_to_slider_position (y);
1271 } else if (alist->parameter().type() == PanAutomation) {
1272 // vertical coordinate axis reversal
1274 } else if (alist->parameter().type() == PluginAutomation) {
1275 y = (y - alist->get_min_y()) / (double)(alist->get_max_y()- alist->get_min_y());
1277 y = y / (double)alist->parameter().max(); /* ... like this */
1283 AutomationLine::set_interpolation(AutomationList::InterpolationStyle style)
1285 _interpolation = style;
1287 if (style == AutomationList::Discrete) {
1288 show_all_control_points();
1291 hide_all_but_selected_control_points();