#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,
, _step_edit_cursor_width (1.0)
, _step_edit_cursor_position (0.0)
, _channel_selection_scoped_note (0)
- , _temporary_note_group (0)
, _mouse_state(None)
, _pressed_button(0)
, _sort_needed (true)
, _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,
, _step_edit_cursor_width (1.0)
, _step_edit_cursor_position (0.0)
, _channel_selection_scoped_note (0)
- , _temporary_note_group (0)
, _mouse_state(None)
, _pressed_button(0)
, _sort_needed (true)
, _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
, _step_edit_cursor_width (1.0)
, _step_edit_cursor_position (0.0)
, _channel_selection_scoped_note (0)
- , _temporary_note_group (0)
, _mouse_state(None)
, _pressed_button(0)
, _sort_needed (true)
, _last_event_y (0)
, _grabbed_keyboard (false)
, _entered (false)
+ , _note_entered (false)
, _mouse_changed_selection (false)
{
init (false);
, _step_edit_cursor_width (1.0)
, _step_edit_cursor_position (0.0)
, _channel_selection_scoped_note (0)
- , _temporary_note_group (0)
, _mouse_state(None)
, _pressed_button(0)
, _sort_needed (true)
, _last_event_y (0)
, _grabbed_keyboard (false)
, _entered (false)
+ , _note_entered (false)
, _mouse_changed_selection (false)
{
init (true);
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 ();
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&
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 ();
+ }
+ }
}
}
{
hide_verbose_cursor ();
remove_ghost_note ();
+ _note_entered = false;
if (_grabbed_keyboard) {
Keyboard::magic_widget_drop_focus();
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;
{
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 ();
- hide_verbose_cursor ();
+ } 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 */
} 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;
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;
}
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);
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) {
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 (visible) {
+ cne->validate ();
+ update_note (cne);
cne->show ();
} else {
cne->hide ();
end_write();
}
- _selection_cleared_connection.disconnect ();
-
_selection.clear();
- clear_events (false);
+ clear_events ();
delete _note_group;
delete _note_diff_command;
delete _step_edit_cursor;
- delete _temporary_note_group;
}
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) ||
what_changed.contains (ARDOUR::Properties::position)) {
_source_relative_time_converter.set_origin_b (_region->position() - _region->start());
}
+ /* catch end and start trim so we can update the view*/
+ if (!what_changed.contains (ARDOUR::Properties::start) &&
+ what_changed.contains (ARDOUR::Properties::length)) {
+ enable_display (true);
+ } else if (what_changed.contains (ARDOUR::Properties::start) &&
+ what_changed.contains (ARDOUR::Properties::length)) {
+ enable_display (true);
+ }
}
void
/* 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;
}
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;
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();
}
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());
}
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) {
void
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 (with_snap) {
- resize_rect->set_x0 (snap_to_pixel(current_x) - 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) - snap_delta);
+ resize_rect->set_x1 (snap_to_pixel (current_x, ensure_snap) - snap_delta);
} else {
resize_rect->set_x1 (current_x - snap_delta);
}
sign = -1;
}
- const double snapped_x = (with_snap ? snap_pixel_to_sample (current_x) : trackview.editor ().pixel_to_sample (current_x));
- Evoral::Beats beats = region_frames_to_region_beats (snapped_x);
- Evoral::Beats len = Evoral::Beats();
+ 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()) {
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;
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) + _region->start ();
+ 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) + _region->start ();
+ current_fr = trackview.editor().pixel_to_sample (current_x);
}
/* and then to beats */
- const Evoral::Beats x_beats = region_frames_to_region_beats (current_fr);
+ 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 - (sign * snap_delta_beats));
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*)
{
+ _note_entered = false;
+
for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
(*i)->hide_velocity ();
}
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'
+ << 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();
/** 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) {
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));
}
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)));
}
}
void
MidiRegionView::trim_front_starting ()
{
- /* Reparent the note group to the region view's parent, so that it doesn't change
- when the region view is trimmed.
+ /* We used to eparent the note group to the region view's parent, so that it didn't change.
+ now we update it.
*/
- _temporary_note_group = new ArdourCanvas::Container (group->parent ());
- _temporary_note_group->move (group->position ());
- _note_group->reparent (_temporary_note_group);
}
void
MidiRegionView::trim_front_ending ()
{
- _note_group->reparent (group);
- delete _temporary_note_group;
- _temporary_note_group = 0;
-
if (_region->start() < 0) {
/* Trim drag made start time -ve; fix this */
midi_region()->fix_negative_start ();
// 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* mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
n->channel(),
patch_key.bank(),
patch_key.program(),
- n->note());
+ note_value);
}
- mtv->set_note_highlight (n->note());
}
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);
-}
-
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);
}