X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=gtk2_ardour%2Fautomation_line.cc;h=a72916191173a202609b0e388837dc3b450c006d;hb=1315ee3dff1e4966ab28bd47e81b5f003cac6e40;hp=ff1097cbd570d7b6d5c89dafc341dd53b86f9344;hpb=e493b2b7c4fbbbfc457f02babf9546289b430177;p=ardour.git diff --git a/gtk2_ardour/automation_line.cc b/gtk2_ardour/automation_line.cc index ff1097cbd5..a729161911 100644 --- a/gtk2_ardour/automation_line.cc +++ b/gtk2_ardour/automation_line.cc @@ -15,20 +15,22 @@ along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - $Id$ */ #include #include #include +#include #include +#include +#include #include #include #include -#include "canvas-simplerect.h" +#include "simplerect.h" #include "automation_line.h" #include "rgb_macros.h" #include "ardour_ui.h" @@ -46,10 +48,13 @@ #include "i18n.h" using namespace std; +using namespace sigc; using namespace ARDOUR; +using namespace PBD; using namespace Editing; +using namespace Gnome; // for Canvas -ControlPoint::ControlPoint (AutomationLine& al, gint (*event_handler)(GtkCanvasItem*, GdkEvent*, gpointer)) +ControlPoint::ControlPoint (AutomationLine& al) : line (al) { model = al.the_list().end(); @@ -61,17 +66,14 @@ ControlPoint::ControlPoint (AutomationLine& al, gint (*event_handler)(GtkCanvasI _size = 4.0; selected = false; - item = gtk_canvas_item_new (line.canvas_group(), - gtk_canvas_simplerect_get_type(), - "draw", (gboolean) TRUE, - "fill", (gboolean) FALSE, - "fill_color_rgba", color_map[cControlPointFill], - "outline_color_rgba", color_map[cControlPointOutline], - "outline_pixels", (gint) 1, - NULL); - - gtk_object_set_data (GTK_OBJECT(item), "control_point", this); - gtk_signal_connect (GTK_OBJECT(item), "event", (GtkSignalFunc) event_handler, this); + item = new Canvas::SimpleRect (line.canvas_group()); + item->property_draw() = true; + item->property_fill() = false; + item->property_fill_color_rgba() = color_map[cControlPointFill]; + item->property_outline_color_rgba() = color_map[cControlPointOutline]; + item->property_outline_pixels() = 1; + item->set_data ("control_point", this); + item->signal_event().connect (mem_fun (this, &ControlPoint::event_handler)); hide (); set_visible (false); @@ -93,12 +95,10 @@ ControlPoint::ControlPoint (const ControlPoint& other, bool dummy_arg_to_force_s _size = other._size; selected = false; - item = gtk_canvas_item_new (line.canvas_group(), - gtk_canvas_simplerect_get_type(), - "fill", (gboolean) FALSE, - "outline_color_rgba", color_map[cEnteredControlPointOutline], - "outline_pixels", (gint) 1, - NULL); + item = new Canvas::SimpleRect (line.canvas_group()); + item->property_fill() = false; + item->property_outline_color_rgba() = color_map[cEnteredControlPointOutline]; + item->property_outline_pixels() = 1; /* NOTE: no event handling in copied ControlPoints */ @@ -108,25 +108,31 @@ ControlPoint::ControlPoint (const ControlPoint& other, bool dummy_arg_to_force_s ControlPoint::~ControlPoint () { - gtk_object_destroy (GTK_OBJECT(item)); + delete item; +} + +bool +ControlPoint::event_handler (GdkEvent* event) +{ + return PublicEditor::instance().canvas_control_point_event (event, item, this); } void ControlPoint::hide () { - gtk_canvas_item_hide (item); + item->hide(); } void ControlPoint::show() { - gtk_canvas_item_show (item); + item->show(); } void ControlPoint::set_visible (bool yn) { - gtk_canvas_item_set (item, "draw", (gboolean) yn, NULL); + item->property_draw() = (gboolean) yn; } void @@ -142,10 +148,10 @@ ControlPoint::show_color (bool entered, bool hide_too) { if (entered) { if (selected) { - gtk_canvas_item_set (item, "outline_color_rgba", color_map[cEnteredControlPointSelected], NULL); + item->property_outline_color_rgba() = color_map[cEnteredControlPointSelected]; set_visible(true); } else { - gtk_canvas_item_set (item, "outline_color_rgba", color_map[cEnteredControlPoint], NULL); + item->property_outline_color_rgba() = color_map[cEnteredControlPoint]; if (hide_too) { set_visible(false); } @@ -153,10 +159,10 @@ ControlPoint::show_color (bool entered, bool hide_too) } else { if (selected) { - gtk_canvas_item_set (item, "outline_color_rgba", color_map[cControlPointSelected], NULL); + item->property_outline_color_rgba() = color_map[cControlPointSelected]; set_visible(true); } else { - gtk_canvas_item_set (item, "outline_color_rgba", color_map[cControlPoint], NULL); + item->property_outline_color_rgba() = color_map[cControlPoint]; if (hide_too) { set_visible(false); } @@ -171,13 +177,9 @@ ControlPoint::set_size (double sz) #if 0 if (_size > 6.0) { - gtk_canvas_item_set (item, - "fill", (gboolean) TRUE, - NULL); + item->property_fill() = (gboolean) TRUE; } else { - gtk_canvas_item_set (item, - "fill", (gboolean) FALSE, - NULL); + item->property_fill() = (gboolean) FALSE; } #endif @@ -206,12 +208,10 @@ ControlPoint::move_to (double x, double y, ShapeType shape) break; } - gtk_canvas_item_set (item, - "x1", x1, - "x2", x2, - "y1", y - half_size, - "y2", y + half_size, - NULL); + item->property_x1() = x1; + item->property_x2() = x2; + item->property_y1() = y - half_size; + item->property_y2() = y + half_size; _x = x; _y = y; @@ -220,52 +220,47 @@ ControlPoint::move_to (double x, double y, ShapeType shape) /*****/ -AutomationLine::AutomationLine (string name, TimeAxisView& tv, GtkCanvasItem* parent, AutomationList& al, - gint (*point_handler)(GtkCanvasItem*, GdkEvent*, gpointer), - gint (*line_handler)(GtkCanvasItem*, GdkEvent*, gpointer)) +AutomationLine::AutomationLine (const string & name, TimeAxisView& tv, ArdourCanvas::Group& parent, AutomationList& al) : trackview (tv), _name (name), - alist (al) + alist (al), + _parent_group (parent) { - point_coords = 0; points_visible = false; update_pending = false; _vc_uses_gain_mapping = false; no_draw = false; _visible = true; - point_callback = point_handler; - _parent_group = parent; terminal_points_can_slide = true; + _y_position = 0; _height = 0; - group = gtk_canvas_item_new (GTK_CANVAS_GROUP(parent), - gtk_canvas_group_get_type(), - "x", 0.0, - "y", 0.0, - NULL); + group = new ArdourCanvas::Group (parent); + group->property_x() = 0.0; + group->property_y() = 0.0; + + line = new ArdourCanvas::Line (*group); + line->property_width_pixels() = (guint)1; + line->set_data ("line", this); - line = gtk_canvas_item_new (GTK_CANVAS_GROUP(group), - gtk_canvas_line_get_type(), - "width_pixels", (guint) 1, - NULL); + line->signal_event().connect (mem_fun (*this, &AutomationLine::event_handler)); - // cerr << _name << " line @ " << line << endl; + alist.StateChanged.connect (mem_fun(*this, &AutomationLine::list_changed)); - gtk_object_set_data (GTK_OBJECT(line), "line", this); - gtk_signal_connect (GTK_OBJECT(line), "event", (GtkSignalFunc) line_handler, this); + trackview.session().register_with_memento_command_factory(alist.id(), this); - alist.StateChanged.connect (slot (*this, &AutomationLine::list_changed)); } AutomationLine::~AutomationLine () { - if (point_coords) { - gtk_canvas_points_unref (point_coords); - } - vector_delete (&control_points); + delete group; +} - gtk_object_destroy (GTK_OBJECT(group)); +bool +AutomationLine::event_handler (GdkEvent* event) +{ + return PublicEditor::instance().canvas_line_event (event, line, this); } void @@ -273,22 +268,14 @@ AutomationLine::queue_reset () { if (!update_pending) { update_pending = true; - Gtkmm2ext::UI::instance()->call_slot (slot (*this, &AutomationLine::reset)); + Gtkmm2ext::UI::instance()->call_slot (mem_fun(*this, &AutomationLine::reset)); } } -void -AutomationLine::set_point_size (double sz) -{ - for (vector::iterator i = control_points.begin(); i != control_points.end(); ++i) { - (*i)->set_size (sz); - } -} - void AutomationLine::show () { - gtk_canvas_item_show (line); + line->show(); if (points_visible) { for (vector::iterator i = control_points.begin(); i != control_points.end(); ++i) { @@ -302,27 +289,46 @@ AutomationLine::show () void AutomationLine::hide () { - gtk_canvas_item_hide (line); + line->hide(); for (vector::iterator i = control_points.begin(); i != control_points.end(); ++i) { (*i)->hide(); } _visible = false; } +double +AutomationLine::control_point_box_size () +{ + if (_height > TimeAxisView::hLarger) { + return 8.0; + } else if (_height > (guint32) TimeAxisView::hNormal) { + return 6.0; + } + return 4.0; +} + void -AutomationLine::set_height (guint32 h) +AutomationLine::set_y_position_and_height (guint32 y, guint32 h) { + bool changed = false; + + if (y != _y_position) { + _y_position = y; + changed = true; + } + if (h != _height) { _height = h; - if (_height > (guint32) TimeAxisView::Larger) { - set_point_size (8.0); - } else if (_height > (guint32) TimeAxisView::Normal) { - set_point_size (6.0); - } else { - set_point_size (4.0); + double const bsz = control_point_box_size(); + + for (vector::iterator i = control_points.begin(); i != control_points.end(); ++i) { + (*i)->set_size (bsz); } + changed = true; + } + if (changed) { reset (); } } @@ -331,7 +337,7 @@ void AutomationLine::set_line_color (uint32_t color) { _line_color = color; - gtk_canvas_item_set (line, "fill_color_rgba", color, NULL); + line->property_fill_color_rgba() = color; } void @@ -353,13 +359,6 @@ AutomationLine::nth (uint32_t n) } } -void -AutomationLine::modify_view (ControlPoint& cp, double x, double y, bool with_push) -{ - modify_view_point (cp, x, y, with_push); - update_line (); -} - void AutomationLine::modify_view_point (ControlPoint& cp, double x, double y, bool with_push) { @@ -377,7 +376,7 @@ AutomationLine::modify_view_point (ControlPoint& cp, double x, double y, bool wi y = max (0.0, y); y = min (1.0, y); - y = _height - (y * _height); + y = _y_position + _height - (y * _height); if (cp.can_slide) { @@ -422,7 +421,7 @@ AutomationLine::modify_view_point (ControlPoint& cp, double x, double y, bool wi ControlPoint* after; /* find the first point that can't move */ - + for (uint32_t n = cp.view_index + 1; (after = nth (n)) != 0; ++n) { if (!after->can_slide) { x_limit = after->get_x() - 1.0; @@ -477,18 +476,12 @@ AutomationLine::modify_view_point (ControlPoint& cp, double x, double y, bool wi void AutomationLine::reset_line_coords (ControlPoint& cp) { - if (point_coords) { - point_coords->coords[cp.view_index*2] = cp.get_x(); - point_coords->coords[(cp.view_index*2) + 1] = cp.get_y(); + if (cp.view_index < line_points.size()) { + line_points[cp.view_index].set_x (cp.get_x()); + line_points[cp.view_index].set_y (cp.get_y()); } } -void -AutomationLine::update_line () -{ - gtk_canvas_item_set (line, "points", point_coords, NULL); -} - void AutomationLine::sync_model_with_view_line (uint32_t start, uint32_t end) { @@ -499,10 +492,8 @@ AutomationLine::sync_model_with_view_line (uint32_t start, uint32_t end) for (uint32_t i = start; i <= end; ++i) { p = nth(i); - sync_model_with_view_point(*p); + sync_model_with_view_point (*p, false, 0); } - - } void @@ -513,19 +504,17 @@ AutomationLine::model_representation (ControlPoint& cp, ModelRepresentation& mr) line to convert them to something relevant. */ - mr.xval = (jack_nframes_t) floor (cp.get_x()); - mr.yval = 1.0 - (cp.get_y() / _height); - + mr.xval = (nframes_t) floor (cp.get_x()); + mr.yval = 1.0 - ( (cp.get_y() - _y_position) / _height); /* if xval has not changed, set it directly from the model to avoid rounding errors */ if (mr.xval == trackview.editor.frame_to_unit((*cp.model)->when)) { - mr.xval = (jack_nframes_t) (*cp.model)->when; + mr.xval = (nframes_t) (*cp.model)->when; } else { mr.xval = trackview.editor.unit_to_frame (mr.xval); } - /* virtual call: this will do the right thing for whatever particular type of line we are. */ @@ -535,7 +524,7 @@ AutomationLine::model_representation (ControlPoint& cp, ModelRepresentation& mr) /* part 2: find out where the model point is now */ - mr.xpos = (jack_nframes_t) (*cp.model)->when; + mr.xpos = (nframes_t) (*cp.model)->when; mr.ypos = (*cp.model)->value; /* part 3: get the position of the visual control @@ -554,7 +543,7 @@ AutomationLine::model_representation (ControlPoint& cp, ModelRepresentation& mr) after = nth (cp.view_index + 1); if (before) { - mr.xmin = (jack_nframes_t) (*before->model)->when; + mr.xmin = (nframes_t) (*before->model)->when; mr.ymin = (*before->model)->value; mr.start = before->model; ++mr.start; @@ -575,104 +564,11 @@ AutomationLine::model_representation (ControlPoint& cp, ModelRepresentation& mr) } void -AutomationLine::sync_model_from (ControlPoint& cp) +AutomationLine::determine_visible_control_points (ALPoints& points) { - ControlPoint* p; - uint32_t lasti; - - sync_model_with_view_point (cp); - - /* we might have moved all points after `cp' by some amount - if we pressed the with_push modifyer some of the time during the drag - so all subsequent points have to be resynced - */ - - lasti = control_points.size() - 1; - p = nth (lasti); - - update_pending = true; - - while (p != &cp && lasti) { - sync_model_with_view_point (*p); - --lasti; - p = nth (lasti); - } -} - -void -AutomationLine::sync_model_with_view_point (ControlPoint& cp) -{ - ModelRepresentation mr; - double ydelta; - - model_representation (cp, mr); - - /* part 4: how much are we changing the central point by */ - - ydelta = mr.yval - mr.ypos; - - /* IMPORTANT: changing the model when the x-coordinate changes - may invalidate the iterators that we are using. this means that we have - to change the points before+after the one corresponding to the visual CP - first (their x-coordinate doesn't change). then we change the - "main" point. - - apply the full change to the central point, and interpolate - in each direction to cover all model points represented - by the control point. - */ - - /* part 5: change all points before the primary point */ - - for (AutomationList::iterator i = mr.start; i != cp.model; ++i) { - - double delta; - - delta = ydelta * ((*i)->when - mr.xmin) / (mr.xpos - mr.xmin); - - /* x-coordinate (generally time) stays where it is, - y-coordinate moves by a certain amount. - */ - - update_pending = true; - change_model (i, (*i)->when, mr.yval + delta); - } - - /* part 6: change later points */ - - AutomationList::iterator i = cp.model; - - ++i; - - while (i != mr.end) { - - double delta; - - delta = ydelta * (mr.xmax - (*i)->when) / (mr.xmax - mr.xpos); - - /* x-coordinate (generally time) stays where it is, - y-coordinate moves by a certain amount. - */ - - update_pending = true; - change_model (i, (*i)->when, (*i)->value + delta); - - ++i; - } - - /* part 7: change the primary point */ - - update_pending = true; - change_model (cp.model, mr.xval, mr.yval); -} - -void -AutomationLine::determine_visible_control_points (GtkCanvasPoints* points) -{ - uint32_t xi, yi, view_index, pi; - int n; + uint32_t view_index, pi, n; AutomationList::iterator model; - uint32_t npoints = points->num_points; + uint32_t npoints; double last_control_point_x = 0.0; double last_control_point_y = 0.0; uint32_t this_rx = 0; @@ -680,31 +576,46 @@ AutomationLine::determine_visible_control_points (GtkCanvasPoints* points) uint32_t this_ry = 0; uint32_t prev_ry = 0; double* slope; + uint32_t box_size; + uint32_t cpsize; + + /* hide all existing points, and the line */ + + cpsize = 0; for (vector::iterator i = control_points.begin(); i != control_points.end(); ++i) { (*i)->hide(); + ++cpsize; } - gtk_canvas_item_hide (line); - if (points == 0 || points->num_points == 0) { + line->hide (); + + if (points.empty()) { return; } + npoints = points.size(); + /* compute derivative/slope for the entire line */ slope = new double[npoints]; - for (n = 0, xi = 2, yi = 3, view_index = 0; n < points->num_points - 1; xi += 2, yi +=2, ++n, ++view_index) { - double xdelta; - double ydelta; - xdelta = points->coords[xi] - points->coords[xi-2]; - ydelta = points->coords[yi] - points->coords[yi-2]; - slope[view_index] = ydelta/xdelta; + for (n = 0; n < npoints - 1; ++n) { + double xdelta = points[n+1].x - points[n].x; + double ydelta = points[n+1].y - points[n].y; + slope[n] = ydelta/xdelta; } + box_size = (uint32_t) control_point_box_size (); + /* read all points and decide which ones to show as control points */ - for (model = alist.begin(), pi = 0, xi = 0, yi = 1, view_index = 0; pi < npoints; ++model, xi += 2, yi +=2, ++pi) { + view_index = 0; + + for (model = alist.begin(), pi = 0; pi < npoints; ++model, ++pi) { + + double tx = points[pi].x; + double ty = points[pi].y; /* now ensure that the control_points vector reflects the current curve state, but don't plot control points too close together. also, don't @@ -737,30 +648,28 @@ AutomationLine::determine_visible_control_points (GtkCanvasPoints* points) points. */ - this_rx = (uint32_t) rint (points->coords[xi]); - this_ry = (unsigned long) rint (points->coords[yi]); - - if (view_index && pi != npoints && (this_rx == prev_rx) && (this_ry == prev_ry)) { + this_rx = (uint32_t) rint (tx); + this_ry = (uint32_t) rint (ty); + if (view_index && pi != npoints && /* not the first, not the last */ + (((this_rx == prev_rx) && (this_ry == prev_ry)) || /* same point */ + (((this_rx - prev_rx) < (box_size + 2)) && /* not identical, but still too close horizontally */ + (abs ((int)(this_ry - prev_ry)) < (int) (box_size + 2))))) { /* too close vertically */ continue; } /* ok, we should display this point */ - if (view_index >= control_points.size()) { - /* make sure we have enough control points */ + if (view_index >= cpsize) { - ControlPoint* ncp = new ControlPoint (*this, point_callback); + /* make sure we have enough control points */ - if (_height > (guint32) TimeAxisView::Larger) { - ncp->set_size (8.0); - } else if (_height > (guint32) TimeAxisView::Normal) { - ncp->set_size (6.0); - } else { - ncp->set_size (4.0); - } + ControlPoint* ncp = new ControlPoint (*this); + + ncp->set_size (box_size); control_points.push_back (ncp); + ++cpsize; } ControlPoint::ShapeType shape; @@ -768,7 +677,7 @@ AutomationLine::determine_visible_control_points (GtkCanvasPoints* points) if (!terminal_points_can_slide) { if (pi == 0) { control_points[view_index]->can_slide = false; - if (points->coords[xi] == 0) { + if (tx == 0) { shape = ControlPoint::Start; } else { shape = ControlPoint::Full; @@ -785,10 +694,10 @@ AutomationLine::determine_visible_control_points (GtkCanvasPoints* points) shape = ControlPoint::Full; } - control_points[view_index]->reset (points->coords[xi], points->coords[yi], model, view_index, shape); + last_control_point_x = tx; + last_control_point_y = ty; - last_control_point_x = points->coords[xi]; - last_control_point_y = points->coords[yi]; + control_points[view_index]->reset (tx, ty, model, view_index, shape); prev_rx = this_rx; prev_ry = this_ry; @@ -806,7 +715,7 @@ AutomationLine::determine_visible_control_points (GtkCanvasPoints* points) view_index++; } - + /* discard extra CP's to avoid confusing ourselves */ while (control_points.size() > view_index) { @@ -821,43 +730,35 @@ AutomationLine::determine_visible_control_points (GtkCanvasPoints* points) delete [] slope; - /* Now make sure the "point_coords" array is large enough - to represent all the visible points. - */ - if (view_index > 1) { npoints = view_index; - if (point_coords) { - if (point_coords->num_points < (int) npoints) { - gtk_canvas_points_unref (point_coords); - point_coords = get_canvas_points ("autoline", npoints); - } else { - point_coords->num_points = npoints; - } - } else { - point_coords = get_canvas_points ("autoline", npoints); - } - /* reset the line coordinates */ - uint32_t pci; - - for (pci = 0, view_index = 0; view_index < npoints; ++view_index) { - point_coords->coords[pci++] = control_points[view_index]->get_x(); - point_coords->coords[pci++] = control_points[view_index]->get_y(); + while (line_points.size() < npoints) { + line_points.push_back (Art::Point (0,0)); } - // cerr << "set al2 points, nc = " << point_coords->num_points << endl; - gtk_canvas_item_set (line, "points", point_coords, NULL); + while (line_points.size() > npoints) { + line_points.pop_back (); + } + + for (view_index = 0; view_index < npoints; ++view_index) { + line_points[view_index].set_x (control_points[view_index]->get_x()); + line_points[view_index].set_y (control_points[view_index]->get_y()); + } + + line->property_points() = line_points; if (_visible) { - gtk_canvas_item_show (line); + line->show (); } + } set_selected_points (trackview.editor.get_selection().points); + } string @@ -879,20 +780,20 @@ AutomationLine::get_verbose_cursor_string (float fraction) } bool -AutomationLine::invalid_point (GtkCanvasPoints* p, uint32_t index) +AutomationLine::invalid_point (ALPoints& p, uint32_t index) { - return p->coords[index*2] == max_frames && p->coords[(index*2)+1] == DBL_MAX; + return p[index].x == max_frames && p[index].y == DBL_MAX; } void -AutomationLine::invalidate_point (GtkCanvasPoints* p, uint32_t index) +AutomationLine::invalidate_point (ALPoints& p, uint32_t index) { - p->coords[index*2] = max_frames; - p->coords[(index*2)+1] = DBL_MAX; + p[index].x = max_frames; + p[index].y = DBL_MAX; } void -AutomationLine::start_drag (ControlPoint* cp, float fraction) +AutomationLine::start_drag (ControlPoint* cp, nframes_t x, float fraction) { if (trackview.editor.current_session() == 0) { /* how? */ return; @@ -907,24 +808,43 @@ AutomationLine::start_drag (ControlPoint* cp, float fraction) } trackview.editor.current_session()->begin_reversible_command (str); - trackview.editor.current_session()->add_undo (get_memento()); + trackview.editor.current_session()->add_command (new MementoCommand(alist, &get_state(), 0)); + drag_x = x; + drag_distance = 0; first_drag_fraction = fraction; last_drag_fraction = fraction; drags = 0; + did_push = false; } void -AutomationLine::point_drag (ControlPoint& cp, jack_nframes_t x, float fraction, bool with_push) +AutomationLine::point_drag (ControlPoint& cp, nframes_t x, float fraction, bool with_push) { - modify_view (cp, x, fraction, with_push); + if (x > drag_x) { + drag_distance += (x - drag_x); + } else { + drag_distance -= (drag_x - x); + } + + drag_x = x; + + modify_view_point (cp, x, fraction, with_push); + + if (line_points.size() > 1) { + line->property_points() = line_points; + } + drags++; + did_push = with_push; } void AutomationLine::line_drag (uint32_t i1, uint32_t i2, float fraction, bool with_push) { - double ydelta = fraction - last_drag_fraction; + double const ydelta = fraction - last_drag_fraction; + + did_push = with_push; last_drag_fraction = fraction; @@ -935,10 +855,12 @@ AutomationLine::line_drag (uint32_t i1, uint32_t i2, float fraction, bool with_p for (uint32_t i = i1 ; i <= i2; i++) { cp = nth (i); - modify_view_point (*cp, trackview.editor.unit_to_frame (cp->get_x()), ((_height - cp->get_y()) /_height) + ydelta, with_push); + modify_view_point (*cp, trackview.editor.unit_to_frame (cp->get_x()), ((_height - cp->get_y() + _y_position) /_height) + ydelta, with_push); } - update_line (); + if (line_points.size() > 1) { + line->property_points() = line_points; + } drags++; } @@ -946,20 +868,95 @@ AutomationLine::line_drag (uint32_t i1, uint32_t i2, float fraction, bool with_p void AutomationLine::end_drag (ControlPoint* cp) { - if (drags) { + if (!drags) { + return; + } - if (cp) { - sync_model_from (*cp); - } else { - sync_model_with_view_line (line_drag_cp1, line_drag_cp2); + alist.freeze (); + + if (cp) { + sync_model_with_view_point (*cp, did_push, drag_distance); + } else { + sync_model_with_view_line (line_drag_cp1, line_drag_cp2); + } + + alist.thaw (); + + update_pending = false; + + trackview.editor.current_session()->add_command (new MementoCommand(alist, 0, &alist.get_state())); + trackview.editor.current_session()->commit_reversible_command (); + trackview.editor.current_session()->set_dirty (); +} + + +void +AutomationLine::sync_model_with_view_point (ControlPoint& cp, bool did_push, int64_t distance) +{ + ModelRepresentation mr; + double ydelta; + + model_representation (cp, mr); + + /* how much are we changing the central point by */ + + ydelta = mr.yval - mr.ypos; + + /* + apply the full change to the central point, and interpolate + on both axes to cover all model points represented + by the control point. + */ + + /* change all points before the primary point */ + + for (AutomationList::iterator i = mr.start; i != cp.model; ++i) { + + double fract = ((*i)->when - mr.xmin) / (mr.xpos - mr.xmin); + double y_delta = ydelta * fract; + double x_delta = distance * fract; + + /* interpolate */ + + if (y_delta || x_delta) { + alist.modify (i, (*i)->when + x_delta, mr.ymin + y_delta); } + } + + /* change the primary point */ + + update_pending = true; + alist.modify (cp.model, mr.xval, mr.yval); + + + /* change later points */ + + AutomationList::iterator i = cp.model; + + ++i; + + while (i != mr.end) { + + double delta = ydelta * (mr.xmax - (*i)->when) / (mr.xmax - mr.xpos); + + /* all later points move by the same distance along the x-axis as the main point */ + + if (delta) { + alist.modify (i, (*i)->when + distance, (*i)->value + delta); + } + + ++i; + } + + if (did_push) { - update_pending = false; + /* move all points after the range represented by the view by the same distance + as the main point moved. + */ - trackview.editor.current_session()->add_redo_no_execute (get_memento()); - trackview.editor.current_session()->commit_reversible_command (); - trackview.editor.current_session()->set_dirty (); + alist.slide (mr.end, drag_distance); } + } bool @@ -1033,37 +1030,37 @@ AutomationLine::remove_point (ControlPoint& cp) model_representation (cp, mr); trackview.editor.current_session()->begin_reversible_command (_("remove control point")); - trackview.editor.current_session()->add_undo (get_memento()); + XMLNode &before = alist.get_state(); alist.erase (mr.start, mr.end); - trackview.editor.current_session()->add_redo_no_execute (get_memento()); + trackview.editor.current_session()->add_command(new MementoCommand(alist, &before, &alist.get_state())); trackview.editor.current_session()->commit_reversible_command (); trackview.editor.current_session()->set_dirty (); } void -AutomationLine::get_selectables (jack_nframes_t& start, jack_nframes_t& end, +AutomationLine::get_selectables (nframes_t& start, nframes_t& end, double botfrac, double topfrac, list& results) { double top; double bot; - jack_nframes_t nstart; - jack_nframes_t nend; + nframes_t nstart; + nframes_t nend; bool collecting = false; /* Curse X11 and its inverted coordinate system! */ - bot = (1.0 - topfrac) * _height; - top = (1.0 - botfrac) * _height; + bot = _y_position + (1.0 - topfrac) * _height; + top = _y_position + (1.0 - botfrac) * _height; nstart = max_frames; nend = 0; for (vector::iterator i = control_points.begin(); i != control_points.end(); ++i) { - jack_nframes_t when = (jack_nframes_t) (*(*i)->model)->when; + nframes_t when = (nframes_t) (*(*i)->model)->when; if (when >= start && when <= end) { @@ -1122,8 +1119,8 @@ AutomationLine::set_selected_points (PointSelection& points) /* Curse X11 and its inverted coordinate system! */ - bot = (1.0 - (*r).high_fract) * _height; - top = (1.0 - (*r).low_fract) * _height; + bot = _y_position + (1.0 - (*r).high_fract) * _height; + top = _y_position + (1.0 - (*r).low_fract) * _height; for (vector::iterator i = control_points.begin(); i != control_points.end(); ++i) { @@ -1150,13 +1147,18 @@ AutomationLine::set_selected_points (PointSelection& points) } +void AutomationLine::set_colors() { + set_line_color( color_map[cAutomationLine] ); + for (vector::iterator i = control_points.begin(); i != control_points.end(); ++i) { + (*i)->show_color (false, !points_visible); + } +} + void AutomationLine::show_selection () { TimeSelection& time (trackview.editor.get_selection().time); - // cerr << "show selection\n"; - for (vector::iterator i = control_points.begin(); i != control_points.end(); ++i) { (*i)->selected = false; @@ -1180,20 +1182,11 @@ AutomationLine::show_selection () void AutomationLine::hide_selection () { - // cerr << "hide selection\n"; // show_selection (); } - -// This is copied into AudioRegionGainLine -UndoAction -AutomationLine::get_memento () -{ - return alist.get_memento(); -} - void -AutomationLine::list_changed (Change ignored) +AutomationLine::list_changed () { queue_reset (); } @@ -1201,7 +1194,7 @@ AutomationLine::list_changed (Change ignored) void AutomationLine::reset_callback (const AutomationList& events) { - GtkCanvasPoints *tmp_points; + ALPoints tmp_points; uint32_t npoints = events.size(); if (npoints == 0) { @@ -1209,29 +1202,22 @@ AutomationLine::reset_callback (const AutomationList& events) delete *i; } control_points.clear (); - gtk_canvas_item_hide (line); + line->hide(); return; } - tmp_points = get_canvas_points ("autoline reset", max (npoints, (uint32_t) 2)); - - uint32_t xi, yi; AutomationList::const_iterator ai; - for (ai = events.const_begin(), xi = 0, yi = 1; ai != events.const_end(); xi += 2, yi +=2, ++ai) { + for (ai = events.const_begin(); ai != events.const_end(); ++ai) { - tmp_points->coords[xi] = trackview.editor.frame_to_unit ((*ai)->when); - double translated_y; - - translated_y = (*ai)->value; + double translated_y = (*ai)->value; model_to_view_y (translated_y); - tmp_points->coords[yi] = _height - (translated_y * _height); - } - - tmp_points->num_points = npoints; + tmp_points.push_back (ALPoint (trackview.editor.frame_to_unit ((*ai)->when), + _y_position + _height - (translated_y * _height))); + } + determine_visible_control_points (tmp_points); - gtk_canvas_points_unref (tmp_points); } void @@ -1250,9 +1236,9 @@ void AutomationLine::clear () { /* parent must create command */ - trackview.editor.current_session()->add_undo (get_memento()); + XMLNode &before = get_state(); alist.clear(); - trackview.editor.current_session()->add_redo_no_execute (get_memento()); + trackview.editor.current_session()->add_command (new MementoCommand(*this, &before, &get_state())); trackview.editor.current_session()->commit_reversible_command (); trackview.editor.current_session()->set_dirty (); } @@ -1260,7 +1246,6 @@ AutomationLine::clear () void AutomationLine::change_model (AutomationList::iterator i, double x, double y) { - alist.modify (i, (jack_nframes_t) x, y); } void @@ -1291,3 +1276,17 @@ AutomationLine::hide_all_but_selected_control_points () } } } + +XMLNode & +AutomationLine::get_state (void) +{ + /* function as a proxy for the model */ + return alist.get_state(); +} + +int +AutomationLine::set_state (const XMLNode &node) +{ + /* function as a proxy for the model */ + return alist.set_state (node); +}