Fix AutomationTrackItem rubberband click thinking it was unhandled.

Fix several other cases where a single mouse click could cause several
(not nested) selection ops.
Fix missing selection memento for midi notes and midi commands.
Rename some variables.
Fix random style issues.
This commit is contained in:
nick_m 2015-01-11 04:07:31 +11:00
parent 9e873acedb
commit 44203ce955
14 changed files with 375 additions and 66 deletions

View file

@ -156,7 +156,7 @@ void
AutomationLine::update_visibility () AutomationLine::update_visibility ()
{ {
if (_visible & Line) { if (_visible & Line) {
/* Only show the line there are some points, otherwise we may show an out-of-date line /* Only show the line when there are some points, otherwise we may show an out-of-date line
when automation points have been removed (the line will still follow the shape of the when automation points have been removed (the line will still follow the shape of the
old points). old points).
*/ */

View file

@ -80,7 +80,6 @@
#include "control_protocol/control_protocol.h" #include "control_protocol/control_protocol.h"
#include "actions.h"
#include "actions.h" #include "actions.h"
#include "analysis_window.h" #include "analysis_window.h"
#include "audio_clock.h" #include "audio_clock.h"
@ -251,6 +250,7 @@ pane_size_watcher (Paned* pane)
Editor::Editor () Editor::Editor ()
: _join_object_range_state (JOIN_OBJECT_RANGE_NONE) : _join_object_range_state (JOIN_OBJECT_RANGE_NONE)
, _mouse_changed_selection (false)
/* time display buttons */ /* time display buttons */
, minsec_label (_("Mins:Secs")) , minsec_label (_("Mins:Secs"))
, bbt_label (_("Bars:Beats")) , bbt_label (_("Bars:Beats"))
@ -3336,7 +3336,7 @@ void
Editor::begin_reversible_selection_op (string name) Editor::begin_reversible_selection_op (string name)
{ {
if (_session) { if (_session) {
//cerr << name << endl; cerr << name << endl;
/* begin/commit pairs can be nested */ /* begin/commit pairs can be nested */
selection_op_cmd_depth++; selection_op_cmd_depth++;
} }
@ -3349,20 +3349,22 @@ Editor::commit_reversible_selection_op ()
if (selection_op_cmd_depth == 1) { if (selection_op_cmd_depth == 1) {
if (selection_op_history_it > 0 && selection_op_history_it < selection_op_history.size()) { if (selection_op_history_it > 0 && selection_op_history_it < selection_op_history.size()) {
/* the user has undone some selection ops and then made a new one */
list<XMLNode *>::iterator it = selection_op_history.begin(); list<XMLNode *>::iterator it = selection_op_history.begin();
advance (it, selection_op_history_it); advance (it, selection_op_history_it);
selection_op_history.erase (selection_op_history.begin(), it); selection_op_history.erase (selection_op_history.begin(), it);
} }
selection_op_history.push_front (&_selection_memento->get_state ()); selection_op_history.push_front (&_selection_memento->get_state ());
selection_op_history_it = 0; selection_op_history_it = 0;
selection_undo_action->set_sensitive (true);
selection_redo_action->set_sensitive (false);
} }
if (selection_op_cmd_depth > 0) { if (selection_op_cmd_depth > 0) {
selection_op_cmd_depth--; selection_op_cmd_depth--;
} }
selection_undo_action->set_sensitive (true);
selection_redo_action->set_sensitive (false);
} }
} }
@ -3378,7 +3380,6 @@ Editor::undo_selection_op ()
selection_redo_action->set_sensitive (true); selection_redo_action->set_sensitive (true);
} }
++n; ++n;
} }
/* is there an earlier entry? */ /* is there an earlier entry? */
if ((selection_op_history_it + 1) >= selection_op_history.size()) { if ((selection_op_history_it + 1) >= selection_op_history.size()) {
@ -3401,7 +3402,6 @@ Editor::redo_selection_op ()
selection_undo_action->set_sensitive (true); selection_undo_action->set_sensitive (true);
} }
++n; ++n;
} }
if (selection_op_history_it == 0) { if (selection_op_history_it == 0) {
@ -4899,17 +4899,17 @@ Editor::get_regions_from_selection_and_entered ()
} }
void void
Editor::get_regionviews_by_id (PBD::ID const & id, RegionSelection & regions) const Editor::get_regionviews_by_id (PBD::ID const id, RegionSelection & regions) const
{ {
for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) { for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
RouteTimeAxisView* tatv; RouteTimeAxisView* rtav;
if ((tatv = dynamic_cast<RouteTimeAxisView*> (*i)) != 0) { if ((rtav = dynamic_cast<RouteTimeAxisView*> (*i)) != 0) {
boost::shared_ptr<Playlist> pl; boost::shared_ptr<Playlist> pl;
std::vector<boost::shared_ptr<Region> > results; std::vector<boost::shared_ptr<Region> > results;
boost::shared_ptr<Track> tr; boost::shared_ptr<Track> tr;
if ((tr = tatv->track()) == 0) { if ((tr = rtav->track()) == 0) {
/* bus */ /* bus */
continue; continue;
} }
@ -4917,9 +4917,9 @@ Editor::get_regionviews_by_id (PBD::ID const & id, RegionSelection & regions) co
if ((pl = (tr->playlist())) != 0) { if ((pl = (tr->playlist())) != 0) {
boost::shared_ptr<Region> r = pl->region_by_id (id); boost::shared_ptr<Region> r = pl->region_by_id (id);
if (r) { if (r) {
RegionView* marv = tatv->view()->find_view (r); RegionView* rv = rtav->view()->find_view (r);
if (marv) { if (rv) {
regions.push_back (marv); regions.push_back (rv);
} }
} }
} }
@ -4927,6 +4927,21 @@ Editor::get_regionviews_by_id (PBD::ID const & id, RegionSelection & regions) co
} }
} }
void
Editor::get_per_region_note_selection (list<pair<PBD::ID, set<boost::shared_ptr<Evoral::Note<Evoral::Beats> > > > > &selection) const
{
for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
MidiTimeAxisView* mtav;
if ((mtav = dynamic_cast<MidiTimeAxisView*> (*i)) != 0) {
mtav->get_per_region_note_selection (selection);
}
}
}
void void
Editor::get_regions_corresponding_to (boost::shared_ptr<Region> region, vector<RegionView*>& regions, bool src_comparison) Editor::get_regions_corresponding_to (boost::shared_ptr<Region> region, vector<RegionView*>& regions, bool src_comparison)
{ {
@ -5102,6 +5117,19 @@ Editor::region_view_added (RegionView * rv)
break; break;
} }
} }
MidiRegionView* mrv = dynamic_cast<MidiRegionView*> (rv);
if (mrv) {
list<pair<PBD::ID const, list<boost::shared_ptr<Evoral::Note<Evoral::Beats> > > > >::iterator rnote;
for (rnote = selection->pending_midi_note_selection.begin(); rnote != selection->pending_midi_note_selection.end(); ++rnote) {
if (rv->region()->id () == (*rnote).first) {
mrv->select_notes ((*rnote).second);
selection->pending_midi_note_selection.erase(rnote);
break;
}
}
}
_summary->set_background_dirty (); _summary->set_background_dirty ();
} }

