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 set_uses_gain_mapping (true);
91 set_interpolation(alist->interpolation());
94 AutomationLine::~AutomationLine ()
96 vector_delete (&control_points);
101 AutomationLine::event_handler (GdkEvent* event)
103 return PublicEditor::instance().canvas_line_event (event, line, this);
107 AutomationLine::queue_reset ()
109 if (!update_pending) {
110 update_pending = true;
111 Gtkmm2ext::UI::instance()->call_slot (mem_fun(*this, &AutomationLine::reset));
116 AutomationLine::show ()
118 if (_interpolation != AutomationList::Discrete)
121 if (points_visible) {
122 for (vector<ControlPoint*>::iterator i = control_points.begin(); i != control_points.end(); ++i) {
131 AutomationLine::hide ()
134 for (vector<ControlPoint*>::iterator i = control_points.begin(); i != control_points.end(); ++i) {
141 AutomationLine::control_point_box_size ()
143 if (_interpolation == AutomationList::Discrete) {
144 return max((_height*4.0) / (double)(alist->parameter().max() - alist->parameter().min()),
148 if (_height > TimeAxisView::hLarger) {
150 } else if (_height > (guint32) TimeAxisView::hNormal) {
157 AutomationLine::set_height (guint32 h)
162 double bsz = control_point_box_size();
164 for (vector<ControlPoint*>::iterator i = control_points.begin(); i != control_points.end(); ++i) {
165 (*i)->set_size (bsz);
173 AutomationLine::set_line_color (uint32_t color)
176 line->property_fill_color_rgba() = color;
180 AutomationLine::set_uses_gain_mapping (bool yn)
182 if (yn != _uses_gain_mapping) {
183 _uses_gain_mapping = yn;
189 AutomationLine::nth (uint32_t n)
191 if (n < control_points.size()) {
192 return control_points[n];
199 AutomationLine::modify_point_y (ControlPoint& cp, double y)
201 /* clamp y-coord appropriately. y is supposed to be a normalized fraction (0.0-1.0),
202 and needs to be converted to a canvas unit distance.
207 y = _height - (y * _height);
209 double const x = trackview.editor.frame_to_unit ((*cp.model())->when);
211 trackview.editor.current_session()->begin_reversible_command (_("automation event move"));
212 trackview.editor.current_session()->add_command (new MementoCommand<AutomationList>(*alist.get(), &get_state(), 0));
214 cp.move_to (x, y, ControlPoint::Full);
215 reset_line_coords (cp);
217 if (line_points.size() > 1) {
218 line->property_points() = line_points;
222 sync_model_with_view_point (cp, false, 0);
225 update_pending = false;
227 trackview.editor.current_session()->add_command (new MementoCommand<AutomationList>(*alist.get(), 0, &alist->get_state()));
228 trackview.editor.current_session()->commit_reversible_command ();
229 trackview.editor.current_session()->set_dirty ();
234 AutomationLine::modify_view_point (ControlPoint& cp, double x, double y, bool with_push)
237 uint32_t last_movable = UINT_MAX;
238 double x_limit = DBL_MAX;
240 /* this just changes the current view. it does not alter
241 the model in any way at all.
244 /* clamp y-coord appropriately. y is supposed to be a normalized fraction (0.0-1.0),
245 and needs to be converted to a canvas unit distance.
250 y = _height - (y * _height);
252 if (cp.can_slide()) {
254 /* x-coord cannot move beyond adjacent points or the start/end, and is
255 already in frames. it needs to be converted to canvas units.
258 x = trackview.editor.frame_to_unit (x);
260 /* clamp x position using view coordinates */
262 ControlPoint *before;
265 if (cp.view_index()) {
266 before = nth (cp.view_index() - 1);
267 x = max (x, before->get_x()+1.0);
274 if (cp.view_index() < control_points.size() - 1) {
276 after = nth (cp.view_index() + 1);
278 /*if it is a "spike" leave the x alone */
280 if (after->get_x() - before->get_x() < 2) {
284 x = min (x, after->get_x()-1.0);
294 /* find the first point that can't move */
296 for (uint32_t n = cp.view_index() + 1; (after = nth (n)) != 0; ++n) {
297 if (!after->can_slide()) {
298 x_limit = after->get_x() - 1.0;
299 last_movable = after->view_index();
304 delta = x - cp.get_x();
309 /* leave the x-coordinate alone */
311 x = trackview.editor.frame_to_unit ((*cp.model())->when);
317 cp.move_to (x, y, ControlPoint::Full);
318 reset_line_coords (cp);
322 uint32_t limit = min (control_points.size(), (size_t)last_movable);
324 /* move the current point to wherever the user told it to go, subject
328 cp.move_to (min (x, x_limit), y, ControlPoint::Full);
329 reset_line_coords (cp);
331 /* now move all subsequent control points, to reflect the motion.
334 for (uint32_t i = cp.view_index() + 1; i < limit; ++i) {
335 ControlPoint *p = nth (i);
338 if (p->can_slide()) {
339 new_x = min (p->get_x() + delta, x_limit);
340 p->move_to (new_x, p->get_y(), ControlPoint::Full);
341 reset_line_coords (*p);
348 AutomationLine::reset_line_coords (ControlPoint& cp)
350 if (cp.view_index() < line_points.size()) {
351 line_points[cp.view_index()].set_x (cp.get_x());
352 line_points[cp.view_index()].set_y (cp.get_y());
357 AutomationLine::sync_model_with_view_line (uint32_t start, uint32_t end)
362 update_pending = true;
364 for (uint32_t i = start; i <= end; ++i) {
366 sync_model_with_view_point (*p, false, 0);
371 AutomationLine::model_representation (ControlPoint& cp, ModelRepresentation& mr)
373 /* part one: find out where the visual control point is.
374 initial results are in canvas units. ask the
375 line to convert them to something relevant.
378 mr.xval = (nframes_t) floor (cp.get_x());
379 mr.yval = 1.0 - (cp.get_y() / _height);
381 /* if xval has not changed, set it directly from the model to avoid rounding errors */
383 if (mr.xval == trackview.editor.frame_to_unit((*cp.model())->when)) {
384 mr.xval = (nframes_t) (*cp.model())->when;
386 mr.xval = trackview.editor.unit_to_frame (mr.xval);
389 /* virtual call: this will do the right thing
390 for whatever particular type of line we are.
393 view_to_model_y (mr.yval);
395 /* part 2: find out where the model point is now
398 mr.xpos = (nframes_t) (*cp.model())->when;
399 mr.ypos = (*cp.model())->value;
401 /* part 3: get the position of the visual control
402 points before and after us.
405 ControlPoint* before;
408 if (cp.view_index()) {
409 before = nth (cp.view_index() - 1);
414 after = nth (cp.view_index() + 1);
417 mr.xmin = (nframes_t) (*before->model())->when;
418 mr.ymin = (*before->model())->value;
419 mr.start = before->model();
424 mr.start = cp.model();
428 mr.end = after->model();
438 AutomationLine::determine_visible_control_points (ALPoints& points)
440 uint32_t view_index, pi, n;
441 AutomationList::iterator model;
443 double last_control_point_x = 0.0;
444 double last_control_point_y = 0.0;
445 uint32_t this_rx = 0;
446 uint32_t prev_rx = 0;
447 uint32_t this_ry = 0;
448 uint32_t prev_ry = 0;
453 /* hide all existing points, and the line */
457 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 if (view_index >= cpsize) {
542 /* make sure we have enough control points */
544 ControlPoint* ncp = new ControlPoint (*this);
546 ncp->set_size (box_size);
548 control_points.push_back (ncp);
552 ControlPoint::ShapeType shape;
554 if (!terminal_points_can_slide) {
556 control_points[view_index]->set_can_slide(false);
558 shape = ControlPoint::Start;
560 shape = ControlPoint::Full;
562 } else if (pi == npoints - 1) {
563 control_points[view_index]->set_can_slide(false);
564 shape = ControlPoint::End;
566 control_points[view_index]->set_can_slide(true);
567 shape = ControlPoint::Full;
570 control_points[view_index]->set_can_slide(true);
571 shape = ControlPoint::Full;
574 last_control_point_x = tx;
575 last_control_point_y = ty;
577 control_points[view_index]->reset (tx, ty, model, view_index, shape);
582 /* finally, control visibility */
584 if (_visible && points_visible) {
585 control_points[view_index]->show ();
586 control_points[view_index]->set_visible (true);
588 if (!points_visible) {
589 control_points[view_index]->set_visible (false);
596 /* discard extra CP's to avoid confusing ourselves */
598 while (control_points.size() > view_index) {
599 ControlPoint* cp = control_points.back();
600 control_points.pop_back ();
604 if (!terminal_points_can_slide) {
605 control_points.back()->set_can_slide(false);
610 if (view_index > 1) {
612 npoints = view_index;
614 /* reset the line coordinates */
616 while (line_points.size() < npoints) {
617 line_points.push_back (Art::Point (0,0));
620 while (line_points.size() > npoints) {
621 line_points.pop_back ();
624 for (view_index = 0; view_index < npoints; ++view_index) {
625 line_points[view_index].set_x (control_points[view_index]->get_x());
626 line_points[view_index].set_y (control_points[view_index]->get_y());
629 line->property_points() = line_points;
631 if (_visible && _interpolation != AutomationList::Discrete)
636 set_selected_points (trackview.editor.get_selection().points);
641 AutomationLine::get_verbose_cursor_string (double fraction) const
643 std::string s = fraction_to_string (fraction);
644 if (_uses_gain_mapping) {
652 * @param fraction y fraction
653 * @return string representation of this value, using dB if appropriate.
657 AutomationLine::fraction_to_string (double fraction) const
661 if (_uses_gain_mapping) {
662 if (fraction == 0.0) {
663 snprintf (buf, sizeof (buf), "-inf");
665 snprintf (buf, sizeof (buf), "%.1f", coefficient_to_dB (slider_position_to_gain (fraction)));
668 view_to_model_y (fraction);
669 if (EventTypeMap::instance().is_integer (alist->parameter())) {
670 snprintf (buf, sizeof (buf), "%d", (int)fraction);
672 snprintf (buf, sizeof (buf), "%.2f", fraction);
681 * @param s Value string in the form as returned by fraction_to_string.
682 * @return Corresponding y fraction.
686 AutomationLine::string_to_fraction (string const & s) const
693 sscanf (s.c_str(), "%lf", &v);
695 if (_uses_gain_mapping) {
696 v = gain_to_slider_position (dB_to_coefficient (v));
705 AutomationLine::invalid_point (ALPoints& p, uint32_t index)
707 return p[index].x == max_frames && p[index].y == DBL_MAX;
711 AutomationLine::invalidate_point (ALPoints& p, uint32_t index)
713 p[index].x = max_frames;
714 p[index].y = DBL_MAX;
718 AutomationLine::start_drag (ControlPoint* cp, nframes_t x, float fraction)
720 if (trackview.editor.current_session() == 0) { /* how? */
727 str = _("automation event move");
729 str = _("automation range drag");
732 trackview.editor.current_session()->begin_reversible_command (str);
733 trackview.editor.current_session()->add_command (new MementoCommand<AutomationList>(*alist.get(), &get_state(), 0));
737 first_drag_fraction = fraction;
738 last_drag_fraction = fraction;
744 AutomationLine::point_drag (ControlPoint& cp, nframes_t x, float fraction, bool with_push)
747 drag_distance += (x - drag_x);
749 drag_distance -= (drag_x - x);
754 modify_view_point (cp, x, fraction, with_push);
756 if (line_points.size() > 1) {
757 line->property_points() = line_points;
761 did_push = with_push;
765 AutomationLine::line_drag (uint32_t i1, uint32_t i2, float fraction, bool with_push)
767 double ydelta = fraction - last_drag_fraction;
769 did_push = with_push;
771 last_drag_fraction = fraction;
778 for (uint32_t i = i1 ; i <= i2; i++) {
780 modify_view_point (*cp, trackview.editor.unit_to_frame (cp->get_x()), ((_height - cp->get_y()) /_height) + ydelta, with_push);
783 if (line_points.size() > 1) {
784 line->property_points() = line_points;
791 AutomationLine::end_drag (ControlPoint* cp)
800 sync_model_with_view_point (*cp, did_push, drag_distance);
802 sync_model_with_view_line (line_drag_cp1, line_drag_cp2);
807 update_pending = false;
809 trackview.editor.current_session()->add_command (new MementoCommand<AutomationList>(*alist.get(), 0, &alist->get_state()));
810 trackview.editor.current_session()->commit_reversible_command ();
811 trackview.editor.current_session()->set_dirty ();
816 AutomationLine::sync_model_with_view_point (ControlPoint& cp, bool did_push, int64_t distance)
818 ModelRepresentation mr;
821 model_representation (cp, mr);
823 /* how much are we changing the central point by */
825 ydelta = mr.yval - mr.ypos;
828 apply the full change to the central point, and interpolate
829 on both axes to cover all model points represented
830 by the control point.
833 /* change all points before the primary point */
835 for (AutomationList::iterator i = mr.start; i != cp.model(); ++i) {
837 double fract = ((*i)->when - mr.xmin) / (mr.xpos - mr.xmin);
838 double y_delta = ydelta * fract;
839 double x_delta = distance * fract;
843 if (y_delta || x_delta) {
844 alist->modify (i, (*i)->when + x_delta, mr.ymin + y_delta);
848 /* change the primary point */
850 update_pending = true;
851 alist->modify (cp.model(), mr.xval, mr.yval);
854 /* change later points */
856 AutomationList::iterator i = cp.model();
860 while (i != mr.end) {
862 double delta = ydelta * (mr.xmax - (*i)->when) / (mr.xmax - mr.xpos);
864 /* all later points move by the same distance along the x-axis as the main point */
867 alist->modify (i, (*i)->when + distance, (*i)->value + delta);
875 /* move all points after the range represented by the view by the same distance
876 as the main point moved.
879 alist->slide (mr.end, drag_distance);
885 AutomationLine::control_points_adjacent (double xval, uint32_t & before, uint32_t& after)
887 ControlPoint *bcp = 0;
888 ControlPoint *acp = 0;
891 /* xval is in frames */
893 unit_xval = trackview.editor.frame_to_unit (xval);
895 for (vector<ControlPoint*>::iterator i = control_points.begin(); i != control_points.end(); ++i) {
897 if ((*i)->get_x() <= unit_xval) {
899 if (!bcp || (*i)->get_x() > bcp->get_x()) {
901 before = bcp->view_index();
904 } else if ((*i)->get_x() > unit_xval) {
906 after = acp->view_index();
915 AutomationLine::is_last_point (ControlPoint& cp)
917 ModelRepresentation mr;
919 model_representation (cp, mr);
921 // If the list is not empty, and the point is the last point in the list
923 if (!alist->empty() && mr.end == alist->end()) {
931 AutomationLine::is_first_point (ControlPoint& cp)
933 ModelRepresentation mr;
935 model_representation (cp, mr);
937 // If the list is not empty, and the point is the first point in the list
939 if (!alist->empty() && mr.start == alist->begin()) {
946 // This is copied into AudioRegionGainLine
948 AutomationLine::remove_point (ControlPoint& cp)
950 ModelRepresentation mr;
952 model_representation (cp, mr);
954 trackview.editor.current_session()->begin_reversible_command (_("remove control point"));
955 XMLNode &before = alist->get_state();
957 alist->erase (mr.start, mr.end);
959 trackview.editor.current_session()->add_command(new MementoCommand<AutomationList>(
960 *alist.get(), &before, &alist->get_state()));
961 trackview.editor.current_session()->commit_reversible_command ();
962 trackview.editor.current_session()->set_dirty ();
966 AutomationLine::get_selectables (nframes_t& start, nframes_t& end,
967 double botfrac, double topfrac, list<Selectable*>& results)
974 bool collecting = false;
976 /* Curse X11 and its inverted coordinate system! */
978 bot = (1.0 - topfrac) * _height;
979 top = (1.0 - botfrac) * _height;
984 for (vector<ControlPoint*>::iterator i = control_points.begin(); i != control_points.end(); ++i) {
986 nframes_t when = (nframes_t) (*(*i)->model())->when;
988 if (when >= start && when <= end) {
990 if ((*i)->get_y() >= bot && (*i)->get_y() <= top) {
993 (*i)->set_visible(true);
995 nstart = min (nstart, when);
996 nend = max (nend, when);
1002 results.push_back (new AutomationSelectable (nstart, nend, botfrac, topfrac, trackview));
1004 nstart = max_frames;
1012 results.push_back (new AutomationSelectable (nstart, nend, botfrac, topfrac, trackview));
1018 AutomationLine::get_inverted_selectables (Selection&, list<Selectable*>& results)
1024 AutomationLine::set_selected_points (PointSelection& points)
1029 for (vector<ControlPoint*>::iterator i = control_points.begin(); i != control_points.end(); ++i) {
1030 (*i)->set_selected(false);
1033 if (points.empty()) {
1037 for (PointSelection::iterator r = points.begin(); r != points.end(); ++r) {
1039 if (&(*r).track != &trackview) {
1043 /* Curse X11 and its inverted coordinate system! */
1045 bot = (1.0 - (*r).high_fract) * _height;
1046 top = (1.0 - (*r).low_fract) * _height;
1048 for (vector<ControlPoint*>::iterator i = control_points.begin(); i != control_points.end(); ++i) {
1050 double rstart, rend;
1052 rstart = trackview.editor.frame_to_unit ((*r).start);
1053 rend = trackview.editor.frame_to_unit ((*r).end);
1055 if ((*i)->get_x() >= rstart && (*i)->get_x() <= rend) {
1057 if ((*i)->get_y() >= bot && (*i)->get_y() <= top) {
1059 (*i)->set_selected(true);
1067 for (vector<ControlPoint*>::iterator i = control_points.begin(); i != control_points.end(); ++i) {
1068 (*i)->show_color (false, !points_visible);
1073 void AutomationLine::set_colors() {
1074 set_line_color( ARDOUR_UI::config()->canvasvar_AutomationLine.get() );
1075 for (vector<ControlPoint*>::iterator i = control_points.begin(); i != control_points.end(); ++i) {
1076 (*i)->show_color (false, !points_visible);
1081 AutomationLine::show_selection ()
1083 TimeSelection& time (trackview.editor.get_selection().time);
1085 for (vector<ControlPoint*>::iterator i = control_points.begin(); i != control_points.end(); ++i) {
1087 (*i)->set_selected(false);
1089 for (list<AudioRange>::iterator r = time.begin(); r != time.end(); ++r) {
1090 double rstart, rend;
1092 rstart = trackview.editor.frame_to_unit ((*r).start);
1093 rend = trackview.editor.frame_to_unit ((*r).end);
1095 if ((*i)->get_x() >= rstart && (*i)->get_x() <= rend) {
1096 (*i)->set_selected(true);
1101 (*i)->show_color (false, !points_visible);
1106 AutomationLine::hide_selection ()
1108 // show_selection ();
1112 AutomationLine::list_changed ()
1118 AutomationLine::reset_callback (const Evoral::ControlList& events)
1120 ALPoints tmp_points;
1121 uint32_t npoints = events.size();
1124 for (vector<ControlPoint*>::iterator i = control_points.begin(); i != control_points.end(); ++i) {
1127 control_points.clear ();
1132 AutomationList::const_iterator ai;
1134 for (ai = events.begin(); ai != events.end(); ++ai) {
1136 double translated_y = (*ai)->value;
1137 model_to_view_y (translated_y);
1139 add_model_point (tmp_points, (*ai)->when, translated_y);
1142 determine_visible_control_points (tmp_points);
1147 AutomationLine::add_model_point (ALPoints& tmp_points, double frame, double yfract)
1149 tmp_points.push_back (ALPoint (trackview.editor.frame_to_unit (frame),
1150 _height - (yfract * _height)));
1154 AutomationLine::reset ()
1156 update_pending = false;
1162 alist->apply_to_points (*this, &AutomationLine::reset_callback);
1166 AutomationLine::clear ()
1168 /* parent must create command */
1169 XMLNode &before = get_state();
1171 trackview.editor.current_session()->add_command (new MementoCommand<AutomationLine>(*this, &before, &get_state()));
1172 trackview.editor.current_session()->commit_reversible_command ();
1173 trackview.editor.current_session()->set_dirty ();
1177 AutomationLine::change_model (AutomationList::iterator i, double x, double y)
1182 AutomationLine::set_list(boost::shared_ptr<ARDOUR::AutomationList> list)
1189 AutomationLine::show_all_control_points ()
1191 points_visible = true;
1193 for (vector<ControlPoint*>::iterator i = control_points.begin(); i != control_points.end(); ++i) {
1194 (*i)->show_color((_interpolation != AutomationList::Discrete), false);
1196 (*i)->set_visible (true);
1201 AutomationLine::hide_all_but_selected_control_points ()
1203 if (alist->interpolation() == AutomationList::Discrete)
1206 points_visible = false;
1208 for (vector<ControlPoint*>::iterator i = control_points.begin(); i != control_points.end(); ++i) {
1209 if (!(*i)->selected()) {
1210 (*i)->set_visible (false);
1216 AutomationLine::track_entered()
1218 if (alist->interpolation() != AutomationList::Discrete)
1219 show_all_control_points();
1223 AutomationLine::track_exited()
1225 if (alist->interpolation() != AutomationList::Discrete) {
1226 hide_all_but_selected_control_points();
1231 AutomationLine::get_state (void)
1233 /* function as a proxy for the model */
1234 return alist->get_state();
1238 AutomationLine::set_state (const XMLNode &node)
1240 /* function as a proxy for the model */
1241 return alist->set_state (node);
1245 AutomationLine::view_to_model_y (double& y) const
1247 /* TODO: This should be more generic ... */
1248 if (alist->parameter().type() == GainAutomation) {
1249 y = slider_position_to_gain (y);
1252 } else if (alist->parameter().type() == PanAutomation) {
1253 // vertical coordinate axis reversal
1255 } else if (alist->parameter().type() == PluginAutomation) {
1256 y = y * (double)(alist->get_max_y()- alist->get_min_y()) + alist->get_min_y();
1258 y = (int)(y * alist->parameter().max());
1263 AutomationLine::model_to_view_y (double& y) const
1265 /* TODO: This should be more generic ... */
1266 if (alist->parameter().type() == GainAutomation) {
1267 y = gain_to_slider_position (y);
1268 } else if (alist->parameter().type() == PanAutomation) {
1269 // vertical coordinate axis reversal
1271 } else if (alist->parameter().type() == PluginAutomation) {
1272 y = (y - alist->get_min_y()) / (double)(alist->get_max_y()- alist->get_min_y());
1274 y = y / (double)alist->parameter().max(); /* ... like this */
1280 AutomationLine::set_interpolation(AutomationList::InterpolationStyle style)
1282 _interpolation = style;
1284 if (style == AutomationList::Discrete) {
1285 show_all_control_points();
1288 hide_all_but_selected_control_points();