#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"
+#include "pbd/i18n.h"
using namespace ARDOUR;
using namespace PBD;
using namespace std;
using Gtkmm2ext::Keyboard;
-PBD::Signal1<void, MidiRegionView *> MidiRegionView::SelectionCleared;
-
#define MIDI_BP_ZERO ((Config->get_first_midi_bank_is_zero())?0:1)
MidiRegionView::MidiRegionView (ArdourCanvas::Container* parent,
, _current_range_max(0)
, _region_relative_time_converter(r->session().tempo_map(), r->position())
, _source_relative_time_converter(r->session().tempo_map(), r->position() - r->start())
+ , _region_relative_time_converter_double(r->session().tempo_map(), r->position())
, _active_notes(0)
, _note_group (new ArdourCanvas::Container (group))
, _note_diff_command (0)
, _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()));
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,
, _current_range_max(0)
, _region_relative_time_converter(r->session().tempo_map(), r->position())
, _source_relative_time_converter(r->session().tempo_map(), r->position() - r->start())
+ , _region_relative_time_converter_double(r->session().tempo_map(), r->position())
, _active_notes(0)
, _note_group (new ArdourCanvas::Container (group))
, _note_diff_command (0)
, _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()));
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
, _current_range_max(0)
, _region_relative_time_converter(other.region_relative_time_converter())
, _source_relative_time_converter(other.source_relative_time_converter())
+ , _region_relative_time_converter_double(other.region_relative_time_converter_double())
, _active_notes(0)
, _note_group (new ArdourCanvas::Container (get_canvas_group()))
, _note_diff_command (0)
, _last_event_y (0)
, _grabbed_keyboard (false)
, _entered (false)
+ , _note_entered (false)
, _mouse_changed_selection (false)
{
init (false);
, _current_range_max(0)
, _region_relative_time_converter(other.region_relative_time_converter())
, _source_relative_time_converter(other.source_relative_time_converter())
+ , _region_relative_time_converter_double(other.region_relative_time_converter_double())
, _active_notes(0)
, _note_group (new ArdourCanvas::Container (get_canvas_group()))
, _note_diff_command (0)
, _last_event_y (0)
, _grabbed_keyboard (false)
, _entered (false)
+ , _note_entered (false)
, _mouse_changed_selection (false)
{
init (true);
{
PublicEditor::DropDownKeys.connect (sigc::mem_fun (*this, &MidiRegionView::drop_down_keys));
- NoteBase::NoteBaseDeleted.connect (note_delete_connection, MISSING_INVALIDATOR,
- boost::bind (&MidiRegionView::maybe_remove_deleted_note_from_selection, this, _1),
- gui_context());
-
if (wfd) {
Glib::Threads::Mutex::Lock lm(midi_region()->midi_source(0)->mutex());
midi_region()->midi_source(0)->load_model(lm);
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 ();
group->raise_to_top();
- midi_view()->midi_track()->PlaybackChannelModeChanged.connect (_channel_mode_changed_connection, invalidator (*this),
+ midi_view()->midi_track()->playback_filter().ChannelModeChanged.connect (_channel_mode_changed_connection, invalidator (*this),
boost::bind (&MidiRegionView::midi_channel_mode_changed, this),
gui_context ());
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&
case GDK_BUTTON_RELEASE:
r = button_release (&ev->button);
- _note_player.reset();
return r;
case GDK_MOTION_NOTIFY:
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 ();
+ }
+ }
}
}
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();
}
if (_mouse_state != SelectTouchDragging) {
-
+
_pressed_button = ev->button;
_mouse_state = Pressed;
-
+
return true;
}
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)) {
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;
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:
_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;
}
if (_mouse_changed_selection) {
- trackview.editor().begin_reversible_selection_op (_("Mouse Selection Change"));
+ trackview.editor().begin_reversible_selection_op (X_("Mouse Selection Change"));
trackview.editor().commit_reversible_selection_op ();
}
{
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 */
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 *> (&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 *> (&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;
case AddDragging:
editor.drags()->motion_handler ((GdkEvent *) ev, false);
break;
-
+
case SelectTouchDragging:
return false;
}
- /* 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);
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);
*/
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) {
} else if (ev->keyval == GDK_Tab || ev->keyval == GDK_ISO_Left_Tab) {
- trackview.editor().begin_reversible_selection_op (_("Select Adjacent Note"));
+ trackview.editor().begin_reversible_selection_op (X_("Select Adjacent Note"));
if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) {
goto_previous_note (Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier));
if (_selection.empty()) {
return;
}
-
+
/* pick a note somewhat at random (since Selection is a set<>) to
* provide the "current" velocity for the dialog.
*/
MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
MidiStreamView* const view = mtv->midi_view();
+ boost::shared_ptr<MidiRegion> mr = boost::dynamic_pointer_cast<MidiRegion> (_region);
+
+ if (!mr) {
+ return;
+ }
// Start of note in frames relative to region start
+ uint32_t divisions = 0;
+
if (snap_t) {
framecnt_t grid_frames;
t = snap_frame_to_grid_underneath (t, grid_frames);
+ divisions = trackview.editor().get_grid_music_divisions (0);
}
- const MidiModel::TimeType beat_time = region_frames_to_region_beats(
- t + _region->start());
+ const MidiModel::TimeType beat_time = Evoral::Beats (trackview.session()->tempo_map().exact_beat_at_frame (_region->position() + t, divisions)
+ - (mr->beat() - mr->start_beats().to_double()));
const double note = view->y_to_note(y);
const uint8_t chan = mtv->get_channel_for_add();
const uint8_t velocity = get_velocity_for_add(beat_time);
start_note_diff_command(_("add note"));
- clear_selection ();
+ clear_editor_note_selection ();
note_diff_add_note (new_note, true, false);
apply_diff();
}
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<GhostRegion*>::iterator g = ghosts.begin(); g != ghosts.end(); ++g) {
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();
{
delete _note_diff_command;
_note_diff_command = 0;
- clear_selection();
+ clear_editor_note_selection();
}
NoteBase*
_optimization_iterator = _events.begin();
bool empty_when_starting = _events.empty();
+ NoteBase* cne;
for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
boost::shared_ptr<NoteType> note (*n);
- NoteBase* cne;
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 ();
}
} else {
-
+
if (!empty_when_starting && (cne = find_canvas_note (note)) != 0) {
cne->validate ();
cne->hide ();
if (!empty_when_starting) {
for (Events::iterator i = _events.begin(); i != _events.end(); ) {
if (!(*i)->valid ()) {
-
+
for (vector<GhostRegion*>::iterator j = ghosts.begin(); j != ghosts.end(); ++j) {
MidiGhostRegion* gr = dynamic_cast<MidiGhostRegion*> (*j);
if (gr) {
gr->remove_note (*i);
}
}
-
+
delete *i;
i = _events.erase (i);
-
+
} else {
++i;
}
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<const Evoral::MIDIEvent<Evoral::Beats> > mev =
+ const boost::shared_ptr<const Evoral::MIDIEvent<Evoral::Beats> > mev =
boost::static_pointer_cast<const Evoral::MIDIEvent<Evoral::Beats> > (*i);
-
+
if (mev) {
if (mev->is_spp() || mev->is_mtc_quarter() || mev->is_mtc_full()) {
have_periodic_system_messages = true;
}
}
}
-
+
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;
for (MidiModel::SysExes::const_iterator i = _model->sysexes().begin(); i != _model->sysexes().end(); ++i) {
- const boost::shared_ptr<const Evoral::MIDIEvent<Evoral::Beats> > mev =
+ const boost::shared_ptr<const Evoral::MIDIEvent<Evoral::Beats> > mev =
boost::static_pointer_cast<const Evoral::MIDIEvent<Evoral::Beats> > (*i);
Evoral::Beats time = (*i)->time();
{
in_destructor = true;
- trackview.editor().verbose_cursor()->hide ();
-
- note_delete_connection.disconnect ();
+ hide_verbose_cursor ();
delete _list_editor;
end_write();
}
- _selection_cleared_connection.disconnect ();
-
_selection.clear();
- clear_events (false);
+ clear_events ();
delete _note_group;
delete _note_diff_command;
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());
- set_duration(_region->length(), 0);
- if (_enable_display) {
- redisplay_model();
- }
+ _region_relative_time_converter_double.set_origin_b(_region->position());
+ /* reset_width dependent_items() redisplays model */
+
}
if (what_changed.contains (ARDOUR::Properties::start) ||
what_changed.contains (ARDOUR::Properties::position)) {
_source_relative_time_converter.set_origin_b (_region->position() - _region->start());
}
+ /* catch an end trim so we can live update */
+ if (!what_changed.contains (ARDOUR::Properties::start) &&
+ what_changed.contains (ARDOUR::Properties::length)) {
+ enable_display (true);
+ }
}
void
if (_enable_display) {
redisplay_model();
- }
+ }
for (PatchChanges::iterator x = _patch_changes.begin(); x != _patch_changes.end(); ++x) {
if ((*x)->canvas_item()->width() >= _pixel_width) {
const double y0 = 1. + floor (midi_stream_view()->note_to_y(note->note()));
const double y1 = y0 + std::max(1., floor(midi_stream_view()->note_height()) - 1.);
+ if (y0 < 0 || y1 >= _height) {
+ /* During DnD, the region uses the 'old/current'
+ * midi_stream_view()'s range and its position/height calculation.
+ *
+ * Ideally DnD would decouple the midi_stream_view() for the
+ * region(s) being dragged and set it to the target's range
+ * (or in case of the drop-zone, FullRange).
+ * but I don't see how this can be done without major rework.
+ *
+ * For now, just prevent visual bleeding of events in case
+ * the target-track is smaller.
+ */
+ event->hide();
+ continue;
+ }
cnote->set_y0 (y0);
cnote->set_y1 (y1);
/* 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) {
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;
}
void
MidiRegionView::play_midi_note(boost::shared_ptr<NoteType> note)
{
- if (_no_sound_notes || !ARDOUR_UI::config()->get_sound_midi_notes()) {
+ if (_no_sound_notes || !UIConfiguration::instance().get_sound_midi_notes()) {
return;
}
void
MidiRegionView::start_playing_midi_chord (vector<boost::shared_ptr<NoteType> > notes)
{
- if (_no_sound_notes || !ARDOUR_UI::config()->get_sound_midi_notes()) {
+ if (_no_sound_notes || !UIConfiguration::instance().get_sound_midi_notes()) {
return;
}
return;
}
- _note_player = boost::shared_ptr<NotePlayer>(new NotePlayer(route_ui->midi_track()));
+ NotePlayer* player = new NotePlayer (route_ui->midi_track());
for (vector<boost::shared_ptr<NoteType> >::iterator n = notes.begin(); n != notes.end(); ++n) {
- _note_player->add (*n);
+ player->add (*n);
}
- _note_player->on ();
+ player->play ();
}
bool
MidiRegionView::note_in_region_range (const boost::shared_ptr<NoteType> note, bool& visible) const
{
- /* This is imprecise due to all the conversion conversion involved, so only
- hide notes if they seem to start more than one tick before the start. */
- const framecnt_t tick_frames = Evoral::Beats::tick().to_ticks(trackview.session()->frame_rate());
- const framepos_t note_start_frames = source_beats_to_region_frames (note->time());
- const bool outside = ((note_start_frames <= -tick_frames) ||
- (note_start_frames >= _region->length()));
+ const boost::shared_ptr<ARDOUR::MidiRegion> midi_reg = midi_region();
+ const bool outside = (note->time() < midi_reg->start_beats() ||
+ note->time() > midi_reg->start_beats() + midi_reg->length_beats());
visible = (note->note() >= midi_stream_view()->lowest_note()) &&
(note->note() <= midi_stream_view()->highest_note());
void
MidiRegionView::update_sustained (Note* ev, bool update_ghost_regions)
{
+ TempoMap& map (trackview.session()->tempo_map());
+ const boost::shared_ptr<ARDOUR::MidiRegion> mr = midi_region();
boost::shared_ptr<NoteType> note = ev->note();
- const double x = trackview.editor().sample_to_pixel (source_beats_to_region_frames (note->time()));
+ const framepos_t note_start_frames = map.frame_at_beat (_region->beat() - mr->start_beats().to_double()
+ + note->time().to_double()) - _region->position();
+
+ const double x0 = trackview.editor().sample_to_pixel (note_start_frames);
+ double x1;
const double y0 = 1 + floor(midi_stream_view()->note_to_y(note->note()));
+ double y1;/* trim note display to not overlap the end of its region */
- ev->set_x0 (x);
- ev->set_y0 (y0);
+ if (note->length() > 0) {
+ Evoral::Beats note_end_time = note->end_time();
- /* trim note display to not overlap the end of its region */
+ if (note->end_time() > mr->start_beats() + mr->length_beats()) {
+ note_end_time = mr->start_beats() + mr->length_beats();
+ }
- 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));
+ const framepos_t note_end_frames = map.frame_at_beat (_region->beat() - mr->start_beats().to_double()
+ + note_end_time.to_double()) - _region->position();
+
+ 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);
+
+ ArdourCanvas::Rect rect (x0, y0, x1, y1);
+ ev->set (rect);
if (!note->length()) {
if (_active_notes && note->note() < 128) {
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;
}
// 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()));
+ //const uint32_t base_col = ev->base_color();
+ //ev->set_fill_color(base_col);
+ //ev->set_outline_color(ev->calculate_outline(base_col, ev->selected()));
if (update_ghost_regions) {
for (std::vector<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
{
boost::shared_ptr<NoteType> note = ev->note();
- const framepos_t note_start_frames = source_beats_to_region_frames(note->time());
+ const framepos_t note_start_frames = trackview.session()->tempo_map().frame_at_beat (_region->beat() - midi_region()->start_beats().to_double()
+ + note->time().to_double()) - _region->position();
const double x = trackview.editor().sample_to_pixel(note_start_frames);
const double diamond_size = std::max(1., floor(midi_stream_view()->note_height()) - 2.);
const double y = 1.5 + floor(midi_stream_view()->note_to_y(note->note())) + diamond_size * .5;
+ // see DnD note in MidiRegionView::apply_note_range() above
+ if (y <= 0 || y >= _height) {
+ ev->hide();
+ } else {
+ ev->show();
+ }
+
ev->set_position (ArdourCanvas::Duple (x, y));
ev->set_height (diamond_size);
framepos_t region_end = _region->last_frame();
if (end_frame > region_end) {
- _region->set_length (end_frame - _region->position());
+ /* XX sets length in beats from audio space. make musical */
+ _region->set_length (end_frame - _region->position(), 0);
}
MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
start_note_diff_command (_("step add"));
- clear_selection ();
+ clear_editor_note_selection ();
note_diff_add_note (new_note, true, false);
apply_diff();
{
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
}
void
-MidiRegionView::maybe_remove_deleted_note_from_selection (NoteBase* cne)
+MidiRegionView::note_deleted (NoteBase* cne)
{
if (_selection.empty()) {
return;
_selection.clear();
apply_diff ();
+ hide_verbose_cursor ();
}
void
_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);
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());
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();
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
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) {
(*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) {
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);
}
/** @param x Pixel relative to the region position.
+ * @param ensure_snap defaults to false. true = snap always, ignoring snap mode and magnetic snap.
+ * Used for inverting the snap logic with key modifiers and snap delta calculation.
* @return Snapped frame relative to the region position.
*/
framepos_t
-MidiRegionView::snap_pixel_to_sample(double x)
+MidiRegionView::snap_pixel_to_sample(double x, bool ensure_snap)
{
PublicEditor& editor (trackview.editor());
- return snap_frame_to_frame (editor.pixel_to_sample (x));
+ return snap_frame_to_frame (editor.pixel_to_sample (x), ensure_snap);
}
/** @param x Pixel relative to the region position.
+ * @param ensure_snap defaults to false. true = ignore magnetic snap and snap mode (used for snap delta calculation).
* @return Snapped pixel relative to the region position.
*/
double
-MidiRegionView::snap_to_pixel(double x)
+MidiRegionView::snap_to_pixel(double x, bool ensure_snap)
{
- return (double) trackview.editor().sample_to_pixel(snap_pixel_to_sample(x));
+ return (double) trackview.editor().sample_to_pixel(snap_pixel_to_sample(x, ensure_snap));
}
double
return _region_relative_time_converter.from(frames);
}
+double
+MidiRegionView::region_frames_to_region_beats_double (framepos_t frames) const
+{
+ return _region_relative_time_converter_double.from(frames);
+}
+
void
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
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);
* a difference when multiple notes are being resized; in relative mode, each note's length is changed by the
* amount of the drag. In non-relative mode, all selected notes are set to have the same start or end point
* as the \a primary note.
+ * @param snap_delta snap offset of the primary note in pixels. used in SnapRelative SnapDelta mode.
+ * @param with_snap true if snap is to be used to determine the position, false if no snap is to be used.
*/
void
-MidiRegionView::update_resizing (NoteBase* primary, bool at_front, double delta_x, bool relative)
+MidiRegionView::update_resizing (NoteBase* primary, bool at_front, double delta_x, bool relative, double snap_delta, bool with_snap)
{
+ TempoMap& tmap (trackview.session()->tempo_map());
bool cursor_set = false;
+ bool const ensure_snap = trackview.editor().snap_mode () != SnapMagnetic;
for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
ArdourCanvas::Rectangle* resize_rect = (*i)->resize_rect;
if (at_front) {
if (relative) {
- current_x = canvas_note->x0() + delta_x;
+ current_x = canvas_note->x0() + delta_x + snap_delta;
} else {
- current_x = primary->x0() + delta_x;
+ current_x = primary->x0() + delta_x + snap_delta;
}
} else {
if (relative) {
- current_x = canvas_note->x1() + delta_x;
+ current_x = canvas_note->x1() + delta_x + snap_delta;
} else {
- current_x = primary->x1() + delta_x;
+ current_x = primary->x1() + delta_x + snap_delta;
}
}
}
if (at_front) {
- resize_rect->set_x0 (snap_to_pixel(current_x));
+ if (with_snap) {
+ 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 {
- resize_rect->set_x1 (snap_to_pixel(current_x));
+ if (with_snap) {
+ resize_rect->set_x1 (snap_to_pixel (current_x, ensure_snap) - snap_delta);
+ } else {
+ resize_rect->set_x1 (current_x - snap_delta);
+ }
resize_rect->set_x0 (canvas_note->x0());
}
if (!cursor_set) {
- const double snapped_x = snap_pixel_to_sample (current_x);
- Evoral::Beats beats = region_frames_to_region_beats (snapped_x);
- Evoral::Beats len = Evoral::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 (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;
+ }
+
+ double snapped_x;
+ uint32_t divisions = 0;
+
+ if (with_snap) {
+ snapped_x = snap_pixel_to_sample (current_x, ensure_snap);
+ divisions = trackview.editor().get_grid_music_divisions (0);
+ } else {
+ snapped_x = trackview.editor ().pixel_to_sample (current_x);
+ }
+ const Evoral::Beats beats = Evoral::Beats (tmap.exact_beat_at_frame (snapped_x + midi_region()->position(), divisions)
+ - midi_region()->beat()) + midi_region()->start_beats();
+
+ Evoral::Beats len = Evoral::Beats();
if (at_front) {
if (beats < canvas_note->note()->end_time()) {
- len = canvas_note->note()->time() - 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();
+ len = beats - canvas_note->note()->time() - (sign * snap_delta_beats);
}
}
* Parameters the same as for \a update_resizing().
*/
void
-MidiRegionView::commit_resizing (NoteBase* primary, bool at_front, double delta_x, bool relative)
+MidiRegionView::commit_resizing (NoteBase* primary, bool at_front, double delta_x, bool relative, double snap_delta, bool with_snap)
{
_note_diff_command = _model->new_note_diff_command (_("resize notes"));
+ TempoMap& tmap (trackview.session()->tempo_map());
+
+ /* XX why doesn't snap_pixel_to_sample() handle this properly? */
+ bool const ensure_snap = trackview.editor().snap_mode () != SnapMagnetic;
for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
Note* canvas_note = (*i)->note;
/* Get the new x position for this resize, which is in pixels relative
* to the region position.
*/
-
+
double current_x;
if (at_front) {
if (relative) {
- current_x = canvas_note->x0() + delta_x;
+ current_x = canvas_note->x0() + delta_x + snap_delta;
} else {
- current_x = primary->x0() + delta_x;
+ current_x = primary->x0() + delta_x + snap_delta;
}
} else {
if (relative) {
- current_x = canvas_note->x1() + delta_x;
+ current_x = canvas_note->x1() + delta_x + snap_delta;
} else {
- current_x = primary->x1() + delta_x;
+ current_x = primary->x1() + delta_x + snap_delta;
}
}
current_x = trackview.editor().sample_to_pixel(_region->length());
}
- /* Convert that to a frame within the source */
- current_x = snap_pixel_to_sample (current_x) + _region->start ();
+ /* 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 = 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) {
+ snap_delta_beats = region_frames_to_region_beats_double ( - snap_delta_samps);
+ sign = -1;
+ }
+
+ uint32_t divisions = 0;
+ /* 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);
+ divisions = trackview.editor().get_grid_music_divisions (0);
+ } else {
+ current_fr = trackview.editor().pixel_to_sample (current_x);
+ }
/* and then to beats */
- const Evoral::Beats x_beats = region_frames_to_region_beats (current_x);
+ const Evoral::Beats x_beats = Evoral::Beats (tmap.exact_beat_at_frame (current_fr + midi_region()->position(), divisions)
+ - midi_region()->beat()) + midi_region()->start_beats();
if (at_front && x_beats < canvas_note->note()->end_time()) {
- note_diff_add_change (canvas_note, MidiModel::NoteDiffCommand::StartTime, x_beats);
-
- Evoral::Beats len = canvas_note->note()->time() - x_beats;
+ note_diff_add_change (canvas_note, MidiModel::NoteDiffCommand::StartTime, x_beats - (sign * snap_delta_beats));
+ Evoral::Beats len = canvas_note->note()->time() - x_beats + (sign * snap_delta_beats);
len += canvas_note->note()->length();
if (!!len) {
}
if (!at_front) {
- const Evoral::Beats len = std::max(Evoral::Beats(1 / 512.0),
- x_beats - canvas_note->note()->time());
+ Evoral::Beats len = std::max(Evoral::Beats(1 / 512.0),
+ x_beats - canvas_note->note()->time() - (sign * snap_delta_beats));
note_diff_add_change (canvas_note, MidiModel::NoteDiffCommand::Length, len);
}
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) {
void
MidiRegionView::note_entered(NoteBase* ev)
{
+ _note_entered = true;
+
Editor* editor = dynamic_cast<Editor*>(&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 ());
}
}
void
MidiRegionView::note_left (NoteBase*)
{
- Editor* editor = dynamic_cast<Editor*>(&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
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'
+ << instrument_info().get_patch_name_without (p->patch()->bank(), p->patch()->program(), p->patch()->channel()) << '\n'
<< _("Channel ") << ((int) p->patch()->channel() + 1);
show_verbose_cursor (s.str(), 10, 20);
p->item().grab_focus();
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.
*/
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.
*/
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
/** This method handles undo */
bool
-MidiRegionView::paste (framepos_t pos, const ::Selection& selection, PasteContext& ctx)
+MidiRegionView::paste (framepos_t pos, const ::Selection& selection, PasteContext& ctx, const int32_t sub_num)
{
bool commit = false;
// Paste notes, if available
typedef RouteTimeAxisView::AutomationTracks ATracks;
const ATracks& atracks = midi_view()->automation_tracks();
for (ATracks::const_iterator a = atracks.begin(); a != atracks.end(); ++a) {
- if (a->second->paste(pos, selection, ctx)) {
+ if (a->second->paste(pos, selection, ctx, sub_num)) {
commit = true;
}
}
duration, pos, _region->position(),
pos_beats));
- clear_selection ();
+ clear_editor_note_selection ();
for (int n = 0; n < (int) times; ++n) {
boost::shared_ptr<NoteType> 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 */
DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("Paste extended region from %1 to %2\n", region_end, end_frame));
_region->clear_changes ();
- _region->set_length (end_frame - _region->position());
+ /* we probably need to get the snap modifier somehow to make this correct for non-musical use */
+ _region->set_length (end_frame - _region->position(), trackview.editor().get_grid_music_divisions (0));
trackview.session()->add_command (new StatefulDiffCommand (_region));
}
_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);
_ghost_note = 0;
}
+void
+MidiRegionView::hide_verbose_cursor ()
+{
+ trackview.editor().verbose_cursor()->hide ();
+ MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
+ if (mtv) {
+ mtv->set_note_highlight (NO_MIDI_NOTE);
+ }
+}
+
void
MidiRegionView::snap_changed ()
{
MidiRegionView::enable_display (bool yn)
{
RegionView::enable_display (yn);
- if (yn) {
- redisplay_model ();
- }
}
void
_step_edit_cursor_width = beats;
if (_step_edit_cursor) {
- _step_edit_cursor->set_x1 (_step_edit_cursor->x0() + trackview.editor().sample_to_pixel (region_beats_to_region_frames (beats)));
+ _step_edit_cursor->set_x1 (_step_edit_cursor->x0() + trackview.editor().sample_to_pixel (
+ region_beats_to_region_frames (_step_edit_cursor_position + beats)
+ - region_beats_to_region_frames (_step_edit_cursor_position)));
}
}
/* Trim drag made start time -ve; fix this */
midi_region()->fix_negative_start ();
}
+ /* until _start is modified on the fly during front trim,
+ we have to redisplay the model when a start trim has finished.
+ */
+ enable_display (true);
}
void
// display_sysexes();
}
-void
-MidiRegionView::show_verbose_cursor (boost::shared_ptr<NoteType> n) const
+std::string
+MidiRegionView::get_note_name (boost::shared_ptr<NoteType> n, uint8_t note_value) const
{
using namespace MIDI::Name;
-
std::string name;
- MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
+ MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
if (mtv) {
boost::shared_ptr<MasterDeviceNames> device_names(mtv->get_device_names());
if (device_names) {
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() ? ParameterDescriptor::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<NoteType> current_note,
+ uint8_t new_value) const
+{
+ MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*>(&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<NoteType> n) const
+{
+ show_verbose_cursor_for_new_note_value(n, n->note());
}
void
MidiRegionView::snap_frame_to_grid_underneath (framepos_t p, framecnt_t& grid_frames) const
{
PublicEditor& editor = trackview.editor ();
-
const Evoral::Beats grid_beats = get_grid_beats(p);
+ const Evoral::Beats p_beat = max (Evoral::Beats(), region_frames_to_region_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.
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);
-}
-
-void
-MidiRegionView::note_button_release ()
-{
- _note_player.reset();
-}
-
ChannelMode
MidiRegionView::get_channel_mode () 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);
}