From 17294ab9ec2d0b826dce88930148fda0f5e978b3 Mon Sep 17 00:00:00 2001 From: nick_m Date: Mon, 14 Sep 2015 05:24:28 +1000 Subject: [PATCH] Make control point selection more consistent. - disallow simultaneous events via ControlList::editor_add () - clicking on an automation line selects the points that define it. - don't 'flash' a region selection when using mousedraw mode. - cp click selection resembles region selection. - region gain points respect snap modifier (a la automation points). --- gtk2_ardour/audio_region_view.cc | 27 +++- gtk2_ardour/automation_region_view.cc | 13 +- gtk2_ardour/automation_time_axis.cc | 17 ++- gtk2_ardour/editor_drag.cc | 189 ++++++++++++++------------ gtk2_ardour/editor_mouse.cc | 55 +++++++- gtk2_ardour/editor_selection.cc | 14 +- gtk2_ardour/selection.cc | 2 +- libs/evoral/evoral/ControlList.hpp | 3 +- libs/evoral/src/ControlList.cpp | 20 ++- 9 files changed, 224 insertions(+), 116 deletions(-) diff --git a/gtk2_ardour/audio_region_view.cc b/gtk2_ardour/audio_region_view.cc index 706611a81d..dd15529019 100644 --- a/gtk2_ardour/audio_region_view.cc +++ b/gtk2_ardour/audio_region_view.cc @@ -1334,25 +1334,40 @@ AudioRegionView::add_gain_point_event (ArdourCanvas::Item *item, GdkEvent *ev, b gain_line->view_to_model_coord (x, y); + trackview.editor ().snap_to_with_modifier (fx, ev); + /* XXX STATEFUL: can't convert to stateful diff until we can represent automation data with it. */ - trackview.editor().begin_reversible_command (_("add gain control point")); XMLNode &before = audio_region()->envelope()->get_state(); + MementoCommand* region_memento = 0; if (!audio_region()->envelope_active()) { XMLNode ®ion_before = audio_region()->get_state(); audio_region()->set_envelope_active(true); XMLNode ®ion_after = audio_region()->get_state(); - trackview.session()->add_command (new MementoCommand(*(audio_region().get()), ®ion_before, ®ion_after)); + region_memento = new MementoCommand(*(audio_region().get()), ®ion_before, ®ion_after); } - audio_region()->envelope()->editor_add (fx, y, with_guard_points); + if (audio_region()->envelope()->editor_add (fx, y, with_guard_points)) { + XMLNode &after = audio_region()->envelope()->get_state(); + std::list results; - XMLNode &after = audio_region()->envelope()->get_state(); - trackview.session()->add_command (new MementoCommand(*audio_region()->envelope().get(), &before, &after)); - trackview.editor().commit_reversible_command (); + trackview.editor().begin_reversible_command (_("add gain control point")); + + if (region_memento) { + trackview.session()->add_command (region_memento); + } + + trackview.session()->add_command (new MementoCommand(*audio_region()->envelope().get(), &before, &after)); + + gain_line->get_selectables (fx, fx, 0.0, 1.0, results); + trackview.editor ().get_selection ().set (results); + + trackview.editor ().commit_reversible_command (); + trackview.session ()->set_dirty (); + } } void diff --git a/gtk2_ardour/automation_region_view.cc b/gtk2_ardour/automation_region_view.cc index 5450815329..f1f9d68b98 100644 --- a/gtk2_ardour/automation_region_view.cc +++ b/gtk2_ardour/automation_region_view.cc @@ -187,17 +187,18 @@ AutomationRegionView::add_automation_event (GdkEvent *, framepos_t when, double double when_d = when; _line->view_to_model_coord (when_d, y); - view->editor().begin_reversible_command (_("add automation event")); XMLNode& before = _line->the_list()->get_state(); - _line->the_list()->editor_add (when_d, y, with_guard_points); + if (_line->the_list()->editor_add (when_d, y, with_guard_points)) { + view->editor().begin_reversible_command (_("add automation event")); - XMLNode& after = _line->the_list()->get_state(); + XMLNode& after = _line->the_list()->get_state(); - view->session()->add_command (new MementoCommand (_line->memento_command_binder(), &before, &after)); - view->editor().commit_reversible_command (); + view->session()->add_command (new MementoCommand (_line->memento_command_binder(), &before, &after)); + view->editor().commit_reversible_command (); - view->session()->set_dirty (); + view->session()->set_dirty (); + } } bool diff --git a/gtk2_ardour/automation_time_axis.cc b/gtk2_ardour/automation_time_axis.cc index 1c79593b34..1ff3024ac6 100644 --- a/gtk2_ardour/automation_time_axis.cc +++ b/gtk2_ardour/automation_time_axis.cc @@ -638,18 +638,21 @@ AutomationTimeAxisView::add_automation_event (GdkEvent* event, framepos_t when, _line->view_to_model_coord (x, y); - _editor.snap_to_with_modifier (when, event); - _editor.begin_reversible_command (_("add automation event")); XMLNode& before = list->get_state(); + std::list results; + if (list->editor_add (when, y, with_guard_points)) { + XMLNode& after = list->get_state(); + _editor.begin_reversible_command (_("add automation event")); + _session->add_command (new MementoCommand (*list.get (), &before, &after)); - list->editor_add (when, y, with_guard_points); + _line->get_selectables (when, when, 0.0, 1.0, results); + _editor.get_selection ().set (results); - XMLNode& after = list->get_state(); - _session->add_command (new MementoCommand (*list.get (), &before, &after)); - _editor.commit_reversible_command (); - _session->set_dirty (); + _editor.commit_reversible_command (); + _session->set_dirty (); + } } bool diff --git a/gtk2_ardour/editor_drag.cc b/gtk2_ardour/editor_drag.cc index 90e4dc48e4..d3e40adf12 100644 --- a/gtk2_ardour/editor_drag.cc +++ b/gtk2_ardour/editor_drag.cc @@ -5557,92 +5557,6 @@ AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor) } } - } else { - - for (list::const_iterator i = _ranges.begin(); i != _ranges.end(); ++i) { - - framecnt_t const half = (i->start + i->end) / 2; - - /* find the line that this audio range starts in */ - list::iterator j = _lines.begin(); - while (j != _lines.end() && (j->range.first > i->start || j->range.second < i->start)) { - ++j; - } - - if (j != _lines.end()) { - boost::shared_ptr the_list = j->line->the_list (); - - /* j is the line that this audio range starts in; fade into it; - 64 samples length plucked out of thin air. - */ - - framepos_t a = i->start + 64; - if (a > half) { - a = half; - } - - double const p = j->line->time_converter().from (i->start - j->line->time_converter().origin_b ()); - double const q = j->line->time_converter().from (a - j->line->time_converter().origin_b ()); - - the_list->editor_add (p, value (the_list, p), false); - the_list->editor_add (q, value (the_list, q), false); - } - - /* same thing for the end */ - - j = _lines.begin(); - while (j != _lines.end() && (j->range.first > i->end || j->range.second < i->end)) { - ++j; - } - - if (j != _lines.end()) { - boost::shared_ptr the_list = j->line->the_list (); - - /* j is the line that this audio range starts in; fade out of it; - 64 samples length plucked out of thin air. - */ - - framepos_t b = i->end - 64; - if (b < half) { - b = half; - } - - double const p = j->line->time_converter().from (b - j->line->time_converter().origin_b ()); - double const q = j->line->time_converter().from (i->end - j->line->time_converter().origin_b ()); - - the_list->editor_add (p, value (the_list, p), false); - the_list->editor_add (q, value (the_list, q), false); - } - } - - _nothing_to_drag = true; - - /* Find all the points that should be dragged and put them in the relevant - points lists in the Line structs. - */ - - for (list::iterator i = _lines.begin(); i != _lines.end(); ++i) { - - uint32_t const N = i->line->npoints (); - for (uint32_t j = 0; j < N; ++j) { - - /* here's a control point on this line */ - ControlPoint* p = i->line->nth (j); - double const w = i->line->time_converter().to ((*p->model())->when) + i->line->time_converter().origin_b (); - - /* see if it's inside a range */ - list::const_iterator k = _ranges.begin (); - while (k != _ranges.end() && (k->start >= w || k->end <= w)) { - ++k; - } - - if (k != _ranges.end()) { - /* dragging this point */ - _nothing_to_drag = false; - i->points.push_back (p); - } - } - } } if (_nothing_to_drag) { @@ -5653,12 +5567,113 @@ AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor) void AutomationRangeDrag::motion (GdkEvent*, bool first_move) { - if (_nothing_to_drag) { + if (_nothing_to_drag && !first_move) { return; } if (first_move) { _editor->begin_reversible_command (_("automation range move")); + + if (!_ranges.empty()) { + + for (list::const_iterator i = _ranges.begin(); i != _ranges.end(); ++i) { + + framecnt_t const half = (i->start + i->end) / 2; + + /* find the line that this audio range starts in */ + list::iterator j = _lines.begin(); + while (j != _lines.end() && (j->range.first > i->start || j->range.second < i->start)) { + ++j; + } + + if (j != _lines.end()) { + boost::shared_ptr the_list = j->line->the_list (); + + /* j is the line that this audio range starts in; fade into it; + 64 samples length plucked out of thin air. + */ + + framepos_t a = i->start + 64; + if (a > half) { + a = half; + } + + double const p = j->line->time_converter().from (i->start - j->line->time_converter().origin_b ()); + double const q = j->line->time_converter().from (a - j->line->time_converter().origin_b ()); + + XMLNode &before = the_list->get_state(); + bool const add_p = the_list->editor_add (p, value (the_list, p), false); + bool const add_q = the_list->editor_add (q, value (the_list, q), false); + + if (add_p || add_q) { + _editor->session()->add_command ( + new MementoCommand(*the_list.get (), &before, &the_list->get_state())); + } + } + + /* same thing for the end */ + + j = _lines.begin(); + while (j != _lines.end() && (j->range.first > i->end || j->range.second < i->end)) { + ++j; + } + + if (j != _lines.end()) { + boost::shared_ptr the_list = j->line->the_list (); + + /* j is the line that this audio range starts in; fade out of it; + 64 samples length plucked out of thin air. + */ + + framepos_t b = i->end - 64; + if (b < half) { + b = half; + } + + double const p = j->line->time_converter().from (b - j->line->time_converter().origin_b ()); + double const q = j->line->time_converter().from (i->end - j->line->time_converter().origin_b ()); + + XMLNode &before = the_list->get_state(); + bool const add_p = the_list->editor_add (p, value (the_list, p), false); + bool const add_q = the_list->editor_add (q, value (the_list, q), false); + + if (add_p || add_q) { + _editor->session()->add_command ( + new MementoCommand(*the_list.get (), &before, &the_list->get_state())); + } + } + } + + _nothing_to_drag = true; + + /* Find all the points that should be dragged and put them in the relevant + points lists in the Line structs. + */ + + for (list::iterator i = _lines.begin(); i != _lines.end(); ++i) { + + uint32_t const N = i->line->npoints (); + for (uint32_t j = 0; j < N; ++j) { + + /* here's a control point on this line */ + ControlPoint* p = i->line->nth (j); + double const w = i->line->time_converter().to ((*p->model())->when) + i->line->time_converter().origin_b (); + + /* see if it's inside a range */ + list::const_iterator k = _ranges.begin (); + while (k != _ranges.end() && (k->start >= w || k->end <= w)) { + ++k; + } + + if (k != _ranges.end()) { + /* dragging this point */ + _nothing_to_drag = false; + i->points.push_back (p); + } + } + } + } + for (list::iterator i = _lines.begin(); i != _lines.end(); ++i) { i->line->start_drag_multiple (i->points, y_fraction (i->line, current_pointer_y()), i->state); } diff --git a/gtk2_ardour/editor_mouse.cc b/gtk2_ardour/editor_mouse.cc index ccf63839e2..b4e4cc81af 100644 --- a/gtk2_ardour/editor_mouse.cc +++ b/gtk2_ardour/editor_mouse.cc @@ -399,8 +399,9 @@ Editor::step_mouse_mode (bool next) } void -Editor::button_selection (ArdourCanvas::Item* /*item*/, GdkEvent* event, ItemType item_type) +Editor::button_selection (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type) { + /* in object/audition/timefx/gain-automation mode, any button press sets the selection if the object can be selected. this is a bit of hack, because @@ -472,6 +473,9 @@ Editor::button_selection (ArdourCanvas::Item* /*item*/, GdkEvent* event, ItemTyp switch (item_type) { case RegionItem: + if (eff_mouse_mode == MouseDraw) { + break; + } if (press) { if (eff_mouse_mode != MouseRange) { _mouse_changed_selection = set_selected_regionview_from_click (press, op); @@ -519,6 +523,51 @@ Editor::button_selection (ArdourCanvas::Item* /*item*/, GdkEvent* event, ItemTyp } break; + case GainLineItem: + case AutomationLineItem: + if (eff_mouse_mode != MouseRange) { + AutomationLine* al; + std::list selectables; + uint32_t before, after; + framecnt_t const where = (framecnt_t) floor (event->button.x * samples_per_pixel); + + if ((al = reinterpret_cast (item->get_data ("line")))) { + + if (!al->control_points_adjacent (where, before, after)) { + break; + } + } + + selectables.push_back (al->nth (before)); + selectables.push_back (al->nth (after)); + + switch (op) { + case Selection::Set: + if (press) { + selection->set (selectables); + _mouse_changed_selection = true; + } + break; + case Selection::Add: + if (press) { + selection->add (selectables); + _mouse_changed_selection = true; + } + break; + case Selection::Toggle: + if (press) { + selection->toggle (selectables); + _mouse_changed_selection = true; + } + break; + + case Selection::Extend: + /* XXX */ + break; + } + } + break; + case StreamItem: /* for context click, select track */ if (event->button.button == 3) { @@ -532,7 +581,9 @@ Editor::button_selection (ArdourCanvas::Item* /*item*/, GdkEvent* event, ItemTyp break; case AutomationTrackItem: - set_selected_track_as_side_effect (op); + if (eff_mouse_mode != MouseDraw) { + set_selected_track_as_side_effect (op); + } break; default: diff --git a/gtk2_ardour/editor_selection.cc b/gtk2_ardour/editor_selection.cc index 3a6277ecec..27e17d5fcc 100644 --- a/gtk2_ardour/editor_selection.cc +++ b/gtk2_ardour/editor_selection.cc @@ -327,15 +327,27 @@ Editor::set_selected_control_point_from_click (bool press, Selection::Operation if (!clicked_control_point) { return false; } + bool ret = false; switch (op) { case Selection::Set: - if (press) { + if (!selection->selected (clicked_control_point)) { selection->set (clicked_control_point); ret = true; + } else { + /* clicked on an already selected point */ + if (press) { + break; + } else { + if (selection->points.size() > 1) { + selection->set (clicked_control_point); + ret = true; + } + } } break; + case Selection::Add: if (press) { selection->add (clicked_control_point); diff --git a/gtk2_ardour/selection.cc b/gtk2_ardour/selection.cc index 784f646f39..822aca0384 100644 --- a/gtk2_ardour/selection.cc +++ b/gtk2_ardour/selection.cc @@ -1113,7 +1113,7 @@ Selection::set (ControlPoint* cp) clear_time (); //enforce region/object exclusivity clear_tracks(); //enforce object/track exclusivity - if (cp->get_selected()) { + if (cp->get_selected () && points.size () == 1) { return; } diff --git a/libs/evoral/evoral/ControlList.hpp b/libs/evoral/evoral/ControlList.hpp index 3784b25720..80096b65a4 100644 --- a/libs/evoral/evoral/ControlList.hpp +++ b/libs/evoral/evoral/ControlList.hpp @@ -125,7 +125,8 @@ public: void shift (double before, double distance); virtual void add (double when, double value, bool with_guards=true, bool with_initial=true); - virtual void editor_add (double when, double value, bool with_guard); + + virtual bool editor_add (double when, double value, bool with_guard); void fast_simple_add (double when, double value); diff --git a/libs/evoral/src/ControlList.cpp b/libs/evoral/src/ControlList.cpp index d8665d3396..d9c1b993bd 100644 --- a/libs/evoral/src/ControlList.cpp +++ b/libs/evoral/src/ControlList.cpp @@ -451,12 +451,19 @@ ControlList::in_write_pass () const return _in_write_pass; } -void +bool ControlList::editor_add (double when, double value, bool with_guard) { /* this is for making changes from a graphical line editor */ + ControlEvent cp (when, 0.0f); + iterator i = lower_bound (_events.begin(), _events.end(), &cp, time_comparator); + + if (i != _events.end () && (*i)->when == when) { + return false; + } + if (_events.empty()) { /* as long as the point we're adding is not at zero, @@ -477,15 +484,18 @@ ControlList::editor_add (double when, double value, bool with_guard) maybe_add_insert_guard (when); } - ControlEvent cp (when, 0.0f); - iterator i = lower_bound (_events.begin(), _events.end(), &cp, time_comparator); + iterator result; DEBUG_TRACE (DEBUG::ControlList, string_compose ("editor_add: actually add when= %1 value= %2\n", when, value)); - _events.insert (i, new ControlEvent (when, value)); + result = _events.insert (i, new ControlEvent (when, value)); + + if (i == result) { + return false; + } mark_dirty (); - maybe_signal_changed (); + return true; } void