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).
This commit is contained in:
parent
03df442d0e
commit
17294ab9ec
@ -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<AudioRegion>* 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<AudioRegion>(*(audio_region().get()), ®ion_before, ®ion_after));
|
||||
region_memento = new MementoCommand<AudioRegion>(*(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<Selectable*> results;
|
||||
|
||||
XMLNode &after = audio_region()->envelope()->get_state();
|
||||
trackview.session()->add_command (new MementoCommand<AutomationList>(*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<AutomationList>(*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
|
||||
|
@ -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<ARDOUR::AutomationList> (_line->memento_command_binder(), &before, &after));
|
||||
view->editor().commit_reversible_command ();
|
||||
view->session()->add_command (new MementoCommand<ARDOUR::AutomationList> (_line->memento_command_binder(), &before, &after));
|
||||
view->editor().commit_reversible_command ();
|
||||
|
||||
view->session()->set_dirty ();
|
||||
view->session()->set_dirty ();
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
|
@ -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<Selectable*> 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<ARDOUR::AutomationList> (*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<ARDOUR::AutomationList> (*list.get (), &before, &after));
|
||||
_editor.commit_reversible_command ();
|
||||
_session->set_dirty ();
|
||||
_editor.commit_reversible_command ();
|
||||
_session->set_dirty ();
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
|
@ -5557,92 +5557,6 @@ AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
for (list<AudioRange>::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<Line>::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<AutomationList> 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<AutomationList> 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<Line>::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<AudioRange>::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<AudioRange>::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<Line>::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<AutomationList> 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<AutomationList>(*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<AutomationList> 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<AutomationList>(*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<Line>::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<AudioRange>::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<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
|
||||
i->line->start_drag_multiple (i->points, y_fraction (i->line, current_pointer_y()), i->state);
|
||||
}
|
||||
|
@ -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<Selectable*> selectables;
|
||||
uint32_t before, after;
|
||||
framecnt_t const where = (framecnt_t) floor (event->button.x * samples_per_pixel);
|
||||
|
||||
if ((al = reinterpret_cast<AutomationLine*> (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:
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user