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_event.h>
30 #include <ardour/curve.h>
31 #include <ardour/dB.h>
33 #include "simplerect.h"
34 #include "automation_line.h"
35 #include "rgb_macros.h"
36 #include "ardour_ui.h"
37 #include "public_editor.h"
39 #include "selection.h"
40 #include "time_axis_view.h"
41 #include "point_selection.h"
42 #include "automation_selectable.h"
43 #include "automation_time_axis.h"
44 #include "public_editor.h"
46 #include <ardour/session.h>
52 using namespace ARDOUR;
54 using namespace Editing;
55 using namespace Gnome; // for Canvas
57 ControlPoint::ControlPoint (AutomationLine& al)
60 model = al.the_list()->end();
69 item = new Canvas::SimpleRect (line.canvas_group());
70 item->property_draw() = true;
71 item->property_fill() = false;
72 item->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_ControlPointFill.get();
73 item->property_outline_color_rgba() = ARDOUR_UI::config()->canvasvar_ControlPointOutline.get();
74 item->property_outline_pixels() = 1;
75 item->set_data ("control_point", this);
76 item->signal_event().connect (mem_fun (this, &ControlPoint::event_handler));
82 ControlPoint::ControlPoint (const ControlPoint& other, bool dummy_arg_to_force_special_copy_constructor)
90 view_index = other.view_index;
91 can_slide = other.can_slide;
94 _shape = other._shape;
98 item = new Canvas::SimpleRect (line.canvas_group());
99 item->property_fill() = false;
100 item->property_outline_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredControlPointOutline.get();
101 item->property_outline_pixels() = 1;
103 /* NOTE: no event handling in copied ControlPoints */
109 ControlPoint::~ControlPoint ()
115 ControlPoint::event_handler (GdkEvent* event)
117 return PublicEditor::instance().canvas_control_point_event (event, item, this);
121 ControlPoint::hide ()
133 ControlPoint::set_visible (bool yn)
135 item->property_draw() = (gboolean) yn;
139 ControlPoint::reset (double x, double y, AutomationList::iterator mi, uint32_t vi, ShapeType shape)
143 move_to (x, y, shape);
147 ControlPoint::show_color (bool entered, bool hide_too)
151 item->property_outline_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredControlPointSelected.get();
154 item->property_outline_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredControlPoint.get();
162 item->property_outline_color_rgba() = ARDOUR_UI::config()->canvasvar_ControlPointSelected.get();
165 item->property_outline_color_rgba() = ARDOUR_UI::config()->canvasvar_ControlPoint.get();
174 ControlPoint::set_size (double sz)
180 item->property_fill() = (gboolean) TRUE;
182 item->property_fill() = (gboolean) FALSE;
186 move_to (_x, _y, _shape);
190 ControlPoint::move_to (double x, double y, ShapeType shape)
194 double half_size = rint(_size/2.0);
211 item->property_x1() = x1;
212 item->property_x2() = x2;
213 item->property_y1() = y - half_size;
214 item->property_y2() = y + half_size;
223 AutomationLine::AutomationLine (const string & name, TimeAxisView& tv, ArdourCanvas::Group& parent, boost::shared_ptr<AutomationList> al)
227 _parent_group (parent)
229 points_visible = false;
230 update_pending = false;
231 _vc_uses_gain_mapping = false;
234 terminal_points_can_slide = true;
238 group = new ArdourCanvas::Group (parent);
239 group->property_x() = 0.0;
240 group->property_y() = 0.0;
242 line = new ArdourCanvas::Line (*group);
243 line->property_width_pixels() = (guint)1;
244 line->set_data ("line", this);
246 line->signal_event().connect (mem_fun (*this, &AutomationLine::event_handler));
248 alist->StateChanged.connect (mem_fun(*this, &AutomationLine::list_changed));
250 trackview.session().register_with_memento_command_factory(alist->id(), this);
252 if (alist->parameter().type() == GainAutomation)
253 set_verbose_cursor_uses_gain_mapping (true);
256 AutomationLine::~AutomationLine ()
258 vector_delete (&control_points);
263 AutomationLine::event_handler (GdkEvent* event)
265 return PublicEditor::instance().canvas_line_event (event, line, this);
269 AutomationLine::queue_reset ()
271 if (!update_pending) {
272 update_pending = true;
273 Gtkmm2ext::UI::instance()->call_slot (mem_fun(*this, &AutomationLine::reset));
278 AutomationLine::show ()
282 if (points_visible) {
283 for (vector<ControlPoint*>::iterator i = control_points.begin(); i != control_points.end(); ++i) {
292 AutomationLine::hide ()
295 for (vector<ControlPoint*>::iterator i = control_points.begin(); i != control_points.end(); ++i) {
302 AutomationLine::control_point_box_size ()
304 if (_height > TimeAxisView::hLarger) {
306 } else if (_height > (guint32) TimeAxisView::hNormal) {
313 AutomationLine::set_y_position_and_height (guint32 y, guint32 h)
315 bool changed = false;
317 if (y != _y_position) {
325 double const bsz = control_point_box_size();
327 for (vector<ControlPoint*>::iterator i = control_points.begin(); i != control_points.end(); ++i) {
328 (*i)->set_size (bsz);
339 AutomationLine::set_line_color (uint32_t color)
342 line->property_fill_color_rgba() = color;
346 AutomationLine::set_verbose_cursor_uses_gain_mapping (bool yn)
348 if (yn != _vc_uses_gain_mapping) {
349 _vc_uses_gain_mapping = yn;
355 AutomationLine::nth (uint32_t n)
357 if (n < control_points.size()) {
358 return control_points[n];
365 AutomationLine::modify_view_point (ControlPoint& cp, double x, double y, bool with_push)
368 uint32_t last_movable = UINT_MAX;
369 double x_limit = DBL_MAX;
371 /* this just changes the current view. it does not alter
372 the model in any way at all.
375 /* clamp y-coord appropriately. y is supposed to be a normalized fraction (0.0-1.0),
376 and needs to be converted to a canvas unit distance.
381 y = _y_position + _height - (y * _height);
385 /* x-coord cannot move beyond adjacent points or the start/end, and is
386 already in frames. it needs to be converted to canvas units.
389 x = trackview.editor.frame_to_unit (x);
391 /* clamp x position using view coordinates */
393 ControlPoint *before;
397 before = nth (cp.view_index - 1);
398 x = max (x, before->get_x()+1.0);
405 if (cp.view_index < control_points.size() - 1) {
407 after = nth (cp.view_index + 1);
409 /*if it is a "spike" leave the x alone */
411 if (after->get_x() - before->get_x() < 2) {
415 x = min (x, after->get_x()-1.0);
425 /* find the first point that can't move */
427 for (uint32_t n = cp.view_index + 1; (after = nth (n)) != 0; ++n) {
428 if (!after->can_slide) {
429 x_limit = after->get_x() - 1.0;
430 last_movable = after->view_index;
435 delta = x - cp.get_x();
440 /* leave the x-coordinate alone */
442 x = trackview.editor.frame_to_unit ((*cp.model)->when);
448 cp.move_to (x, y, ControlPoint::Full);
449 reset_line_coords (cp);
453 uint32_t limit = min (control_points.size(), (size_t)last_movable);
455 /* move the current point to wherever the user told it to go, subject
459 cp.move_to (min (x, x_limit), y, ControlPoint::Full);
460 reset_line_coords (cp);
462 /* now move all subsequent control points, to reflect the motion.
465 for (uint32_t i = cp.view_index + 1; i < limit; ++i) {
466 ControlPoint *p = nth (i);
470 new_x = min (p->get_x() + delta, x_limit);
471 p->move_to (new_x, p->get_y(), ControlPoint::Full);
472 reset_line_coords (*p);
479 AutomationLine::reset_line_coords (ControlPoint& cp)
481 if (cp.view_index < line_points.size()) {
482 line_points[cp.view_index].set_x (cp.get_x());
483 line_points[cp.view_index].set_y (cp.get_y());
488 AutomationLine::sync_model_with_view_line (uint32_t start, uint32_t end)
493 update_pending = true;
495 for (uint32_t i = start; i <= end; ++i) {
497 sync_model_with_view_point (*p, false, 0);
502 AutomationLine::model_representation (ControlPoint& cp, ModelRepresentation& mr)
504 /* part one: find out where the visual control point is.
505 initial results are in canvas units. ask the
506 line to convert them to something relevant.
509 mr.xval = (nframes_t) floor (cp.get_x());
510 mr.yval = 1.0 - ( (cp.get_y() - _y_position) / _height);
512 /* if xval has not changed, set it directly from the model to avoid rounding errors */
514 if (mr.xval == trackview.editor.frame_to_unit((*cp.model)->when)) {
515 mr.xval = (nframes_t) (*cp.model)->when;
517 mr.xval = trackview.editor.unit_to_frame (mr.xval);
520 /* virtual call: this will do the right thing
521 for whatever particular type of line we are.
524 view_to_model_y (mr.yval);
526 /* part 2: find out where the model point is now
529 mr.xpos = (nframes_t) (*cp.model)->when;
530 mr.ypos = (*cp.model)->value;
532 /* part 3: get the position of the visual control
533 points before and after us.
536 ControlPoint* before;
540 before = nth (cp.view_index - 1);
545 after = nth (cp.view_index + 1);
548 mr.xmin = (nframes_t) (*before->model)->when;
549 mr.ymin = (*before->model)->value;
550 mr.start = before->model;
559 mr.end = after->model;
569 AutomationLine::determine_visible_control_points (ALPoints& points)
571 uint32_t view_index, pi, n;
572 AutomationList::iterator model;
574 double last_control_point_x = 0.0;
575 double last_control_point_y = 0.0;
576 uint32_t this_rx = 0;
577 uint32_t prev_rx = 0;
578 uint32_t this_ry = 0;
579 uint32_t prev_ry = 0;
584 /* hide all existing points, and the line */
588 for (vector<ControlPoint*>::iterator i = control_points.begin(); i != control_points.end(); ++i) {
595 if (points.empty()) {
599 npoints = points.size();
601 /* compute derivative/slope for the entire line */
603 slope = new double[npoints];
605 for (n = 0; n < npoints - 1; ++n) {
606 double xdelta = points[n+1].x - points[n].x;
607 double ydelta = points[n+1].y - points[n].y;
608 slope[n] = ydelta/xdelta;
611 box_size = (uint32_t) control_point_box_size ();
613 /* read all points and decide which ones to show as control points */
617 for (model = alist->begin(), pi = 0; pi < npoints; ++model, ++pi) {
619 double tx = points[pi].x;
620 double ty = points[pi].y;
622 /* now ensure that the control_points vector reflects the current curve
623 state, but don't plot control points too close together. also, don't
624 plot a series of points all with the same value.
626 always plot the first and last points, of course.
629 if (invalid_point (points, pi)) {
630 /* for some reason, we are supposed to ignore this point,
631 but still keep track of the model index.
636 if (pi > 0 && pi < npoints - 1) {
637 if (slope[pi] == slope[pi-1]) {
639 /* no reason to display this point */
645 /* need to round here. the ultimate coordinates are integer
646 pixels, so tiny deltas in the coords will be eliminated
647 and we end up with "colinear" line segments. since the
648 line rendering code in libart doesn't like this very
649 much, we eliminate them here. don't do this for the first and last
653 this_rx = (uint32_t) rint (tx);
654 this_ry = (uint32_t) rint (ty);
656 if (view_index && pi != npoints && /* not the first, not the last */
657 (((this_rx == prev_rx) && (this_ry == prev_ry)) || /* same point */
658 (((this_rx - prev_rx) < (box_size + 2)) && /* not identical, but still too close horizontally */
659 (abs ((int)(this_ry - prev_ry)) < (int) (box_size + 2))))) { /* too close vertically */
663 /* ok, we should display this point */
665 if (view_index >= cpsize) {
667 /* make sure we have enough control points */
669 ControlPoint* ncp = new ControlPoint (*this);
671 ncp->set_size (box_size);
673 control_points.push_back (ncp);
677 ControlPoint::ShapeType shape;
679 if (!terminal_points_can_slide) {
681 control_points[view_index]->can_slide = false;
683 shape = ControlPoint::Start;
685 shape = ControlPoint::Full;
687 } else if (pi == npoints - 1) {
688 control_points[view_index]->can_slide = false;
689 shape = ControlPoint::End;
691 control_points[view_index]->can_slide = true;
692 shape = ControlPoint::Full;
695 control_points[view_index]->can_slide = true;
696 shape = ControlPoint::Full;
699 last_control_point_x = tx;
700 last_control_point_y = ty;
702 control_points[view_index]->reset (tx, ty, model, view_index, shape);
707 /* finally, control visibility */
709 if (_visible && points_visible) {
710 control_points[view_index]->show ();
711 control_points[view_index]->set_visible (true);
713 if (!points_visible) {
714 control_points[view_index]->set_visible (false);
721 /* discard extra CP's to avoid confusing ourselves */
723 while (control_points.size() > view_index) {
724 ControlPoint* cp = control_points.back();
725 control_points.pop_back ();
729 if (!terminal_points_can_slide) {
730 control_points.back()->can_slide = false;
735 if (view_index > 1) {
737 npoints = view_index;
739 /* reset the line coordinates */
741 while (line_points.size() < npoints) {
742 line_points.push_back (Art::Point (0,0));
745 while (line_points.size() > npoints) {
746 line_points.pop_back ();
749 for (view_index = 0; view_index < npoints; ++view_index) {
750 line_points[view_index].set_x (control_points[view_index]->get_x());
751 line_points[view_index].set_y (control_points[view_index]->get_y());
754 line->property_points() = line_points;
762 set_selected_points (trackview.editor.get_selection().points);
767 AutomationLine::get_verbose_cursor_string (double fraction)
771 if (_vc_uses_gain_mapping) {
772 if (fraction == 0.0) {
773 snprintf (buf, sizeof (buf), "-inf dB");
775 snprintf (buf, sizeof (buf), "%.1fdB", coefficient_to_dB (slider_position_to_gain (fraction)));
778 view_to_model_y(fraction);
779 if (alist->parameter().type() == MidiCCAutomation)
780 snprintf (buf, sizeof (buf), "%d", (int)fraction);
782 snprintf (buf, sizeof (buf), "%.2f", fraction);
789 AutomationLine::invalid_point (ALPoints& p, uint32_t index)
791 return p[index].x == max_frames && p[index].y == DBL_MAX;
795 AutomationLine::invalidate_point (ALPoints& p, uint32_t index)
797 p[index].x = max_frames;
798 p[index].y = DBL_MAX;
802 AutomationLine::start_drag (ControlPoint* cp, nframes_t x, float fraction)
804 if (trackview.editor.current_session() == 0) { /* how? */
811 str = _("automation event move");
813 str = _("automation range drag");
816 trackview.editor.current_session()->begin_reversible_command (str);
817 trackview.editor.current_session()->add_command (new MementoCommand<AutomationList>(*alist.get(), &get_state(), 0));
821 first_drag_fraction = fraction;
822 last_drag_fraction = fraction;
828 AutomationLine::point_drag (ControlPoint& cp, nframes_t x, float fraction, bool with_push)
831 drag_distance += (x - drag_x);
833 drag_distance -= (drag_x - x);
838 modify_view_point (cp, x, fraction, with_push);
840 if (line_points.size() > 1) {
841 line->property_points() = line_points;
845 did_push = with_push;
849 AutomationLine::line_drag (uint32_t i1, uint32_t i2, float fraction, bool with_push)
851 double const ydelta = fraction - last_drag_fraction;
853 did_push = with_push;
855 last_drag_fraction = fraction;
862 for (uint32_t i = i1 ; i <= i2; i++) {
864 modify_view_point (*cp, trackview.editor.unit_to_frame (cp->get_x()), ((_height - cp->get_y() + _y_position) /_height) + ydelta, with_push);
867 if (line_points.size() > 1) {
868 line->property_points() = line_points;
875 AutomationLine::end_drag (ControlPoint* cp)
884 sync_model_with_view_point (*cp, did_push, drag_distance);
886 sync_model_with_view_line (line_drag_cp1, line_drag_cp2);
891 update_pending = false;
893 trackview.editor.current_session()->add_command (new MementoCommand<AutomationList>(*alist.get(), 0, &alist->get_state()));
894 trackview.editor.current_session()->commit_reversible_command ();
895 trackview.editor.current_session()->set_dirty ();
900 AutomationLine::sync_model_with_view_point (ControlPoint& cp, bool did_push, int64_t distance)
902 ModelRepresentation mr;
905 model_representation (cp, mr);
907 /* how much are we changing the central point by */
909 ydelta = mr.yval - mr.ypos;
912 apply the full change to the central point, and interpolate
913 on both axes to cover all model points represented
914 by the control point.
917 /* change all points before the primary point */
919 for (AutomationList::iterator i = mr.start; i != cp.model; ++i) {
921 double fract = ((*i)->when - mr.xmin) / (mr.xpos - mr.xmin);
922 double y_delta = ydelta * fract;
923 double x_delta = distance * fract;
927 if (y_delta || x_delta) {
928 alist->modify (i, (*i)->when + x_delta, mr.ymin + y_delta);
932 /* change the primary point */
934 update_pending = true;
935 alist->modify (cp.model, mr.xval, mr.yval);
938 /* change later points */
940 AutomationList::iterator i = cp.model;
944 while (i != mr.end) {
946 double delta = ydelta * (mr.xmax - (*i)->when) / (mr.xmax - mr.xpos);
948 /* all later points move by the same distance along the x-axis as the main point */
951 alist->modify (i, (*i)->when + distance, (*i)->value + delta);
959 /* move all points after the range represented by the view by the same distance
960 as the main point moved.
963 alist->slide (mr.end, drag_distance);
969 AutomationLine::control_points_adjacent (double xval, uint32_t & before, uint32_t& after)
971 ControlPoint *bcp = 0;
972 ControlPoint *acp = 0;
975 /* xval is in frames */
977 unit_xval = trackview.editor.frame_to_unit (xval);
979 for (vector<ControlPoint*>::iterator i = control_points.begin(); i != control_points.end(); ++i) {
981 if ((*i)->get_x() <= unit_xval) {
983 if (!bcp || (*i)->get_x() > bcp->get_x()) {
985 before = bcp->view_index;
988 } else if ((*i)->get_x() > unit_xval) {
990 after = acp->view_index;
999 AutomationLine::is_last_point (ControlPoint& cp)
1001 ModelRepresentation mr;
1003 model_representation (cp, mr);
1005 // If the list is not empty, and the point is the last point in the list
1007 if (!alist->empty() && mr.end == alist->end()) {
1015 AutomationLine::is_first_point (ControlPoint& cp)
1017 ModelRepresentation mr;
1019 model_representation (cp, mr);
1021 // If the list is not empty, and the point is the first point in the list
1023 if (!alist->empty() && mr.start == alist->begin()) {
1030 // This is copied into AudioRegionGainLine
1032 AutomationLine::remove_point (ControlPoint& cp)
1034 ModelRepresentation mr;
1036 model_representation (cp, mr);
1038 trackview.editor.current_session()->begin_reversible_command (_("remove control point"));
1039 XMLNode &before = alist->get_state();
1041 alist->erase (mr.start, mr.end);
1043 trackview.editor.current_session()->add_command(new MementoCommand<AutomationList>(
1044 *alist.get(), &before, &alist->get_state()));
1045 trackview.editor.current_session()->commit_reversible_command ();
1046 trackview.editor.current_session()->set_dirty ();
1050 AutomationLine::get_selectables (nframes_t& start, nframes_t& end,
1051 double botfrac, double topfrac, list<Selectable*>& results)
1058 bool collecting = false;
1060 /* Curse X11 and its inverted coordinate system! */
1062 bot = _y_position + (1.0 - topfrac) * _height;
1063 top = _y_position + (1.0 - botfrac) * _height;
1065 nstart = max_frames;
1068 for (vector<ControlPoint*>::iterator i = control_points.begin(); i != control_points.end(); ++i) {
1070 nframes_t when = (nframes_t) (*(*i)->model)->when;
1072 if (when >= start && when <= end) {
1074 if ((*i)->get_y() >= bot && (*i)->get_y() <= top) {
1077 (*i)->set_visible(true);
1079 nstart = min (nstart, when);
1080 nend = max (nend, when);
1086 results.push_back (new AutomationSelectable (nstart, nend, botfrac, topfrac, trackview));
1088 nstart = max_frames;
1096 results.push_back (new AutomationSelectable (nstart, nend, botfrac, topfrac, trackview));
1102 AutomationLine::get_inverted_selectables (Selection&, list<Selectable*>& results)
1108 AutomationLine::set_selected_points (PointSelection& points)
1113 for (vector<ControlPoint*>::iterator i = control_points.begin(); i != control_points.end(); ++i) {
1114 (*i)->selected = false;
1117 if (points.empty()) {
1121 for (PointSelection::iterator r = points.begin(); r != points.end(); ++r) {
1123 if (&(*r).track != &trackview) {
1127 /* Curse X11 and its inverted coordinate system! */
1129 bot = _y_position + (1.0 - (*r).high_fract) * _height;
1130 top = _y_position + (1.0 - (*r).low_fract) * _height;
1132 for (vector<ControlPoint*>::iterator i = control_points.begin(); i != control_points.end(); ++i) {
1134 double rstart, rend;
1136 rstart = trackview.editor.frame_to_unit ((*r).start);
1137 rend = trackview.editor.frame_to_unit ((*r).end);
1139 if ((*i)->get_x() >= rstart && (*i)->get_x() <= rend) {
1141 if ((*i)->get_y() >= bot && (*i)->get_y() <= top) {
1143 (*i)->selected = true;
1151 for (vector<ControlPoint*>::iterator i = control_points.begin(); i != control_points.end(); ++i) {
1152 (*i)->show_color (false, !points_visible);
1157 void AutomationLine::set_colors() {
1158 set_line_color( ARDOUR_UI::config()->canvasvar_AutomationLine.get() );
1159 for (vector<ControlPoint*>::iterator i = control_points.begin(); i != control_points.end(); ++i) {
1160 (*i)->show_color (false, !points_visible);
1165 AutomationLine::show_selection ()
1167 TimeSelection& time (trackview.editor.get_selection().time);
1169 for (vector<ControlPoint*>::iterator i = control_points.begin(); i != control_points.end(); ++i) {
1171 (*i)->selected = false;
1173 for (list<AudioRange>::iterator r = time.begin(); r != time.end(); ++r) {
1174 double rstart, rend;
1176 rstart = trackview.editor.frame_to_unit ((*r).start);
1177 rend = trackview.editor.frame_to_unit ((*r).end);
1179 if ((*i)->get_x() >= rstart && (*i)->get_x() <= rend) {
1180 (*i)->selected = true;
1185 (*i)->show_color (false, !points_visible);
1190 AutomationLine::hide_selection ()
1192 // show_selection ();
1196 AutomationLine::list_changed ()
1202 AutomationLine::reset_callback (const AutomationList& events)
1204 ALPoints tmp_points;
1205 uint32_t npoints = events.size();
1208 for (vector<ControlPoint*>::iterator i = control_points.begin(); i != control_points.end(); ++i) {
1211 control_points.clear ();
1216 AutomationList::const_iterator ai;
1218 for (ai = events.const_begin(); ai != events.const_end(); ++ai) {
1220 double translated_y = (*ai)->value;
1221 model_to_view_y (translated_y);
1223 tmp_points.push_back (ALPoint (trackview.editor.frame_to_unit ((*ai)->when),
1224 _y_position + _height - (translated_y * _height)));
1227 determine_visible_control_points (tmp_points);
1231 AutomationLine::reset ()
1233 update_pending = false;
1239 alist->apply_to_points (*this, &AutomationLine::reset_callback);
1243 AutomationLine::clear ()
1245 /* parent must create command */
1246 XMLNode &before = get_state();
1248 trackview.editor.current_session()->add_command (new MementoCommand<AutomationLine>(*this, &before, &get_state()));
1249 trackview.editor.current_session()->commit_reversible_command ();
1250 trackview.editor.current_session()->set_dirty ();
1254 AutomationLine::change_model (AutomationList::iterator i, double x, double y)
1259 AutomationLine::change_model_range (AutomationList::iterator start, AutomationList::iterator end, double xdelta, float ydelta)
1261 alist->move_range (start, end, xdelta, ydelta);
1265 AutomationLine::show_all_control_points ()
1267 points_visible = true;
1269 for (vector<ControlPoint*>::iterator i = control_points.begin(); i != control_points.end(); ++i) {
1271 (*i)->set_visible (true);
1276 AutomationLine::hide_all_but_selected_control_points ()
1278 points_visible = false;
1280 for (vector<ControlPoint*>::iterator i = control_points.begin(); i != control_points.end(); ++i) {
1281 if (!(*i)->selected) {
1282 (*i)->set_visible (false);
1288 AutomationLine::get_state (void)
1290 /* function as a proxy for the model */
1291 return alist->get_state();
1295 AutomationLine::set_state (const XMLNode &node)
1297 /* function as a proxy for the model */
1298 return alist->set_state (node);
1302 AutomationLine::view_to_model_y (double& y)
1304 if (alist->parameter().type() == GainAutomation) {
1305 y = slider_position_to_gain (y);
1308 } else if (alist->parameter().type() == PanAutomation) {
1309 // vertical coordinate axis reversal
1311 } else if (alist->parameter().type() == MidiCCAutomation) {
1312 y = (int)(y * 127.0);
1313 } else if (alist->parameter().type() == PluginAutomation) {
1314 y = y * (double)(alist->get_max_y()- alist->get_min_y()) + alist->get_min_y();
1319 AutomationLine::model_to_view_y (double& y)
1321 if (alist->parameter().type() == GainAutomation) {
1322 y = gain_to_slider_position (y);
1323 } else if (alist->parameter().type() == PanAutomation) {
1324 // vertical coordinate axis reversal
1326 } else if (alist->parameter().type() == MidiCCAutomation) {
1328 } else if (alist->parameter().type() == PluginAutomation) {
1329 y = (y - alist->get_min_y()) / (double)(alist->get_max_y()- alist->get_min_y());