View file

@ -421,7 +421,8 @@ class Editor : public PublicEditor, public PBD::ScopedConnectionList, public ARD
void get_regions_corresponding_to (boost::shared_ptr<ARDOUR::Region> region, std::vector<RegionView*>& regions, bool src_comparison); void get_regions_corresponding_to (boost::shared_ptr<ARDOUR::Region> region, std::vector<RegionView*>& regions, bool src_comparison);
void get_regionviews_by_id (PBD::ID const & id, RegionSelection & regions) const; void get_regionviews_by_id (PBD::ID const id, RegionSelection & regions) const;
void get_per_region_note_selection (std::list<std::pair<PBD::ID, std::set<boost::shared_ptr<Evoral::Note<Evoral::Beats> > > > >&) const;
void center_screen (framepos_t); void center_screen (framepos_t);
@ -706,6 +707,7 @@ class Editor : public PublicEditor, public PBD::ScopedConnectionList, public ARD
void button_selection (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type); void button_selection (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type);
bool button_release_can_deselect; bool button_release_can_deselect;
bool _mouse_changed_selection;
void catch_vanishing_regionview (RegionView *); void catch_vanishing_regionview (RegionView *);

View file

@ -178,7 +178,9 @@ Editor::canvas_scroll_event (GdkEventScroll *event, bool from_canvas)
bool bool
Editor::track_canvas_button_press_event (GdkEventButton */*event*/) Editor::track_canvas_button_press_event (GdkEventButton */*event*/)
{ {
begin_reversible_selection_op (_("Clear Selection Click (track canvas)"));
selection->clear (); selection->clear ();
commit_reversible_selection_op();
_track_canvas->grab_focus(); _track_canvas->grab_focus();
return false; return false;
} }
@ -1105,8 +1107,10 @@ Editor::canvas_drop_zone_event (GdkEvent* event)
switch (event->type) { switch (event->type) {
case GDK_BUTTON_RELEASE: case GDK_BUTTON_RELEASE:
if (event->button.button == 1) { if (event->button.button == 1) {
begin_reversible_selection_op (_("Nowhere Click"));
selection->clear_objects (); selection->clear_objects ();
selection->clear_tracks (); selection->clear_tracks ();
commit_reversible_selection_op ();
} }
break; break;

View file

@ -4718,6 +4718,9 @@ NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
} else { } else {
_region->unique_select (_primary); _region->unique_select (_primary);
} }
_editor->begin_reversible_selection_op(_("Select Note Press"));
_editor->commit_reversible_selection_op();
} }
} }
} }
@ -4803,10 +4806,13 @@ NoteDrag::finished (GdkEvent* ev, bool moved)
if (_editor->current_mouse_mode() == Editing::MouseObject || if (_editor->current_mouse_mode() == Editing::MouseObject ||
_editor->current_mouse_mode() == Editing::MouseDraw) { _editor->current_mouse_mode() == Editing::MouseDraw) {
bool changed = false;
if (_was_selected) { if (_was_selected) {
bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier); bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
if (add) { if (add) {
_region->note_deselected (_primary); _region->note_deselected (_primary);
changed = true;
} }
} else { } else {
bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier); bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier);
@ -4814,12 +4820,19 @@ NoteDrag::finished (GdkEvent* ev, bool moved)
if (!extend && !add && _region->selection_size() > 1) { if (!extend && !add && _region->selection_size() > 1) {
_region->unique_select (_primary); _region->unique_select (_primary);
changed = true;
} else if (extend) { } else if (extend) {
_region->note_selected (_primary, true, true); _region->note_selected (_primary, true, true);
changed = true;
} else { } else {
/* it was added during button press */ /* it was added during button press */
} }
} }
if (changed) {
_editor->begin_reversible_selection_op(_("Select Note Release"));
_editor->commit_reversible_selection_op();
}
} }
} else { } else {
_region->note_dropped (_primary, total_dx(), total_dy()); _region->note_dropped (_primary, total_dx(), total_dy());
@ -5204,12 +5217,15 @@ EditorRubberbandSelectDrag::select_things (int button_state, framepos_t x1, fram
void void
EditorRubberbandSelectDrag::deselect_things () EditorRubberbandSelectDrag::deselect_things ()
{ {
if (!getenv("ARDOUR_SAE")) { _editor->begin_reversible_selection_op (_("Clear Selection (rubberband)"));
_editor->selection->clear_tracks();
} _editor->selection->clear_tracks();
_editor->selection->clear_regions(); _editor->selection->clear_regions();
_editor->selection->clear_points (); _editor->selection->clear_points ();
_editor->selection->clear_lines (); _editor->selection->clear_lines ();
_editor->selection->clear_midi_notes ();
_editor->commit_reversible_selection_op();
} }
NoteCreateDrag::NoteCreateDrag (Editor* e, ArdourCanvas::Item* i, MidiRegionView* rv) NoteCreateDrag::NoteCreateDrag (Editor* e, ArdourCanvas::Item* i, MidiRegionView* rv)

View file

@ -448,11 +448,15 @@ Editor::button_selection (ArdourCanvas::Item* /*item*/, GdkEvent* event, ItemTyp
Selection::Operation op = ArdourKeyboard::selection_type (event->button.state); Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
bool press = (event->type == GDK_BUTTON_PRESS); bool press = (event->type == GDK_BUTTON_PRESS);
if (press) {
_mouse_changed_selection = false;
}
switch (item_type) { switch (item_type) {
case RegionItem: case RegionItem:
if (press) { if (press) {
if (eff_mouse_mode != MouseRange) { if (eff_mouse_mode != MouseRange) {
set_selected_regionview_from_click (press, op); _mouse_changed_selection = set_selected_regionview_from_click (press, op);
} else { } else {
/* don't change the selection unless the /* don't change the selection unless the
clicked track is not currently selected. if clicked track is not currently selected. if
@ -465,7 +469,7 @@ Editor::button_selection (ArdourCanvas::Item* /*item*/, GdkEvent* event, ItemTyp
} }
} else { } else {
if (eff_mouse_mode != MouseRange) { if (eff_mouse_mode != MouseRange) {
set_selected_regionview_from_click (press, op); _mouse_changed_selection = set_selected_regionview_from_click (press, op);
} }
} }
break; break;
@ -483,7 +487,7 @@ Editor::button_selection (ArdourCanvas::Item* /*item*/, GdkEvent* event, ItemTyp
case StartCrossFadeItem: case StartCrossFadeItem:
case EndCrossFadeItem: case EndCrossFadeItem:
if (get_smart_mode() || eff_mouse_mode != MouseRange) { if (get_smart_mode() || eff_mouse_mode != MouseRange) {
set_selected_regionview_from_click (press, op); _mouse_changed_selection = set_selected_regionview_from_click (press, op);
} else if (event->type == GDK_BUTTON_PRESS) { } else if (event->type == GDK_BUTTON_PRESS) {
set_selected_track_as_side_effect (op); set_selected_track_as_side_effect (op);
} }
@ -492,7 +496,7 @@ Editor::button_selection (ArdourCanvas::Item* /*item*/, GdkEvent* event, ItemTyp
case ControlPointItem: case ControlPointItem:
set_selected_track_as_side_effect (op); set_selected_track_as_side_effect (op);
if (eff_mouse_mode != MouseRange) { if (eff_mouse_mode != MouseRange) {
set_selected_control_point_from_click (press, op); _mouse_changed_selection = set_selected_control_point_from_click (press, op);
} }
break; break;
@ -501,6 +505,7 @@ Editor::button_selection (ArdourCanvas::Item* /*item*/, GdkEvent* event, ItemTyp
if (event->button.button == 3) { if (event->button.button == 3) {
selection->clear_tracks (); selection->clear_tracks ();
set_selected_track_as_side_effect (op); set_selected_track_as_side_effect (op);
_mouse_changed_selection = true;
} }
break; break;
@ -511,6 +516,12 @@ Editor::button_selection (ArdourCanvas::Item* /*item*/, GdkEvent* event, ItemTyp
default: default:
break; break;
} }
if ((!press) && _mouse_changed_selection) {
begin_reversible_selection_op (_("Button Selection"));
commit_reversible_selection_op ();
_mouse_changed_selection = false;
}
} }
bool bool
@ -744,6 +755,7 @@ Editor::button_press_handler_1 (ArdourCanvas::Item* item, GdkEvent* event, ItemT
case AutomationTrackItem: case AutomationTrackItem:
/* rubberband drag to select automation points */ /* rubberband drag to select automation points */
_drags->set (new EditorRubberbandSelectDrag (this, item), event); _drags->set (new EditorRubberbandSelectDrag (this, item), event);
return true;
break; break;
default: default:
@ -1465,10 +1477,7 @@ Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemT
} }
/* do any (de)selection operations that should occur on button release */ /* do any (de)selection operations that should occur on button release */
button_selection (item, event, item_type);
begin_reversible_selection_op (_("Button Select"));
button_selection (item, event, item_type);
commit_reversible_selection_op ();
return true; return true;
break; break;

View file

@ -202,8 +202,9 @@ Editor::set_selected_track_as_side_effect (Selection::Operation op)
} }
} else if (group && group->is_active()) { } else if (group && group->is_active()) {
for (TrackViewList::iterator i = track_views.begin(); i != track_views.end (); ++i) { for (TrackViewList::iterator i = track_views.begin(); i != track_views.end (); ++i) {
if ((*i)->route_group() == group) if ((*i)->route_group() == group) {
selection->remove(*i); selection->remove(*i);
}
} }
} else { } else {
selection->remove (clicked_axisview); selection->remove (clicked_axisview);
@ -215,8 +216,9 @@ Editor::set_selected_track_as_side_effect (Selection::Operation op)
} }
} else if (group && group->is_active()) { } else if (group && group->is_active()) {
for (TrackViewList::iterator i = track_views.begin(); i != track_views.end (); ++i) { for (TrackViewList::iterator i = track_views.begin(); i != track_views.end (); ++i) {
if ( (*i)->route_group() == group) if ((*i)->route_group() == group) {
selection->add(*i); selection->add(*i);
}
} }
} else { } else {
selection->add (clicked_axisview); selection->add (clicked_axisview);
@ -234,8 +236,9 @@ Editor::set_selected_track_as_side_effect (Selection::Operation op)
} }
} else if (group && group->is_active()) { } else if (group && group->is_active()) {
for (TrackViewList::iterator i = track_views.begin(); i != track_views.end (); ++i) { for (TrackViewList::iterator i = track_views.begin(); i != track_views.end (); ++i) {
if ((*i)->route_group() == group) if ((*i)->route_group() == group) {
selection->add(*i); selection->add(*i);
}
} }
} else { } else {
selection->add (clicked_axisview); selection->add (clicked_axisview);
@ -253,8 +256,9 @@ Editor::set_selected_track_as_side_effect (Selection::Operation op)
} }
} else if (group && group->is_active()) { } else if (group && group->is_active()) {
for (TrackViewList::iterator i = track_views.begin(); i != track_views.end (); ++i) { for (TrackViewList::iterator i = track_views.begin(); i != track_views.end (); ++i) {
if ((*i)->route_group() == group) if ((*i)->route_group() == group) {
selection->add(*i); selection->add(*i);
}
} }
} else { } else {
selection->set (clicked_axisview); selection->set (clicked_axisview);
@ -1953,7 +1957,7 @@ Editor::get_edit_op_range (framepos_t& start, framepos_t& end) const
void void
Editor::deselect_all () Editor::deselect_all ()
{ {
begin_reversible_selection_op(_("Clear Selection")); begin_reversible_selection_op(_("Deselect All"));
selection->clear (); selection->clear ();
commit_reversible_selection_op (); commit_reversible_selection_op ();
} }

View file

@ -123,6 +123,7 @@ MidiRegionView::MidiRegionView (ArdourCanvas::Container* parent,
, _last_event_y (0) , _last_event_y (0)
, _grabbed_keyboard (false) , _grabbed_keyboard (false)
, _entered (false) , _entered (false)
, _mouse_changed_selection (false)
{ {
CANVAS_DEBUG_NAME (_note_group, string_compose ("note group for %1", get_item_name())); CANVAS_DEBUG_NAME (_note_group, string_compose ("note group for %1", get_item_name()));
_note_group->raise_to_top(); _note_group->raise_to_top();
@ -169,6 +170,7 @@ MidiRegionView::MidiRegionView (ArdourCanvas::Container* parent,
, _last_event_y (0) , _last_event_y (0)
, _grabbed_keyboard (false) , _grabbed_keyboard (false)
, _entered (false) , _entered (false)
, _mouse_changed_selection (false)
{ {
CANVAS_DEBUG_NAME (_note_group, string_compose ("note group for %1", get_item_name())); CANVAS_DEBUG_NAME (_note_group, string_compose ("note group for %1", get_item_name()));
_note_group->raise_to_top(); _note_group->raise_to_top();
@ -220,6 +222,7 @@ MidiRegionView::MidiRegionView (const MidiRegionView& other)
, _last_event_y (0) , _last_event_y (0)
, _grabbed_keyboard (false) , _grabbed_keyboard (false)
, _entered (false) , _entered (false)
, _mouse_changed_selection (false)
{ {
init (false); init (false);
} }
@ -250,6 +253,7 @@ MidiRegionView::MidiRegionView (const MidiRegionView& other, boost::shared_ptr<M
, _last_event_y (0) , _last_event_y (0)
, _grabbed_keyboard (false) , _grabbed_keyboard (false)
, _entered (false) , _entered (false)
, _mouse_changed_selection (false)
{ {
init (true); init (true);
} }
@ -502,6 +506,7 @@ MidiRegionView::button_press (GdkEventButton* ev)
} }
_pressed_button = ev->button; _pressed_button = ev->button;
_mouse_changed_selection = false;
return true; return true;
} }
@ -532,12 +537,14 @@ MidiRegionView::button_release (GdkEventButton* ev)
case MouseRange: case MouseRange:
/* no motion occured - simple click */ /* no motion occured - simple click */
clear_selection (); clear_selection ();
_mouse_changed_selection = true;
break; break;
case MouseContent: case MouseContent:
case MouseTimeFX: case MouseTimeFX:
{ {
clear_selection(); clear_selection();
_mouse_changed_selection = true;
if (Keyboard::is_insert_note_event(ev)) { if (Keyboard::is_insert_note_event(ev)) {
@ -591,6 +598,11 @@ MidiRegionView::button_release (GdkEventButton* ev)
break; break;
} }
if(_mouse_changed_selection) {
trackview.editor().begin_reversible_selection_op (_("Mouse Selection Change"));
trackview.editor().commit_reversible_selection_op ();
}
return false; return false;
} }
@ -643,6 +655,7 @@ MidiRegionView::motion (GdkEventMotion* ev)
editor.drags()->set (new MidiRubberbandSelectDrag (dynamic_cast<Editor *> (&editor), this), (GdkEvent *) ev); editor.drags()->set (new MidiRubberbandSelectDrag (dynamic_cast<Editor *> (&editor), this), (GdkEvent *) ev);
if (!Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) { if (!Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) {
clear_selection (); clear_selection ();
_mouse_changed_selection = true;
} }
_mouse_state = SelectRectDragging; _mouse_state = SelectRectDragging;
return true; return true;
@ -747,22 +760,32 @@ MidiRegionView::key_press (GdkEventKey* ev)
} else if (ev->keyval == GDK_Tab) { } else if (ev->keyval == GDK_Tab) {
trackview.editor().begin_reversible_selection_op (_("Select Adjacent Note"));
if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) { if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) {
goto_previous_note (Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier)); goto_previous_note (Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier));
} else { } else {
goto_next_note (Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier)); goto_next_note (Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier));
} }
trackview.editor().commit_reversible_selection_op();
return true; return true;
} else if (ev->keyval == GDK_ISO_Left_Tab) { } else if (ev->keyval == GDK_ISO_Left_Tab) {
/* Shift-TAB generates ISO Left Tab, for some reason */ /* Shift-TAB generates ISO Left Tab, for some reason */
trackview.editor().begin_reversible_selection_op (_("Select Adjacent Note"));
if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) { if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) {
goto_previous_note (Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier)); goto_previous_note (Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier));
} else { } else {
goto_next_note (Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier)); goto_next_note (Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier));
} }
trackview.editor().commit_reversible_selection_op();
return true; return true;
@ -946,9 +969,11 @@ MidiRegionView::create_note_at (framepos_t t, double y, Evoral::Beats length, bo
view->update_note_range(new_note->note()); view->update_note_range(new_note->note());
trackview.editor().begin_reversible_command(_("add note"));
MidiModel::NoteDiffCommand* cmd = _model->new_note_diff_command(_("add note")); MidiModel::NoteDiffCommand* cmd = _model->new_note_diff_command(_("add note"));
cmd->add (new_note); cmd->add (new_note);
_model->apply_command(*trackview.session(), cmd); _model->apply_command(*trackview.session(), cmd);
trackview.editor().commit_reversible_command();
play_midi_note (new_note); play_midi_note (new_note);
} }
@ -982,8 +1007,8 @@ MidiRegionView::display_model(boost::shared_ptr<MidiModel> model)
content_connection.disconnect (); content_connection.disconnect ();
_model->ContentsChanged.connect (content_connection, invalidator (*this), boost::bind (&MidiRegionView::redisplay_model, this), gui_context()); _model->ContentsChanged.connect (content_connection, invalidator (*this), boost::bind (&MidiRegionView::redisplay_model, this), gui_context());
/* Don't signal as nobody else needs to know until selection has been altered.*/
clear_events (); clear_events (false);
if (_enable_display) { if (_enable_display) {
redisplay_model(); redisplay_model();
@ -994,6 +1019,7 @@ void
MidiRegionView::start_note_diff_command (string name) MidiRegionView::start_note_diff_command (string name)
{ {
if (!_note_diff_command) { if (!_note_diff_command) {
trackview.editor().begin_reversible_command (name);
_note_diff_command = _model->new_note_diff_command (name); _note_diff_command = _model->new_note_diff_command (name);
} }
} }
@ -1044,6 +1070,7 @@ void
MidiRegionView::apply_diff (bool as_subcommand) MidiRegionView::apply_diff (bool as_subcommand)
{ {
bool add_or_remove; bool add_or_remove;
bool commit = false;
if (!_note_diff_command) { if (!_note_diff_command) {
return; return;
@ -1060,6 +1087,7 @@ MidiRegionView::apply_diff (bool as_subcommand)
_model->apply_command_as_subcommand (*trackview.session(), _note_diff_command); _model->apply_command_as_subcommand (*trackview.session(), _note_diff_command);
} else { } else {
_model->apply_command (*trackview.session(), _note_diff_command); _model->apply_command (*trackview.session(), _note_diff_command);
commit = true;
} }
_note_diff_command = 0; _note_diff_command = 0;
@ -1070,6 +1098,9 @@ MidiRegionView::apply_diff (bool as_subcommand)
} }
_marked_for_velocity.clear(); _marked_for_velocity.clear();
if (commit) {
trackview.editor().commit_reversible_command ();
}
} }
void void
@ -1100,6 +1131,27 @@ MidiRegionView::find_canvas_note (boost::shared_ptr<NoteType> note)
return 0; return 0;
} }
/** This version finds any canvas note matching the supplied note.*/
NoteBase*
MidiRegionView::find_canvas_note (NoteType note)
{
if (_optimization_iterator != _events.end()) {
++_optimization_iterator;
}
if (_optimization_iterator != _events.end() && (*(*_optimization_iterator)->note()) == note) {
return *_optimization_iterator;
}
for (_optimization_iterator = _events.begin(); _optimization_iterator != _events.end(); ++_optimization_iterator) {
if (*((*_optimization_iterator)->note()) == note) {
return *_optimization_iterator;
}
}
return 0;
}
void void
MidiRegionView::get_events (Events& e, Evoral::Sequence<Evoral::Beats>::NoteOperator op, uint8_t val, int chan_mask) MidiRegionView::get_events (Events& e, Evoral::Sequence<Evoral::Beats>::NoteOperator op, uint8_t val, int chan_mask)
{ {
@ -1173,6 +1225,13 @@ MidiRegionView::redisplay_model()
add_note (note, visible); add_note (note, visible);
} }
set<boost::shared_ptr<NoteType> >::iterator it;
for (it = _pending_note_selection.begin(); it != _pending_note_selection.end(); ++it) {
if (*(*it) == *note) {
add_to_selection (cne);
}
}
} else { } else {
if (!empty_when_starting && (cne = find_canvas_note (note)) != 0) { if (!empty_when_starting && (cne = find_canvas_note (note)) != 0) {
@ -1182,7 +1241,6 @@ MidiRegionView::redisplay_model()
} }
} }
/* remove note items that are no longer valid */ /* remove note items that are no longer valid */
if (!empty_when_starting) { if (!empty_when_starting) {
@ -1213,6 +1271,7 @@ MidiRegionView::redisplay_model()
_marked_for_selection.clear (); _marked_for_selection.clear ();
_marked_for_velocity.clear (); _marked_for_velocity.clear ();
_pending_note_selection.clear ();
/* we may have caused _events to contain things out of order (e.g. if a note /* we may have caused _events to contain things out of order (e.g. if a note
moved earlier or later). we don't generally need them in time order, but moved earlier or later). we don't generally need them in time order, but
@ -1909,7 +1968,9 @@ MidiRegionView::get_patch_key_at (Evoral::Beats time, uint8_t channel, MIDI::Nam
void void
MidiRegionView::change_patch_change (PatchChange& pc, const MIDI::Name::PatchPrimaryKey& new_patch) MidiRegionView::change_patch_change (PatchChange& pc, const MIDI::Name::PatchPrimaryKey& new_patch)
{ {
MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("alter patch change")); string name = _("alter patch change");
trackview.editor().begin_reversible_command (name);
MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (name);
if (pc.patch()->program() != new_patch.program()) { if (pc.patch()->program() != new_patch.program()) {
c->change_program (pc.patch (), new_patch.program()); c->change_program (pc.patch (), new_patch.program());
@ -1921,6 +1982,7 @@ MidiRegionView::change_patch_change (PatchChange& pc, const MIDI::Name::PatchPri
} }
_model->apply_command (*trackview.session(), c); _model->apply_command (*trackview.session(), c);
trackview.editor().commit_reversible_command ();
_patch_changes.clear (); _patch_changes.clear ();
display_patch_changes (); display_patch_changes ();
@ -1929,7 +1991,9 @@ MidiRegionView::change_patch_change (PatchChange& pc, const MIDI::Name::PatchPri
void void
MidiRegionView::change_patch_change (MidiModel::PatchChangePtr old_change, const Evoral::PatchChange<Evoral::Beats> & new_change) MidiRegionView::change_patch_change (MidiModel::PatchChangePtr old_change, const Evoral::PatchChange<Evoral::Beats> & new_change)
{ {
MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("alter patch change")); string name = _("alter patch change");
trackview.editor().begin_reversible_command (name);
MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (name);
if (old_change->time() != new_change.time()) { if (old_change->time() != new_change.time()) {
c->change_time (old_change, new_change.time()); c->change_time (old_change, new_change.time());
@ -1948,6 +2012,7 @@ MidiRegionView::change_patch_change (MidiModel::PatchChangePtr old_change, const
} }
_model->apply_command (*trackview.session(), c); _model->apply_command (*trackview.session(), c);
trackview.editor().commit_reversible_command ();
_patch_changes.clear (); _patch_changes.clear ();
display_patch_changes (); display_patch_changes ();
@ -1962,8 +2027,10 @@ void
MidiRegionView::add_patch_change (framecnt_t t, Evoral::PatchChange<Evoral::Beats> const & patch) MidiRegionView::add_patch_change (framecnt_t t, Evoral::PatchChange<Evoral::Beats> const & patch)
{ {
MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview); MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
string name = _("add patch change");
MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("add patch change")); trackview.editor().begin_reversible_command (name);
MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (name);
c->add (MidiModel::PatchChangePtr ( c->add (MidiModel::PatchChangePtr (
new Evoral::PatchChange<Evoral::Beats> ( new Evoral::PatchChange<Evoral::Beats> (
absolute_frames_to_source_beats (_region->position() + t), absolute_frames_to_source_beats (_region->position() + t),
@ -1973,6 +2040,7 @@ MidiRegionView::add_patch_change (framecnt_t t, Evoral::PatchChange<Evoral::Beat
); );
_model->apply_command (*trackview.session(), c); _model->apply_command (*trackview.session(), c);
trackview.editor().commit_reversible_command ();
_patch_changes.clear (); _patch_changes.clear ();
display_patch_changes (); display_patch_changes ();
@ -1981,9 +2049,11 @@ MidiRegionView::add_patch_change (framecnt_t t, Evoral::PatchChange<Evoral::Beat
void void
MidiRegionView::move_patch_change (PatchChange& pc, Evoral::Beats t) MidiRegionView::move_patch_change (PatchChange& pc, Evoral::Beats t)
{ {
trackview.editor().begin_reversible_command (_("move patch change"));
MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("move patch change")); MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("move patch change"));
c->change_time (pc.patch (), t); c->change_time (pc.patch (), t);
_model->apply_command (*trackview.session(), c); _model->apply_command (*trackview.session(), c);
trackview.editor().commit_reversible_command ();
_patch_changes.clear (); _patch_changes.clear ();
display_patch_changes (); display_patch_changes ();
@ -1992,9 +2062,11 @@ MidiRegionView::move_patch_change (PatchChange& pc, Evoral::Beats t)
void void
MidiRegionView::delete_patch_change (PatchChange* pc) MidiRegionView::delete_patch_change (PatchChange* pc)
{ {
trackview.editor().begin_reversible_command (_("delete patch change"));
MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("delete patch change")); MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("delete patch change"));
c->remove (pc->patch ()); c->remove (pc->patch ());
_model->apply_command (*trackview.session(), c); _model->apply_command (*trackview.session(), c);
trackview.editor().commit_reversible_command ();
_patch_changes.clear (); _patch_changes.clear ();
display_patch_changes (); display_patch_changes ();
@ -2143,6 +2215,24 @@ MidiRegionView::invert_selection ()
} }
} }
/** Used for selection undo/redo.
The requested notes most likely won't exist in the view until the next model redisplay.
*/
void
MidiRegionView::select_notes (list<boost::shared_ptr<NoteType> > notes)
{
NoteBase* cne;
list<boost::shared_ptr<NoteType> >::iterator n;
for (n = notes.begin(); n != notes.end(); ++n) {
if ((cne = find_canvas_note(*(*n))) != 0) {
add_to_selection (cne);
} else {
_pending_note_selection.insert(*n);
}
}
}
void void
MidiRegionView::select_matching_notes (uint8_t notenum, uint16_t channel_mask, bool add, bool extend) MidiRegionView::select_matching_notes (uint8_t notenum, uint16_t channel_mask, bool add, bool extend)
{ {

View file

@ -203,6 +203,7 @@ public:
void move_selection(double dx, double dy, double cumulative_dy); void move_selection(double dx, double dy, double cumulative_dy);
void note_dropped (NoteBase* ev, ARDOUR::frameoffset_t, int8_t d_note); void note_dropped (NoteBase* ev, ARDOUR::frameoffset_t, int8_t d_note);
void select_notes (std::list<boost::shared_ptr<NoteType> >);
void select_matching_notes (uint8_t notenum, uint16_t channel_mask, bool add, bool extend); void select_matching_notes (uint8_t notenum, uint16_t channel_mask, bool add, bool extend);
void toggle_matching_notes (uint8_t notenum, uint16_t channel_mask); void toggle_matching_notes (uint8_t notenum, uint16_t channel_mask);
@ -438,6 +439,9 @@ private:
* when they appear after the command is applied. */ * when they appear after the command is applied. */
std::set< boost::shared_ptr<NoteType> > _marked_for_selection; std::set< boost::shared_ptr<NoteType> > _marked_for_selection;
/** Notes that should be selected when the model is redisplayed. */
std::set< boost::shared_ptr<NoteType> > _pending_note_selection;
/** New notes (created in the current command) which should have visible velocity /** New notes (created in the current command) which should have visible velocity
* when they appear after the command is applied. */ * when they appear after the command is applied. */
std::set< boost::shared_ptr<NoteType> > _marked_for_velocity; std::set< boost::shared_ptr<NoteType> > _marked_for_velocity;
@ -448,6 +452,7 @@ private:
PBD::ScopedConnection content_connection; PBD::ScopedConnection content_connection;
NoteBase* find_canvas_note (boost::shared_ptr<NoteType>); NoteBase* find_canvas_note (boost::shared_ptr<NoteType>);
NoteBase* find_canvas_note (NoteType);
Events::iterator _optimization_iterator; Events::iterator _optimization_iterator;
void update_note (NoteBase*, bool update_ghost_regions = true); void update_note (NoteBase*, bool update_ghost_regions = true);
@ -499,6 +504,8 @@ private:
bool _grabbed_keyboard; bool _grabbed_keyboard;
bool _entered; bool _entered;
bool _mouse_changed_selection;
framepos_t snap_frame_to_grid_underneath (framepos_t p, framecnt_t &) const; framepos_t snap_frame_to_grid_underneath (framepos_t p, framecnt_t &) const;
PBD::ScopedConnection _mouse_mode_connection; PBD::ScopedConnection _mouse_mode_connection;

View file

@ -1308,6 +1308,8 @@ MidiTimeAxisView::set_note_selection (uint8_t note)
{ {
uint16_t chn_mask = midi_track()->get_playback_channel_mask(); uint16_t chn_mask = midi_track()->get_playback_channel_mask();
_editor.begin_reversible_selection_op(_("Set Note Selection"));
if (_view->num_selected_regionviews() == 0) { if (_view->num_selected_regionviews() == 0) {
_view->foreach_regionview ( _view->foreach_regionview (
sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_selection_region_view), sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_selection_region_view),
@ -1317,6 +1319,8 @@ MidiTimeAxisView::set_note_selection (uint8_t note)
sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_selection_region_view), sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_selection_region_view),
note, chn_mask)); note, chn_mask));
} }
_editor.commit_reversible_selection_op();
} }
void void
@ -1324,6 +1328,8 @@ MidiTimeAxisView::add_note_selection (uint8_t note)
{ {
const uint16_t chn_mask = midi_track()->get_playback_channel_mask(); const uint16_t chn_mask = midi_track()->get_playback_channel_mask();
_editor.begin_reversible_selection_op(_("Add Note Selection"));
if (_view->num_selected_regionviews() == 0) { if (_view->num_selected_regionviews() == 0) {
_view->foreach_regionview ( _view->foreach_regionview (
sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection_region_view), sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection_region_view),
@ -1333,6 +1339,8 @@ MidiTimeAxisView::add_note_selection (uint8_t note)
sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection_region_view), sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection_region_view),
note, chn_mask)); note, chn_mask));
} }
_editor.commit_reversible_selection_op();
} }
void void
@ -1340,6 +1348,8 @@ MidiTimeAxisView::extend_note_selection (uint8_t note)
{ {
const uint16_t chn_mask = midi_track()->get_playback_channel_mask(); const uint16_t chn_mask = midi_track()->get_playback_channel_mask();
_editor.begin_reversible_selection_op(_("Extend Note Selection"));
if (_view->num_selected_regionviews() == 0) { if (_view->num_selected_regionviews() == 0) {
_view->foreach_regionview ( _view->foreach_regionview (
sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::extend_note_selection_region_view), sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::extend_note_selection_region_view),
@ -1349,6 +1359,8 @@ MidiTimeAxisView::extend_note_selection (uint8_t note)
sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::extend_note_selection_region_view), sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::extend_note_selection_region_view),
note, chn_mask)); note, chn_mask));
} }
_editor.commit_reversible_selection_op();
} }
void void
@ -1356,6 +1368,8 @@ MidiTimeAxisView::toggle_note_selection (uint8_t note)
{ {
const uint16_t chn_mask = midi_track()->get_playback_channel_mask(); const uint16_t chn_mask = midi_track()->get_playback_channel_mask();
_editor.begin_reversible_selection_op(_("Toggle Note Selection"));
if (_view->num_selected_regionviews() == 0) { if (_view->num_selected_regionviews() == 0) {
_view->foreach_regionview ( _view->foreach_regionview (
sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection_region_view), sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection_region_view),
@ -1365,6 +1379,15 @@ MidiTimeAxisView::toggle_note_selection (uint8_t note)
sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection_region_view), sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection_region_view),
note, chn_mask)); note, chn_mask));
} }
_editor.commit_reversible_selection_op();
}
void
MidiTimeAxisView::get_per_region_note_selection (list<pair<PBD::ID, set<boost::shared_ptr<Evoral::Note<Evoral::Beats> > > > >& selection)
{
_view->foreach_regionview (
sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::get_per_region_note_selection_region_view), sigc::ref(selection)));
} }
void void
@ -1391,6 +1414,24 @@ MidiTimeAxisView::toggle_note_selection_region_view (RegionView* rv, uint8_t not
dynamic_cast<MidiRegionView*>(rv)->toggle_matching_notes (note, chn_mask); dynamic_cast<MidiRegionView*>(rv)->toggle_matching_notes (note, chn_mask);
} }
void
MidiTimeAxisView::get_per_region_note_selection_region_view (RegionView* rv, list<pair<PBD::ID, std::set<boost::shared_ptr<Evoral::Note<Evoral::Beats> > > > > &selection)
{
Evoral::Sequence<Evoral::Beats>::Notes selected;
dynamic_cast<MidiRegionView*>(rv)->selection_as_notelist (selected, false);
std::set<boost::shared_ptr<Evoral::Note<Evoral::Beats> > > notes;
Evoral::Sequence<Evoral::Beats>::Notes::iterator sel_it;
for (sel_it = selected.begin(); sel_it != selected.end(); ++sel_it) {
notes.insert (*sel_it);
}
if (!notes.empty()) {
selection.push_back (make_pair ((rv)->region()->id(), notes));
}
}
void void
MidiTimeAxisView::set_channel_mode (ChannelMode, uint16_t) MidiTimeAxisView::set_channel_mode (ChannelMode, uint16_t)
{ {

View file

@ -30,6 +30,8 @@
#include <gtkmm2ext/selector.h> #include <gtkmm2ext/selector.h>
#include <list> #include <list>
#include "evoral/Note.hpp"
#include "ardour/types.h" #include "ardour/types.h"
#include "ardour/region.h" #include "ardour/region.h"
@ -65,7 +67,7 @@ class MidiChannelSelectorWindow;
class MidiTimeAxisView : public RouteTimeAxisView class MidiTimeAxisView : public RouteTimeAxisView
{ {
public: public:
MidiTimeAxisView (PublicEditor&, ARDOUR::Session*, ArdourCanvas::Canvas& canvas); MidiTimeAxisView (PublicEditor&, ARDOUR::Session*, ArdourCanvas::Canvas& canvas);
virtual ~MidiTimeAxisView (); virtual ~MidiTimeAxisView ();
@ -100,11 +102,13 @@ class MidiTimeAxisView : public RouteTimeAxisView
uint8_t get_channel_for_add () const; uint8_t get_channel_for_add () const;
protected: void get_per_region_note_selection (std::list<std::pair<PBD::ID, std::set<boost::shared_ptr<Evoral::Note<Evoral::Beats> > > > >&);
protected:
void start_step_editing (); void start_step_editing ();
void stop_step_editing (); void stop_step_editing ();
private: private:
sigc::signal<void, std::string, std::string> _midi_patch_settings_changed; sigc::signal<void, std::string, std::string> _midi_patch_settings_changed;
void model_changed(const std::string& model); void model_changed(const std::string& model);
@ -165,6 +169,7 @@ class MidiTimeAxisView : public RouteTimeAxisView
void add_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask); void add_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask);
void extend_note_selection_region_view (RegionView*, uint8_t note, uint16_t chn_mask); void extend_note_selection_region_view (RegionView*, uint8_t note, uint16_t chn_mask);
void toggle_note_selection_region_view (RegionView*, uint8_t note, uint16_t chn_mask); void toggle_note_selection_region_view (RegionView*, uint8_t note, uint16_t chn_mask);
void get_per_region_note_selection_region_view (RegionView*, std::list<std::pair<PBD::ID, std::set<boost::shared_ptr<Evoral::Note<Evoral::Beats> > > > >&);
void ensure_step_editor (); void ensure_step_editor ();

View file

@ -34,6 +34,7 @@
#include <gtkmm/actiongroup.h> #include <gtkmm/actiongroup.h>
#include <sigc++/signal.h> #include <sigc++/signal.h>
#include "evoral/Note.hpp"
#include "evoral/types.hpp" #include "evoral/types.hpp"
#include "pbd/statefuldestructible.h" #include "pbd/statefuldestructible.h"
@ -391,6 +392,8 @@ class PublicEditor : public Gtk::Window, public PBD::StatefulDestructible, publi
virtual void stop_canvas_autoscroll () = 0; virtual void stop_canvas_autoscroll () = 0;
virtual bool autoscroll_active() const = 0; virtual bool autoscroll_active() const = 0;
virtual void begin_reversible_selection_op (std::string cmd_name) = 0;
virtual void commit_reversible_selection_op () = 0;
virtual void begin_reversible_command (std::string cmd_name) = 0; virtual void begin_reversible_command (std::string cmd_name) = 0;
virtual void begin_reversible_command (GQuark) = 0; virtual void begin_reversible_command (GQuark) = 0;
virtual void commit_reversible_command () = 0; virtual void commit_reversible_command () = 0;
@ -412,7 +415,8 @@ class PublicEditor : public Gtk::Window, public PBD::StatefulDestructible, publi
virtual void get_regions_at (RegionSelection &, framepos_t where, TrackViewList const &) const = 0; virtual void get_regions_at (RegionSelection &, framepos_t where, TrackViewList const &) const = 0;
virtual RegionSelection get_regions_from_selection_and_mouse (framepos_t) = 0; virtual RegionSelection get_regions_from_selection_and_mouse (framepos_t) = 0;
virtual void get_regionviews_by_id (PBD::ID const & id, RegionSelection & regions) const = 0; virtual void get_regionviews_by_id (PBD::ID const id, RegionSelection & regions) const = 0;
virtual void get_per_region_note_selection (std::list<std::pair<PBD::ID, std::set<boost::shared_ptr<Evoral::Note<Evoral::Beats> > > > >&) const = 0;
/// Singleton instance, set up by Editor::Editor() /// Singleton instance, set up by Editor::Editor()

View file

@ -109,6 +109,7 @@ Selection::clear ()
clear_midi_notes (); clear_midi_notes ();
clear_midi_regions (); clear_midi_regions ();
clear_markers (); clear_markers ();
pending_midi_note_selection.clear();
} }
void void
@ -506,9 +507,9 @@ Selection::add (RegionView* r)
if (find (regions.begin(), regions.end(), r) == regions.end()) { if (find (regions.begin(), regions.end(), r) == regions.end()) {
bool changed = regions.add (r); bool changed = regions.add (r);
if (changed) { if (changed) {
RegionsChanged (); RegionsChanged ();
} }
} }
} }
@ -1229,12 +1230,45 @@ Selection::get_state () const
for (RegionSelection::const_iterator i = regions.begin(); i != regions.end(); ++i) { for (RegionSelection::const_iterator i = regions.begin(); i != regions.end(); ++i) {
XMLNode* r = node->add_child (X_("Region")); XMLNode* r = node->add_child (X_("Region"));
r->add_property (X_("id"), atoi ((*i)->region ()->id ().to_s ().c_str())); r->add_property (X_("id"), atoi ((*i)->region ()->id ().to_s ().c_str()));
}
/* midi region views have thir own internal selection. */
XMLNode* n;
list<pair<PBD::ID, std::set<boost::shared_ptr<Evoral::Note<Evoral::Beats> > > > > rid_notes;
editor->get_per_region_note_selection (rid_notes);
if (!rid_notes.empty()) {
n = node->add_child (X_("MIDINote"));
}
list<pair<PBD::ID, std::set<boost::shared_ptr<Evoral::Note<Evoral::Beats> > > > >::iterator rn_it;
for (rn_it = rid_notes.begin(); rn_it != rid_notes.end(); ++rn_it) {
n->add_property (X_("region_id"), atoi((*rn_it).first.to_s().c_str()));
for (std::set<boost::shared_ptr<Evoral::Note<Evoral::Beats> > >::iterator i = (*rn_it).second.begin(); i != (*rn_it).second.end(); ++i) {
XMLNode* nc = n->add_child(X_("note"));
snprintf(buf, sizeof(buf), "%d", (*i)->channel());
nc->add_property(X_("channel"), string(buf));
snprintf(buf, sizeof(buf), "%f", (*i)->time().to_double());
nc->add_property(X_("time"), string(buf));
snprintf(buf, sizeof(buf), "%d", (*i)->note());
nc->add_property(X_("note"), string(buf));
snprintf(buf, sizeof(buf), "%f", (*i)->length().to_double());
nc->add_property(X_("length"), string(buf));
snprintf(buf, sizeof(buf), "%d", (*i)->velocity());
nc->add_property(X_("velocity"), string(buf));
snprintf(buf, sizeof(buf), "%d", (*i)->off_velocity());
nc->add_property(X_("off-velocity"), string(buf));
}
} }
for (PointSelection::const_iterator i = points.begin(); i != points.end(); ++i) { for (PointSelection::const_iterator i = points.begin(); i != points.end(); ++i) {
AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (&(*i)->line().trackview); AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (&(*i)->line().trackview);
if (atv) { if (atv) {
XMLNode* r = node->add_child (X_("ControlPoint")); XMLNode* r = node->add_child (X_("ControlPoint"));
r->add_property (X_("type"), "track"); r->add_property (X_("type"), "track");
r->add_property (X_("route-id"), atoi (atv->parent_route()->id ().to_s ().c_str())); r->add_property (X_("route-id"), atoi (atv->parent_route()->id ().to_s ().c_str()));
@ -1243,6 +1277,7 @@ Selection::get_state () const
snprintf(buf, sizeof(buf), "%d", (*i)->view_index()); snprintf(buf, sizeof(buf), "%d", (*i)->view_index());
r->add_property (X_("view-index"), string(buf)); r->add_property (X_("view-index"), string(buf));
} }
} }
@ -1304,43 +1339,105 @@ Selection::set_state (XMLNode const & node, int)
add (rs); add (rs);
} else { } else {
/* /*
regionviews are being constructed - stash the region IDs regionviews haven't been constructed - stash the region IDs
so we can identify them in Editor::region_view_added () so we can identify them in Editor::region_view_added ()
*/ */
regions.pending.push_back (id); regions.pending.push_back (id);
} }
} else if ((*i)->name() == X_("MIDINote")) {
XMLProperty* prop_region_id = (*i)->property (X_("region-id"));
assert (prop_region_id);
PBD::ID const id (prop_region_id->value ());
RegionSelection rs;
editor->get_regionviews_by_id (id, rs); // there could be more than one
std::list<boost::shared_ptr<Evoral::Note<Evoral::Beats> > > notes;
XMLNodeList children = (*i)->children ();
for (XMLNodeList::const_iterator ci = children.begin(); ci != children.end(); ++ci) {
XMLProperty* prop_channel = (*ci)->property (X_("channel"));
XMLProperty* prop_time = (*ci)->property (X_("time"));
XMLProperty* prop_note = (*ci)->property (X_("note"));
XMLProperty* prop_length = (*ci)->property (X_("length"));
XMLProperty* prop_velocity = (*ci)->property (X_("velocity"));
XMLProperty* prop_off_velocity = (*ci)->property (X_("off-velocity"));
assert (prop_channel);
assert (prop_time);
assert (prop_note);
assert (prop_length);
assert (prop_velocity);
assert (prop_off_velocity);
uint8_t channel = atoi(prop_channel->value());
Evoral::Beats time (atof(prop_time->value()));
Evoral::Beats length (atof(prop_length->value()));
uint8_t note = atoi(prop_note->value());
uint8_t velocity = atoi(prop_velocity->value());
uint8_t off_velocity = atoi(prop_off_velocity->value());
boost::shared_ptr<Evoral::Note<Evoral::Beats> > the_note
(new Evoral::Note<Evoral::Beats> (channel, time, length, note, velocity));
the_note->set_off_velocity (off_velocity);
notes.push_back (the_note);
}
for (RegionSelection::iterator rsi = rs.begin(); rsi != rs.end(); ++rsi) {
MidiRegionView* mrv = dynamic_cast<MidiRegionView*> (*rsi);
if (mrv) {
mrv->select_notes(notes);
}
}
if (rs.empty()) {
/* regionviews containing these notes don't yet exist on the canvas.*/
pending_midi_note_selection.push_back (make_pair (id, notes));
}
} else if ((*i)->name() == X_("ControlPoint")) { } else if ((*i)->name() == X_("ControlPoint")) {
XMLProperty* prop_type = (*i)->property (X_("type")); XMLProperty* prop_type = (*i)->property (X_("type"));
XMLProperty* prop_route_id = (*i)->property (X_("route-id"));
XMLProperty* prop_alist_id = (*i)->property (X_("automation-list-id"));
XMLProperty* prop_parameter = (*i)->property (X_("parameter"));
XMLProperty* prop_view_index = (*i)->property (X_("view-index"));
assert (prop_type); assert(prop_type);
assert (prop_route_id);
assert (prop_alist_id);
assert (prop_parameter);
assert (prop_view_index);
if (prop_type->value () == "track") { if (prop_type->value () == "track") {
PBD::ID id (prop_route_id->value ());
RouteTimeAxisView* rtv = editor->get_route_view_by_route_id (id); XMLProperty* prop_route_id = (*i)->property (X_("route-id"));
XMLProperty* prop_alist_id = (*i)->property (X_("automation-list-id"));
XMLProperty* prop_parameter = (*i)->property (X_("parameter"));
XMLProperty* prop_view_index = (*i)->property (X_("view-index"));
assert (prop_type);
assert (prop_route_id);
assert (prop_alist_id);
assert (prop_parameter);
assert (prop_view_index);
PBD::ID route_id (prop_route_id->value ());
RouteTimeAxisView* rtv = editor->get_route_view_by_route_id (route_id);
vector <ControlPoint *> cps;
if (rtv) { if (rtv) {
boost::shared_ptr<AutomationTimeAxisView> atv = rtv->automation_child (EventTypeMap::instance().from_symbol (prop_parameter->value ())); boost::shared_ptr<AutomationTimeAxisView> atv = rtv->automation_child (EventTypeMap::instance().from_symbol (prop_parameter->value ()));
if (atv) { if (atv) {
list<boost::shared_ptr<AutomationLine> > lines = atv->lines(); list<boost::shared_ptr<AutomationLine> > lines = atv->lines();
for (list<boost::shared_ptr<AutomationLine> > ::iterator i = lines.begin(); i != lines.end(); ++i) { for (list<boost::shared_ptr<AutomationLine> > ::iterator li = lines.begin(); li != lines.end(); ++li) {
if ((*i)->the_list()->id() == prop_alist_id->value()) { if ((*li)->the_list()->id() == prop_alist_id->value()) {
ControlPoint* cp = (*i)->nth(atol(prop_view_index->value().c_str())); ControlPoint* cp = (*li)->nth(atol(prop_view_index->value().c_str()));
if (cp) { if (cp) {
add (cp); cps.push_back (cp);
cp->show();
} }
} }
} }
} }
} }
if (!cps.empty()) {
add (cps);
}
} }
} else if ((*i)->name() == X_("AudioRange")) { } else if ((*i)->name() == X_("AudioRange")) {

View file

@ -223,6 +223,8 @@ class Selection : public sigc::trackable, public PBD::ScopedConnectionList
PBD::Signal0<void> ClearMidiNoteSelection; PBD::Signal0<void> ClearMidiNoteSelection;
std::list<std::pair<PBD::ID const, std::list<boost::shared_ptr<Evoral::Note<Evoral::Beats> > > > > pending_midi_note_selection;
private: private:
PublicEditor const * editor; PublicEditor const * editor;
uint32_t next_time_id; uint32_t next_time_id;