#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"
{
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);
void
MidiRegionView::leave_internal()
{
- trackview.editor().verbose_cursor()->hide ();
+ hide_verbose_cursor ();
remove_ghost_note ();
if (_grabbed_keyboard) {
}
if (_mouse_state != SelectTouchDragging) {
-
+
_pressed_button = ev->button;
_mouse_state = Pressed;
-
+
return true;
}
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_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:
} else if (_ghost_note && editor.current_mouse_mode() == MouseContent) {
remove_ghost_note ();
- editor.verbose_cursor()->hide ();
+ hide_verbose_cursor ();
} else if (_ghost_note && editor.current_mouse_mode() == MouseDraw) {
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);
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;
if (_selection.empty()) {
return;
}
-
+
/* pick a note somewhat at random (since Selection is a set<>) to
* provide the "current" velocity for the dialog.
*/
bool visible;
if (note_in_region_range (note, visible)) {
-
+
if (!empty_when_starting && (cne = find_canvas_note (note)) != 0) {
cne->validate ();
}
} 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;
/* 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;
}
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));
+ ev->set_x1 (std::max(1., trackview.editor().sample_to_pixel (note_end_frames)) - 1);
} else {
- ev->set_x1 (trackview.editor().sample_to_pixel (_region->length()));
+ ev->set_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));
{
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
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 ();
(*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, bool explicitly)
+MidiRegionView::snap_pixel_to_sample(double x, bool ensure_snap)
{
PublicEditor& editor (trackview.editor());
- return snap_frame_to_frame (editor.pixel_to_sample (x), explicitly);
+ 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, bool explicitly)
+MidiRegionView::snap_to_pixel(double x, bool ensure_snap)
{
- return (double) trackview.editor().sample_to_pixel(snap_pixel_to_sample(x, explicitly));
+ return (double) trackview.editor().sample_to_pixel(snap_pixel_to_sample(x, ensure_snap));
}
double
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);
* 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, double snap_delta, guint key_state)
+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<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
ArdourCanvas::Rectangle* resize_rect = (*i)->resize_rect;
}
if (at_front) {
- if (ArdourKeyboard::indicates_snap (key_state)) {
- resize_rect->set_x0 (current_x - snap_delta);
+ if (with_snap) {
+ resize_rect->set_x0 (snap_to_pixel (current_x, ensure_snap) - snap_delta);
} else {
- resize_rect->set_x0 (snap_to_pixel(current_x) - snap_delta);
+ resize_rect->set_x0 (current_x - snap_delta);
}
resize_rect->set_x1 (canvas_note->x1());
} else {
- if (ArdourKeyboard::indicates_snap (key_state)) {
- resize_rect->set_x1 (current_x - snap_delta);
+ if (with_snap) {
+ resize_rect->set_x1 (snap_to_pixel (current_x, ensure_snap) - snap_delta);
} else {
- resize_rect->set_x1 (snap_to_pixel(current_x) - snap_delta);
+ resize_rect->set_x1 (current_x - snap_delta);
}
resize_rect->set_x0 (canvas_note->x0());
}
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 = snap_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);
}
}
* Parameters the same as for \a update_resizing().
*/
void
-MidiRegionView::commit_resizing (NoteBase* primary, bool at_front, double delta_x, bool relative, double snap_delta, guint key_state)
+MidiRegionView::commit_resizing (NoteBase* primary, bool at_front, double delta_x, bool relative, double snap_delta, bool with_snap)
{
- bool snap_keys = ArdourKeyboard::indicates_snap (key_state);
-
_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<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
Note* canvas_note = (*i)->note;
ArdourCanvas::Rectangle* resize_rect = (*i)->resize_rect;
/* Get the new x position for this resize, which is in pixels relative
* to the region position.
*/
-
+
double current_x;
if (at_front) {
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) {
sign = -1;
}
- /* Convert that to a frame within the source */
+ /* Convert the new x position to a frame within the source */
framepos_t current_fr;
-
- if (snap_keys) {
- current_fr = trackview.editor().pixel_to_sample (current_x) + _region->start ();
+ if (with_snap) {
+ current_fr = snap_pixel_to_sample (current_x, ensure_snap) + _region->start ();
} else {
- current_fr = snap_pixel_to_sample (current_x) + _region->start ();
+ current_fr = trackview.editor().pixel_to_sample (current_x) + _region->start ();
}
/* and then to beats */
void
MidiRegionView::note_left (NoteBase*)
{
- Editor* editor = dynamic_cast<Editor*>(&trackview.editor());
-
for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
(*i)->hide_velocity ();
}
- editor->verbose_cursor()->hide ();
+ hide_verbose_cursor ();
}
void
{
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();
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
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 */
_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 ()
{
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) {
patch_key.program(),
n->note());
}
+ mtv->set_note_highlight (n->note());
}
char buf[128];
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);
grid_frames = region_beats_to_region_frames (grid_beats);