X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=gtk2_ardour%2Fmidi_region_view.cc;h=324ca0eaf6201d289ac562265786fb9c10b3c7c4;hb=f5c717e893a7def1cee70238cc7b9f55e1ff0b0e;hp=4b0c9f54c438ab6722da19d5e4fdbc4fbfc46f22;hpb=3d599be991a71b7e4611b1c38ee88a77bf1c5460;p=ardour.git diff --git a/gtk2_ardour/midi_region_view.cc b/gtk2_ardour/midi_region_view.cc index 4b0c9f54c4..324ca0eaf6 100644 --- a/gtk2_ardour/midi_region_view.cc +++ b/gtk2_ardour/midi_region_view.cc @@ -76,11 +76,11 @@ #include "streamview.h" #include "patch_change_dialog.h" #include "verbose_cursor.h" -#include "ardour_ui.h" #include "note.h" #include "hit.h" #include "patch_change.h" #include "sys_ex.h" +#include "ui_config.h" #include "i18n.h" @@ -90,8 +90,6 @@ using namespace Editing; using namespace std; using Gtkmm2ext::Keyboard; -PBD::Signal1 MidiRegionView::SelectionCleared; - #define MIDI_BP_ZERO ((Config->get_first_midi_bank_is_zero())?0:1) MidiRegionView::MidiRegionView (ArdourCanvas::Container* parent, @@ -125,6 +123,7 @@ MidiRegionView::MidiRegionView (ArdourCanvas::Container* parent, , _last_event_y (0) , _grabbed_keyboard (false) , _entered (false) + , _note_entered (false) , _mouse_changed_selection (false) { CANVAS_DEBUG_NAME (_note_group, string_compose ("note group for %1", get_item_name())); @@ -133,11 +132,6 @@ MidiRegionView::MidiRegionView (ArdourCanvas::Container* parent, Config->ParameterChanged.connect (*this, invalidator (*this), boost::bind (&MidiRegionView::parameter_changed, this, _1), gui_context()); connect_to_diskstream (); - - SelectionCleared.connect (_selection_cleared_connection, invalidator (*this), boost::bind (&MidiRegionView::selection_cleared, this, _1), gui_context ()); - - PublicEditor& editor (trackview.editor()); - editor.get_selection().ClearMidiNoteSelection.connect (_clear_midi_selection_connection, invalidator (*this), boost::bind (&MidiRegionView::clear_midi_selection, this), gui_context ()); } MidiRegionView::MidiRegionView (ArdourCanvas::Container* parent, @@ -173,6 +167,7 @@ MidiRegionView::MidiRegionView (ArdourCanvas::Container* parent, , _last_event_y (0) , _grabbed_keyboard (false) , _entered (false) + , _note_entered (false) , _mouse_changed_selection (false) { CANVAS_DEBUG_NAME (_note_group, string_compose ("note group for %1", get_item_name())); @@ -181,11 +176,6 @@ MidiRegionView::MidiRegionView (ArdourCanvas::Container* parent, PublicEditor::DropDownKeys.connect (sigc::mem_fun (*this, &MidiRegionView::drop_down_keys)); connect_to_diskstream (); - - SelectionCleared.connect (_selection_cleared_connection, invalidator (*this), boost::bind (&MidiRegionView::selection_cleared, this, _1), gui_context ()); - - PublicEditor& editor (trackview.editor()); - editor.get_selection().ClearMidiNoteSelection.connect (_clear_midi_selection_connection, invalidator (*this), boost::bind (&MidiRegionView::clear_midi_selection, this), gui_context ()); } void @@ -226,6 +216,7 @@ MidiRegionView::MidiRegionView (const MidiRegionView& other) , _last_event_y (0) , _grabbed_keyboard (false) , _entered (false) + , _note_entered (false) , _mouse_changed_selection (false) { init (false); @@ -258,6 +249,7 @@ MidiRegionView::MidiRegionView (const MidiRegionView& other, boost::shared_ptrmidi_source(0)->mutex()); midi_region()->midi_source(0)->load_model(lm); @@ -283,12 +271,12 @@ MidiRegionView::init (bool wfd) RegionView::init (false); - set_height (trackview.current_height()); + //set_height (trackview.current_height()); region_muted (); region_sync_changed (); region_resized (ARDOUR::bounds_change); - region_locked (); + //region_locked (); set_colors (); @@ -320,11 +308,6 @@ MidiRegionView::init (bool wfd) Config->ParameterChanged.connect (*this, invalidator (*this), boost::bind (&MidiRegionView::parameter_changed, this, _1), gui_context()); connect_to_diskstream (); - - SelectionCleared.connect (_selection_cleared_connection, invalidator (*this), boost::bind (&MidiRegionView::selection_cleared, this, _1), gui_context ()); - - PublicEditor& editor (trackview.editor()); - editor.get_selection().ClearMidiNoteSelection.connect (_clear_midi_selection_connection, invalidator (*this), boost::bind (&MidiRegionView::clear_midi_selection, this), gui_context ()); } InstrumentInfo& @@ -434,13 +417,26 @@ MidiRegionView::mouse_mode_changed () set_frame_color(); if (_entered) { - if (trackview.editor().internal_editing()) { - // Switched in to internal editing mode while entered - enter_internal(); - } else { - // Switched out of internal editing mode while entered + if (!trackview.editor().internal_editing()) { + /* Switched out of internal editing mode while entered. + Only necessary for leave as a mouse_mode_change over a region + automatically triggers an enter event. */ leave_internal(); } + else if (trackview.editor().current_mouse_mode() == MouseContent) { + // hide cursor and ghost note after changing to internal edit mode + remove_ghost_note (); + + /* XXX This is problematic as the function is executed for every region + and only for one region _note_entered can be true. Still it's + necessary as to hide the verbose cursor when we're changing from + draw mode to internal edit mode. These lines are the reason why + in some situations no verbose cursor is shown when we enter internal + edit mode over a note. */ + if (!_note_entered) { + hide_verbose_cursor (); + } + } } } @@ -470,8 +466,9 @@ MidiRegionView::enter_internal() void MidiRegionView::leave_internal() { - trackview.editor().verbose_cursor()->hide (); + hide_verbose_cursor (); remove_ghost_note (); + _note_entered = false; if (_grabbed_keyboard) { Keyboard::magic_widget_drop_focus(); @@ -502,10 +499,10 @@ MidiRegionView::button_press (GdkEventButton* ev) } if (_mouse_state != SelectTouchDragging) { - + _pressed_button = ev->button; _mouse_state = Pressed; - + return true; } @@ -539,15 +536,14 @@ MidiRegionView::button_release (GdkEventButton* ev) switch (editor.current_mouse_mode()) { case MouseRange: - /* no motion occured - simple click */ - clear_selection (); + /* no motion occurred - simple click */ + clear_editor_note_selection (); _mouse_changed_selection = true; break; case MouseContent: case MouseTimeFX: { - clear_selection(); _mouse_changed_selection = true; if (Keyboard::is_insert_note_event(ev)) { @@ -559,13 +555,9 @@ MidiRegionView::button_release (GdkEventButton* ev) group->canvas_to_item (event_x, event_y); Evoral::Beats beats = get_grid_beats(editor.pixel_to_sample(event_x)); - - /* Shorten the length by 1 tick so that we can add a new note at the next - grid snap without it overlapping this one. - */ - beats -= Evoral::Beats::tick(); - create_note_at (editor.pixel_to_sample (event_x), event_y, beats, true); + } else { + clear_editor_note_selection (); } break; @@ -573,14 +565,7 @@ MidiRegionView::button_release (GdkEventButton* ev) case MouseDraw: { Evoral::Beats beats = get_grid_beats(editor.pixel_to_sample(event_x)); - - /* Shorten the length by 1 tick so that we can add a new note at the next - grid snap without it overlapping this one. - */ - beats -= Evoral::Beats::tick(); - create_note_at (editor.pixel_to_sample (event_x), event_y, beats, true); - break; } default: @@ -590,11 +575,12 @@ MidiRegionView::button_release (GdkEventButton* ev) _mouse_state = None; break; - case SelectRectDragging: case AddDragging: + /* Only create a ghost note when we added a note, not when we were drag-selecting. */ + create_ghost_note (ev->x, ev->y); + case SelectRectDragging: editor.drags()->end_grab ((GdkEvent *) ev); _mouse_state = None; - create_ghost_note (ev->x, ev->y); break; @@ -615,25 +601,33 @@ MidiRegionView::motion (GdkEventMotion* ev) { PublicEditor& editor = trackview.editor (); - if (!_ghost_note && editor.current_mouse_mode() == MouseContent && - Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier()) && - _mouse_state != AddDragging) { + if (!_note_entered) { - create_ghost_note (ev->x, ev->y); + if (!_ghost_note && editor.current_mouse_mode() == MouseContent && + Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier()) && + _mouse_state != AddDragging) { - } else if (_ghost_note && editor.current_mouse_mode() == MouseContent && - Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier())) { + create_ghost_note (ev->x, ev->y); - update_ghost_note (ev->x, ev->y); + } else if (_ghost_note && editor.current_mouse_mode() == MouseContent && + Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier())) { - } else if (_ghost_note && editor.current_mouse_mode() == MouseContent) { + update_ghost_note (ev->x, ev->y); - remove_ghost_note (); - editor.verbose_cursor()->hide (); + } else if (_ghost_note && editor.current_mouse_mode() == MouseContent) { - } else if (_ghost_note && editor.current_mouse_mode() == MouseDraw) { + remove_ghost_note (); + hide_verbose_cursor (); - update_ghost_note (ev->x, ev->y); + } else if (editor.current_mouse_mode() == MouseDraw) { + + if (_ghost_note) { + update_ghost_note (ev->x, ev->y); + } + else { + create_ghost_note (ev->x, ev->y); + } + } } /* any motion immediately hides velocity text that may have been visible */ @@ -646,19 +640,19 @@ MidiRegionView::motion (GdkEventMotion* ev) case Pressed: if (_pressed_button == 1) { - + MouseMode m = editor.current_mouse_mode(); - + if (m == MouseDraw || (m == MouseContent && Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier()))) { editor.drags()->set (new NoteCreateDrag (dynamic_cast (&editor), group, this), (GdkEvent *) ev); _mouse_state = AddDragging; remove_ghost_note (); - editor.verbose_cursor()->hide (); + hide_verbose_cursor (); return true; } else if (m == MouseContent) { editor.drags()->set (new MidiRubberbandSelectDrag (dynamic_cast (&editor), this), (GdkEvent *) ev); if (!Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) { - clear_selection (); + clear_editor_note_selection (); _mouse_changed_selection = true; } _mouse_state = SelectRectDragging; @@ -677,7 +671,7 @@ MidiRegionView::motion (GdkEventMotion* ev) case AddDragging: editor.drags()->motion_handler ((GdkEvent *) ev, false); break; - + case SelectTouchDragging: return false; @@ -686,7 +680,7 @@ MidiRegionView::motion (GdkEventMotion* ev) } - /* we may be dragging some non-note object (eg. patch-change, sysex) + /* we may be dragging some non-note object (eg. patch-change, sysex) */ return editor.drags()->motion_handler ((GdkEvent *) ev, false); @@ -700,17 +694,19 @@ MidiRegionView::scroll (GdkEventScroll* ev) return false; } - if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) { - /* XXX: bit of a hack; allow PrimaryModifier scroll through so that - it still works for zoom. + if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier) || + Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) { + /* XXX: bit of a hack; allow PrimaryModifier and TertiaryModifier scroll + * through so that it still works for navigation. */ return false; } - trackview.editor().verbose_cursor()->hide (); + hide_verbose_cursor (); bool fine = !Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier); - bool together = Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier); + Keyboard::ModifierMask mask_together(Keyboard::PrimaryModifier|Keyboard::TertiaryModifier); + bool together = Keyboard::modifier_state_contains (ev->state, mask_together); if (ev->direction == GDK_SCROLL_UP) { change_velocities (true, fine, false, together); @@ -733,13 +729,13 @@ MidiRegionView::key_press (GdkEventKey* ev) */ bool unmodified = Keyboard::no_modifier_keys_pressed (ev); - + if (unmodified && (ev->keyval == GDK_Alt_L || ev->keyval == GDK_Alt_R)) { _mouse_state = SelectTouchDragging; return true; } else if (ev->keyval == GDK_Escape && unmodified) { - clear_selection(); + clear_editor_note_selection (); _mouse_state = None; } else if (ev->keyval == GDK_comma || ev->keyval == GDK_period) { @@ -878,7 +874,7 @@ MidiRegionView::velocity_edit () if (_selection.empty()) { return; } - + /* pick a note somewhat at random (since Selection is a set<>) to * provide the "current" velocity for the dialog. */ @@ -957,7 +953,7 @@ MidiRegionView::create_note_at (framepos_t t, double y, Evoral::Beats length, bo start_note_diff_command(_("add note")); - clear_selection (); + clear_editor_note_selection (); note_diff_add_note (new_note, true, false); apply_diff(); @@ -966,9 +962,10 @@ MidiRegionView::create_note_at (framepos_t t, double y, Evoral::Beats length, bo } void -MidiRegionView::clear_events (bool with_selection_signal) +MidiRegionView::clear_events () { - clear_selection (with_selection_signal); + // clear selection without signaling + clear_selection_internal (); MidiGhostRegion* gr; for (std::vector::iterator g = ghosts.begin(); g != ghosts.end(); ++g) { @@ -995,7 +992,7 @@ MidiRegionView::display_model(boost::shared_ptr model) content_connection.disconnect (); _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 (false); + clear_events (); if (_enable_display) { redisplay_model(); @@ -1097,7 +1094,7 @@ MidiRegionView::abort_command() { delete _note_diff_command; _note_diff_command = 0; - clear_selection(); + clear_editor_note_selection(); } NoteBase* @@ -1191,13 +1188,12 @@ MidiRegionView::redisplay_model() bool visible; if (note_in_region_range (note, visible)) { - - if (!empty_when_starting && (cne = find_canvas_note (note)) != 0) { - cne->validate (); - update_note (cne); + if (!empty_when_starting && (cne = find_canvas_note (note)) != 0) { if (visible) { + cne->validate (); + update_note (cne); cne->show (); } else { cne->hide (); @@ -1216,7 +1212,7 @@ MidiRegionView::redisplay_model() } } else { - + if (!empty_when_starting && (cne = find_canvas_note (note)) != 0) { cne->validate (); cne->hide (); @@ -1229,17 +1225,17 @@ MidiRegionView::redisplay_model() if (!empty_when_starting) { for (Events::iterator i = _events.begin(); i != _events.end(); ) { if (!(*i)->valid ()) { - + for (vector::iterator j = ghosts.begin(); j != ghosts.end(); ++j) { MidiGhostRegion* gr = dynamic_cast (*j); if (gr) { gr->remove_note (*i); } } - + delete *i; i = _events.erase (i); - + } else { ++i; } @@ -1298,12 +1294,12 @@ MidiRegionView::display_sysexes() bool have_periodic_system_messages = false; bool display_periodic_messages = true; - if (!ARDOUR_UI::config()->get_never_display_periodic_midi()) { + if (!UIConfiguration::instance().get_never_display_periodic_midi()) { for (MidiModel::SysExes::const_iterator i = _model->sysexes().begin(); i != _model->sysexes().end(); ++i) { - const boost::shared_ptr > mev = + const boost::shared_ptr > mev = boost::static_pointer_cast > (*i); - + if (mev) { if (mev->is_spp() || mev->is_mtc_quarter() || mev->is_mtc_full()) { have_periodic_system_messages = true; @@ -1311,22 +1307,22 @@ MidiRegionView::display_sysexes() } } } - + if (have_periodic_system_messages) { double zoom = trackview.editor().get_current_zoom (); // frames per pixel - + /* get an approximate value for the number of samples per video frame */ - + double video_frame = trackview.session()->frame_rate() * (1.0/30); - + /* if we are zoomed out beyond than the cutoff (i.e. more * frames per pixel than frames per 4 video frames), don't * show periodic sysex messages. */ - + if (zoom > (video_frame*4)) { display_periodic_messages = false; - } + } } } else { display_periodic_messages = false; @@ -1334,7 +1330,7 @@ MidiRegionView::display_sysexes() for (MidiModel::SysExes::const_iterator i = _model->sysexes().begin(); i != _model->sysexes().end(); ++i) { - const boost::shared_ptr > mev = + const boost::shared_ptr > mev = boost::static_pointer_cast > (*i); Evoral::Beats time = (*i)->time(); @@ -1382,9 +1378,7 @@ MidiRegionView::~MidiRegionView () { in_destructor = true; - trackview.editor().verbose_cursor()->hide (); - - note_delete_connection.disconnect (); + hide_verbose_cursor (); delete _list_editor; @@ -1394,10 +1388,8 @@ MidiRegionView::~MidiRegionView () end_write(); } - _selection_cleared_connection.disconnect (); - _selection.clear(); - clear_events (false); + clear_events (); delete _note_group; delete _note_diff_command; @@ -1408,15 +1400,12 @@ MidiRegionView::~MidiRegionView () void MidiRegionView::region_resized (const PropertyChange& what_changed) { - RegionView::region_resized(what_changed); + RegionView::region_resized(what_changed); // calls RegionView::set_duration() if (what_changed.contains (ARDOUR::Properties::position)) { _region_relative_time_converter.set_origin_b(_region->position()); _region_relative_time_converter_double.set_origin_b(_region->position()); - set_duration(_region->length(), 0); - if (_enable_display) { - redisplay_model(); - } + /* reset_width dependent_items() redisplays model */ } if (what_changed.contains (ARDOUR::Properties::start) || @@ -1538,9 +1527,9 @@ MidiRegionView::add_ghost (TimeAxisView& tv) /* if ghost is inserted into midi track, use a dedicated midi ghost canvas group to allow having midi notes on top of note lines and waveforms. */ - ghost = new MidiGhostRegion (*mtv->midi_view(), trackview, unit_position); + ghost = new MidiGhostRegion (*this, *mtv->midi_view(), trackview, unit_position); } else { - ghost = new MidiGhostRegion (tv, trackview, unit_position); + ghost = new MidiGhostRegion (*this, tv, trackview, unit_position); } for (Events::iterator i = _events.begin(); i != _events.end(); ++i) { @@ -1551,8 +1540,6 @@ MidiRegionView::add_ghost (TimeAxisView& tv) ghost->set_duration (_region->length() / samples_per_pixel); ghosts.push_back (ghost); - GhostRegion::CatchDeletion.connect (*this, invalidator (*this), boost::bind (&RegionView::remove_ghost, this, _1), gui_context()); - return ghost; } @@ -1629,7 +1616,7 @@ MidiRegionView::extend_active_notes() void MidiRegionView::play_midi_note(boost::shared_ptr note) { - if (_no_sound_notes || !ARDOUR_UI::config()->get_sound_midi_notes()) { + if (_no_sound_notes || !UIConfiguration::instance().get_sound_midi_notes()) { return; } @@ -1656,7 +1643,7 @@ MidiRegionView::start_playing_midi_note(boost::shared_ptr note) void MidiRegionView::start_playing_midi_chord (vector > notes) { - if (_no_sound_notes || !ARDOUR_UI::config()->get_sound_midi_notes()) { + if (_no_sound_notes || !UIConfiguration::instance().get_sound_midi_notes()) { return; } @@ -1712,22 +1699,26 @@ void MidiRegionView::update_sustained (Note* ev, bool update_ghost_regions) { boost::shared_ptr note = ev->note(); - const double x = trackview.editor().sample_to_pixel (source_beats_to_region_frames (note->time())); + const double x0 = trackview.editor().sample_to_pixel (source_beats_to_region_frames (note->time())); + double x1; const double y0 = 1 + floor(midi_stream_view()->note_to_y(note->note())); - - ev->set_x0 (x); - ev->set_y0 (y0); + double y1; /* trim note display to not overlap the end of its region */ if (note->length() > 0) { const framepos_t note_end_frames = min (source_beats_to_region_frames (note->end_time()), _region->length()); - ev->set_x1 (trackview.editor().sample_to_pixel (note_end_frames)); + x1 = std::max(1., trackview.editor().sample_to_pixel (note_end_frames)) - 1; } else { - ev->set_x1 (trackview.editor().sample_to_pixel (_region->length())); + x1 = std::max(1., trackview.editor().sample_to_pixel (_region->length())) - 1; } - ev->set_y1 (y0 + std::max(1., floor(midi_stream_view()->note_height()) - 1)); + y1 = y0 + std::max(1., floor(midi_stream_view()->note_height()) - 1); + + ev->set_x0 (x0); + ev->set_x1 (x1); + ev->set_y0 (y0); + ev->set_y1 (y1); if (!note->length()) { if (_active_notes && note->note() < 128) { @@ -1735,7 +1726,7 @@ MidiRegionView::update_sustained (Note* ev, bool update_ghost_regions) if (old_rect) { /* There is an active note on this key, so we have a stuck note. Finish the old rectangle here. */ - old_rect->set_x1 (x); + old_rect->set_x1 (x1); old_rect->set_outline_all (); } _active_notes[note->note()] = ev; @@ -1751,8 +1742,8 @@ MidiRegionView::update_sustained (Note* ev, bool update_ghost_regions) } // Update color in case velocity has changed - ev->set_fill_color(ev->base_color()); - ev->set_outline_color(ev->calculate_outline(ev->base_color(), ev->selected())); + //ev->set_fill_color(ev->base_color()); + //ev->set_outline_color(ev->calculate_outline(ev->base_color(), ev->selected())); if (update_ghost_regions) { for (std::vector::iterator i = ghosts.begin(); i != ghosts.end(); ++i) { @@ -1889,7 +1880,7 @@ MidiRegionView::step_add_note (uint8_t channel, uint8_t number, uint8_t velocity start_note_diff_command (_("step add")); - clear_selection (); + clear_editor_note_selection (); note_diff_add_note (new_note, true, false); apply_diff(); @@ -1955,8 +1946,8 @@ patch_applies (const ARDOUR::MidiModel::constPatchChangePtr pc, Evoral::Beats ti { return pc->time() <= time && pc->channel() == channel; } - -void + +void MidiRegionView::get_patch_key_at (Evoral::Beats time, uint8_t channel, MIDI::Name::PatchPrimaryKey& key) const { // The earliest event not before time @@ -2098,7 +2089,7 @@ MidiRegionView::step_patch (PatchChange& patch, bool bank, int delta) } void -MidiRegionView::maybe_remove_deleted_note_from_selection (NoteBase* cne) +MidiRegionView::note_deleted (NoteBase* cne) { if (_selection.empty()) { return; @@ -2125,6 +2116,7 @@ MidiRegionView::delete_selection() _selection.clear(); apply_diff (); + hide_verbose_cursor (); } void @@ -2134,69 +2126,54 @@ MidiRegionView::delete_note (boost::shared_ptr n) _note_diff_command->remove (n); apply_diff (); - trackview.editor().verbose_cursor()->hide (); + hide_verbose_cursor (); } void -MidiRegionView::clear_selection_except (NoteBase* ev, bool signal) +MidiRegionView::clear_editor_note_selection () { - for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) { - if ((*i) != ev) { - Selection::iterator tmp = i; - ++tmp; + DEBUG_TRACE(DEBUG::Selection, "MRV::clear_editor_note_selection\n"); + PublicEditor& editor(trackview.editor()); + editor.get_selection().clear_midi_notes(); +} - (*i)->set_selected (false); - (*i)->hide_velocity (); - _selection.erase (i); +void +MidiRegionView::clear_selection () +{ + clear_selection_internal(); + PublicEditor& editor(trackview.editor()); + editor.get_selection().remove(this); +} - i = tmp; - } else { - ++i; - } +void +MidiRegionView::clear_selection_internal () +{ + DEBUG_TRACE(DEBUG::Selection, "MRV::clear_selection_internal\n"); + + for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) { + (*i)->set_selected(false); + (*i)->hide_velocity(); } + _selection.clear(); - if (!ev && _entered) { + if (_entered) { // Clearing selection entirely, ungrab keyboard Keyboard::magic_widget_drop_focus(); _grabbed_keyboard = false; } - - /* this does not change the status of this regionview w.r.t the editor - selection. - */ - - if (signal) { - SelectionCleared (this); /* EMIT SIGNAL */ - } } void MidiRegionView::unique_select(NoteBase* ev) { - const bool selection_was_empty = _selection.empty(); - - clear_selection_except (ev); - - /* don't bother with checking to see if we should remove this - regionview from the editor selection, since we're about to add - another note, and thus put/keep this regionview in the editor - selection anyway. - */ - - if (!ev->selected()) { - add_to_selection (ev); - if (selection_was_empty && _entered) { - // Grab keyboard for moving notes with arrow keys - Keyboard::magic_widget_grab_focus(); - _grabbed_keyboard = true; - } - } + clear_editor_note_selection(); + add_to_selection(ev); } void MidiRegionView::select_all_notes () { - clear_selection (); + clear_editor_note_selection (); for (Events::iterator i = _events.begin(); i != _events.end(); ++i) { add_to_selection (*i); @@ -2206,7 +2183,7 @@ MidiRegionView::select_all_notes () void MidiRegionView::select_range (framepos_t start, framepos_t end) { - clear_selection (); + clear_editor_note_selection (); for (Events::iterator i = _events.begin(); i != _events.end(); ++i) { framepos_t t = source_beats_to_absolute_frames((*i)->note()->time()); @@ -2254,13 +2231,13 @@ MidiRegionView::select_matching_notes (uint8_t notenum, uint16_t channel_mask, b uint8_t high_note = 0; MidiModel::Notes& notes (_model->notes()); _optimization_iterator = _events.begin(); - + if (extend && !have_selection) { extend = false; } /* scan existing selection to get note range */ - + for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) { if ((*i)->note()->note() < low_note) { low_note = (*i)->note()->note(); @@ -2269,9 +2246,9 @@ MidiRegionView::select_matching_notes (uint8_t notenum, uint16_t channel_mask, b high_note = (*i)->note()->note(); } } - + if (!add) { - clear_selection (); + clear_editor_note_selection (); if (!extend && (low_note == high_note) && (high_note == notenum)) { /* only note previously selected is the one we are @@ -2346,11 +2323,8 @@ void MidiRegionView::note_selected (NoteBase* ev, bool add, bool extend) { if (!add) { - clear_selection_except (ev); - if (!_selection.empty()) { - PublicEditor& editor (trackview.editor()); - editor.get_selection().add (this); - } + clear_editor_note_selection(); + add_to_selection (ev); } if (!extend) { @@ -2536,7 +2510,7 @@ MidiRegionView::move_selection(double dx, double dy, double cumulative_dy) (*i)->move_event(dx, dy); } - if (dy && !_selection.empty() && !_no_sound_notes && ARDOUR_UI::config()->get_sound_midi_notes()) { + if (dy && !_selection.empty() && !_no_sound_notes && UIConfiguration::instance().get_sound_midi_notes()) { if (to_play.size() > 1) { @@ -2592,7 +2566,7 @@ MidiRegionView::note_dropped(NoteBase *, frameoffset_t dt, int8_t dnote) start_note_diff_command (_("move notes")); for (Selection::iterator i = _selection.begin(); i != _selection.end() ; ++i) { - + framepos_t new_frames = source_beats_to_absolute_frames ((*i)->note()->time()) + dt; Evoral::Beats new_time = absolute_frames_to_source_beats (new_frames); @@ -2713,12 +2687,12 @@ MidiRegionView::begin_resizing (bool /*at_front*/) resize_data->note = note; // create a new SimpleRect from the note which will be the resize preview - ArdourCanvas::Rectangle *resize_rect = new ArdourCanvas::Rectangle (_note_group, + ArdourCanvas::Rectangle *resize_rect = new ArdourCanvas::Rectangle (_note_group, ArdourCanvas::Rect (note->x0(), note->y0(), note->x0(), note->y1())); // calculate the colors: get the color settings uint32_t fill_color = UINT_RGBA_CHANGE_A( - ARDOUR_UI::config()->color ("midi note selected"), + UIConfiguration::instance().color ("midi note selected"), 128); // make the resize preview notes more transparent and bright @@ -2731,7 +2705,7 @@ MidiRegionView::begin_resizing (bool /*at_front*/) 0.85)); resize_rect->set_outline_color (NoteBase::calculate_outline ( - ARDOUR_UI::config()->color ("midi note selected"))); + UIConfiguration::instance().color ("midi note selected"))); resize_data->resize_rect = resize_rect; _resize_data.push_back(resize_data); @@ -2754,6 +2728,7 @@ void MidiRegionView::update_resizing (NoteBase* primary, bool at_front, double delta_x, bool relative, double snap_delta, bool with_snap) { bool cursor_set = false; + bool const ensure_snap = trackview.editor().snap_mode () != SnapMagnetic; for (std::vector::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) { ArdourCanvas::Rectangle* resize_rect = (*i)->resize_rect; @@ -2785,14 +2760,14 @@ MidiRegionView::update_resizing (NoteBase* primary, bool at_front, double delta_ if (at_front) { if (with_snap) { - resize_rect->set_x0 (snap_to_pixel(current_x, true) - snap_delta); + resize_rect->set_x0 (snap_to_pixel (current_x, ensure_snap) - snap_delta); } else { resize_rect->set_x0 (current_x - snap_delta); } resize_rect->set_x1 (canvas_note->x1()); } else { if (with_snap) { - resize_rect->set_x1 (snap_to_pixel(current_x, true) - snap_delta); + resize_rect->set_x1 (snap_to_pixel (current_x, ensure_snap) - snap_delta); } else { resize_rect->set_x1 (current_x - snap_delta); } @@ -2800,30 +2775,31 @@ MidiRegionView::update_resizing (NoteBase* primary, bool at_front, double delta_ } if (!cursor_set) { - /* snap delta is in pixels (sigh) */ - framepos_t delta_samps = trackview.editor().pixel_to_sample (snap_delta); - double delta_beats; + /* Convert snap delta from pixels to beats. */ + framepos_t snap_delta_samps = trackview.editor().pixel_to_sample (snap_delta); + double snap_delta_beats = 0.0; int sign = 1; + /* negative beat offsets aren't allowed */ - if (delta_samps > 0) { - delta_beats = region_frames_to_region_beats_double (delta_samps); - } else if (delta_samps < 0) { - delta_beats = region_frames_to_region_beats_double ( - delta_samps); + if (snap_delta_samps > 0) { + snap_delta_beats = region_frames_to_region_beats_double (snap_delta_samps); + } else if (snap_delta_samps < 0) { + snap_delta_beats = region_frames_to_region_beats_double ( - snap_delta_samps); sign = -1; } - const double snapped_x = (with_snap ? snap_pixel_to_sample (current_x, true) : trackview.editor ().pixel_to_sample (current_x)); + const double snapped_x = (with_snap ? snap_pixel_to_sample (current_x, ensure_snap) : trackview.editor ().pixel_to_sample (current_x)); Evoral::Beats beats = region_frames_to_region_beats (snapped_x); Evoral::Beats len = Evoral::Beats(); if (at_front) { if (beats < canvas_note->note()->end_time()) { - len = canvas_note->note()->time() - beats + (sign * delta_beats); + len = canvas_note->note()->time() - beats + (sign * snap_delta_beats); len += canvas_note->note()->length(); } } else { if (beats >= canvas_note->note()->time()) { - len = beats - canvas_note->note()->time() - (sign * delta_beats); + len = beats - canvas_note->note()->time() - (sign * snap_delta_beats); } } @@ -2848,6 +2824,9 @@ MidiRegionView::commit_resizing (NoteBase* primary, bool at_front, double delta_ { _note_diff_command = _model->new_note_diff_command (_("resize notes")); + /* XX why doesn't snap_pixel_to_sample() handle this properly? */ + bool const ensure_snap = trackview.editor().snap_mode () != SnapMagnetic; + for (std::vector::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) { Note* canvas_note = (*i)->note; ArdourCanvas::Rectangle* resize_rect = (*i)->resize_rect; @@ -2855,7 +2834,7 @@ MidiRegionView::commit_resizing (NoteBase* primary, bool at_front, double delta_ /* Get the new x position for this resize, which is in pixels relative * to the region position. */ - + double current_x; if (at_front) { @@ -2878,9 +2857,12 @@ MidiRegionView::commit_resizing (NoteBase* primary, bool at_front, double delta_ if (current_x > trackview.editor().sample_to_pixel(_region->length())) { current_x = trackview.editor().sample_to_pixel(_region->length()); } + + /* Convert snap delta from pixels to beats with sign. */ framepos_t snap_delta_samps = trackview.editor().pixel_to_sample (snap_delta); - double snap_delta_beats; + double snap_delta_beats = 0.0; int sign = 1; + if (snap_delta_samps > 0) { snap_delta_beats = region_frames_to_region_beats_double (snap_delta_samps); } else if (snap_delta_samps < 0) { @@ -2888,8 +2870,13 @@ MidiRegionView::commit_resizing (NoteBase* primary, bool at_front, double delta_ sign = -1; } - /* Convert that to a frame within the source */ - const framepos_t current_fr = snap_pixel_to_sample (current_x, with_snap) + _region->start (); + /* Convert the new x position to a frame within the source */ + framepos_t current_fr; + if (with_snap) { + current_fr = snap_pixel_to_sample (current_x, ensure_snap) + _region->start (); + } else { + current_fr = trackview.editor().pixel_to_sample (current_x) + _region->start (); + } /* and then to beats */ const Evoral::Beats x_beats = region_frames_to_region_beats (current_fr); @@ -3241,7 +3228,7 @@ MidiRegionView::nudge_notes (bool forward, bool fine) if (!fine) { /* non-fine, move by 1 bar regardless of snap */ - delta = Evoral::Beats(trackview.session()->tempo_map().meter_at(ref_point).divisions_per_bar()); + delta = Evoral::Beats(trackview.session()->tempo_map().meter_at_frame (ref_point).divisions_per_bar()); } else if (trackview.editor().snap_mode() == Editing::SnapOff) { @@ -3308,13 +3295,22 @@ MidiRegionView::change_channel(uint8_t channel) void MidiRegionView::note_entered(NoteBase* ev) { + _note_entered = true; + Editor* editor = dynamic_cast(&trackview.editor()); if (_mouse_state == SelectTouchDragging) { + note_selected (ev, true); + } else if (editor->current_mouse_mode() == MouseContent) { + + remove_ghost_note (); show_verbose_cursor (ev->note ()); + } else if (editor->current_mouse_mode() == MouseDraw) { + + remove_ghost_note (); show_verbose_cursor (ev->note ()); } } @@ -3322,13 +3318,13 @@ MidiRegionView::note_entered(NoteBase* ev) void MidiRegionView::note_left (NoteBase*) { - Editor* editor = dynamic_cast(&trackview.editor()); + _note_entered = false; for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) { (*i)->hide_velocity (); } - editor->verbose_cursor()->hide (); + hide_verbose_cursor (); } void @@ -3336,8 +3332,8 @@ MidiRegionView::patch_entered (PatchChange* p) { ostringstream s; /* XXX should get patch name if we can */ - s << _("Bank ") << (p->patch()->bank() + MIDI_BP_ZERO) << '\n' - << _("Program ") << ((int) p->patch()->program()) + MIDI_BP_ZERO << '\n' + s << _("Bank ") << (p->patch()->bank() + MIDI_BP_ZERO) << '\n' + << _("Program ") << ((int) p->patch()->program()) + MIDI_BP_ZERO << '\n' << _("Channel ") << ((int) p->patch()->channel() + 1); show_verbose_cursor (s.str(), 10, 20); p->item().grab_focus(); @@ -3346,7 +3342,7 @@ MidiRegionView::patch_entered (PatchChange* p) void MidiRegionView::patch_left (PatchChange *) { - trackview.editor().verbose_cursor()->hide (); + hide_verbose_cursor (); /* focus will transfer back via the enter-notify event sent to this * midi region view. */ @@ -3366,7 +3362,7 @@ MidiRegionView::sysex_entered (SysEx* p) void MidiRegionView::sysex_left (SysEx *) { - trackview.editor().verbose_cursor()->hide (); + hide_verbose_cursor (); /* focus will transfer back via the enter-notify event sent to this * midi region view. */ @@ -3398,12 +3394,12 @@ MidiRegionView::get_fill_color() const trackview.editor().internal_editing() ? "editable region" : "midi frame base"); if (_selected) { - return ARDOUR_UI::config()->color_mod ("selected region base", mod_name); - } else if ((!ARDOUR_UI::config()->get_show_name_highlight() || high_enough_for_name) && - !ARDOUR_UI::config()->get_color_regions_using_track_color()) { - return ARDOUR_UI::config()->color_mod ("midi frame base", mod_name); + return UIConfiguration::instance().color_mod ("selected region base", mod_name); + } else if ((!UIConfiguration::instance().get_show_name_highlight() || high_enough_for_name) && + !UIConfiguration::instance().get_color_regions_using_track_color()) { + return UIConfiguration::instance().color_mod ("midi frame base", mod_name); } - return ARDOUR_UI::config()->color_mod (fill_color, mod_name); + return UIConfiguration::instance().color_mod (fill_color, mod_name); } void @@ -3542,7 +3538,7 @@ MidiRegionView::paste_internal (framepos_t pos, unsigned paste_count, float time duration, pos, _region->position(), pos_beats)); - clear_selection (); + clear_editor_note_selection (); for (int n = 0; n < (int) times; ++n) { @@ -3550,6 +3546,7 @@ MidiRegionView::paste_internal (framepos_t pos, unsigned paste_count, float time boost::shared_ptr copied_note (new NoteType (*((*i).get()))); copied_note->set_time (pos_beats + copied_note->time() - first_time); + copied_note->set_id (Evoral::next_event_id()); /* make all newly added notes selected */ @@ -3702,7 +3699,7 @@ MidiRegionView::update_ghost_note (double x, double y) _note_group->canvas_to_item (x, y); PublicEditor& editor = trackview.editor (); - + framepos_t const unsnapped_frame = editor.pixel_to_sample (x); framecnt_t grid_frames; framepos_t const f = snap_frame_to_grid_underneath (unsnapped_frame, grid_frames); @@ -3751,6 +3748,16 @@ MidiRegionView::remove_ghost_note () _ghost_note = 0; } +void +MidiRegionView::hide_verbose_cursor () +{ + trackview.editor().verbose_cursor()->hide (); + MidiTimeAxisView* mtv = dynamic_cast(&trackview); + if (mtv) { + mtv->set_note_highlight (NO_MIDI_NOTE); + } +} + void MidiRegionView::snap_changed () { @@ -3819,6 +3826,7 @@ MidiRegionView::color_handler () void MidiRegionView::enable_display (bool yn) { + RegionView::enable_display (yn); if (yn) { redisplay_model (); @@ -3990,14 +3998,13 @@ MidiRegionView::delete_sysex (SysEx* /*sysex*/) // display_sysexes(); } -void -MidiRegionView::show_verbose_cursor (boost::shared_ptr n) const +std::string +MidiRegionView::get_note_name (boost::shared_ptr n, uint8_t note_value) const { using namespace MIDI::Name; - std::string name; - MidiTimeAxisView* const mtv = dynamic_cast(&trackview); + MidiTimeAxisView* mtv = dynamic_cast(&trackview); if (mtv) { boost::shared_ptr device_names(mtv->get_device_names()); if (device_names) { @@ -4007,18 +4014,36 @@ MidiRegionView::show_verbose_cursor (boost::shared_ptr n) const n->channel(), patch_key.bank(), patch_key.program(), - n->note()); + note_value); } } char buf[128]; snprintf (buf, sizeof (buf), "%d %s\nCh %d Vel %d", - (int) n->note (), - name.empty() ? Evoral::midi_note_name (n->note()).c_str() : name.c_str(), + (int) note_value, + name.empty() ? Evoral::midi_note_name (note_value).c_str() : name.c_str(), (int) n->channel() + 1, (int) n->velocity()); - show_verbose_cursor(buf, 10, 20); + return buf; +} + +void +MidiRegionView::show_verbose_cursor_for_new_note_value(boost::shared_ptr current_note, + uint8_t new_value) const +{ + MidiTimeAxisView* mtv = dynamic_cast(&trackview); + if (mtv) { + mtv->set_note_highlight (new_value); + } + + show_verbose_cursor(get_note_name(current_note, new_value), 10, 20); +} + +void +MidiRegionView::show_verbose_cursor (boost::shared_ptr n) const +{ + show_verbose_cursor_for_new_note_value(n, n->note()); } void @@ -4064,10 +4089,10 @@ framepos_t MidiRegionView::snap_frame_to_grid_underneath (framepos_t p, framecnt_t& grid_frames) const { PublicEditor& editor = trackview.editor (); - + const Evoral::Beats p_beat = region_frames_to_region_beats (p); const Evoral::Beats grid_beats = get_grid_beats(p); - grid_frames = region_beats_to_region_frames (grid_beats); + grid_frames = region_beats_to_region_frames (p_beat + grid_beats) - region_beats_to_region_frames (p_beat); /* Hack so that we always snap to the note that we are over, instead of snapping to the next one if we're more than halfway through the one we're over. @@ -4079,20 +4104,6 @@ MidiRegionView::snap_frame_to_grid_underneath (framepos_t p, framecnt_t& grid_fr return snap_frame_to_frame (p); } -/** Called when the selection has been cleared in any MidiRegionView. - * @param rv MidiRegionView that the selection was cleared in. - */ -void -MidiRegionView::selection_cleared (MidiRegionView* rv) -{ - if (rv == this) { - return; - } - - /* Clear our selection in sympathy; but don't signal the fact */ - clear_selection (false); -} - ChannelMode MidiRegionView::get_channel_mode () const { @@ -4113,7 +4124,7 @@ MidiRegionView::get_grid_beats(framepos_t pos) const { PublicEditor& editor = trackview.editor(); bool success = false; - Evoral::Beats beats = editor.get_grid_type_as_beats(success, pos); + Evoral::Beats beats = editor.get_grid_type_as_beats (success, pos); if (!success) { beats = Evoral::Beats(1); }