diff --git a/gtk2_ardour/canvas-midi-event.cc b/gtk2_ardour/canvas-midi-event.cc index 2895be1eba..fca3a1618b 100644 --- a/gtk2_ardour/canvas-midi-event.cc +++ b/gtk2_ardour/canvas-midi-event.cc @@ -43,6 +43,8 @@ CanvasMidiEvent::CanvasMidiEvent(MidiRegionView& region, Item* item, const ARDOU bool CanvasMidiEvent::on_event(GdkEvent* ev) { + static uint8_t drag_delta_note = 0; + static double drag_delta_x = 0; static double last_x, last_y; double event_x, event_y, dx, dy; @@ -85,14 +87,17 @@ CanvasMidiEvent::on_event(GdkEvent* ev) _item->property_parent().get_value()->w2i(event_x, event_y); switch (_state) { - case Pressed: + case Pressed: // Drag begin _item->grab(GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK, Gdk::Cursor(Gdk::FLEUR), ev->motion.time); _state = Dragging; last_x = event_x; last_y = event_y; + drag_delta_x = 0; + drag_delta_note = 0; return true; - case Dragging: + + case Dragging: // Drag motion if (ev->motion.is_hint) { int t_x; int t_y; @@ -107,12 +112,20 @@ CanvasMidiEvent::on_event(GdkEvent* ev) last_x = event_x; + drag_delta_x += dx; + // Snap to note rows if (abs(dy) < _region.note_height()) { dy = 0.0; } else { - dy = _region.note_height() * ((dy > 0) ? 1 : -1); - last_y = event_y; + int8_t this_delta_note; + if (dy > 0) + this_delta_note = (int8_t)ceil(dy / _region.note_height()); + else + this_delta_note = (int8_t)floor(dy / _region.note_height()); + drag_delta_note -= this_delta_note; + dy = _region.note_height() * this_delta_note; + last_y = last_y + dy; } _item->move(dx, dy); @@ -124,6 +137,10 @@ CanvasMidiEvent::on_event(GdkEvent* ev) break; case GDK_BUTTON_RELEASE: + event_x = ev->button.x; + event_y = ev->button.y; + _item->property_parent().get_value()->w2i(event_x, event_y); + switch (_state) { case Pressed: // Clicked _state = None; @@ -132,15 +149,21 @@ CanvasMidiEvent::on_event(GdkEvent* ev) _item->ungrab(ev->button.time); _state = None; if (_note) { - cerr << "Move and stuff." << endl; // This would be nicer with a MoveCommand that doesn't need to copy... - /*_region.start_delta_command(); + _region.start_delta_command(); _region.command_remove_note(this); - Note copy_of_me(*this); - copy_of_me.time = trackview.editor.pixel_to_frame(property_x1()); - copy_of_me.note = stuff; + MidiModel::Note copy(*_note); + + double delta_t = _region.midi_view()->editor.pixel_to_frame( + abs(drag_delta_x)); + if (drag_delta_x < 0) + delta_t *= -1; + + copy.set_time(_note->time() + delta_t); + copy.set_note(_note->note() + drag_delta_note); + + _region.command_add_note(copy); _region.apply_command(); - */ } return true; default: diff --git a/gtk2_ardour/midi_region_view.cc b/gtk2_ardour/midi_region_view.cc index 6712cb486d..7a7cc798e3 100644 --- a/gtk2_ardour/midi_region_view.cc +++ b/gtk2_ardour/midi_region_view.cc @@ -231,13 +231,11 @@ MidiRegionView::create_note_at(double x, double y) MidiTimeAxisView* const mtv = dynamic_cast(&trackview); MidiStreamView* const view = mtv->midi_view(); - const uint8_t note_range = view->highest_note() - view->lowest_note() + 1; - const double footer_height = name_highlight->property_y2() - name_highlight->property_y1(); - const double roll_height = trackview.height - footer_height; - get_canvas_group()->w2i(x, y); - double note = floor((roll_height - y) / roll_height * (double)note_range) + view->lowest_note(); + double note = floor((contents_height() - y) / contents_height() * (double)contents_note_range()) + + view->lowest_note(); + assert(note >= 0.0); assert(note <= 127.0); @@ -418,7 +416,7 @@ MidiRegionView::add_event (const MidiEvent& ev) if (midi_view()->note_mode() == Sustained) { if ((ev.buffer()[0] & 0xF0) == MIDI_CMD_NOTE_ON) { const Byte& note = ev.buffer()[1]; - const double y1 = note_y(note); + const double y1 = note_to_y(note); CanvasNote* ev_rect = new CanvasNote(*this, *group); ev_rect->property_x1() = trackview.editor.frame_to_pixel ( @@ -451,7 +449,7 @@ MidiRegionView::add_event (const MidiEvent& ev) const Byte& note = ev.buffer()[1]; const double diamond_size = std::min(note_height(), 5.0); const double x = trackview.editor.frame_to_pixel((nframes_t)ev.time()); - const double y = note_y(note) + (diamond_size / 2.0); + const double y = note_to_y(note) + (diamond_size / 2.0); CanvasHit* ev_diamond = new CanvasHit(*this, *group, diamond_size); ev_diamond->move(x, y); diff --git a/gtk2_ardour/midi_region_view.h b/gtk2_ardour/midi_region_view.h index c768a539c2..243dbc2d69 100644 --- a/gtk2_ardour/midi_region_view.h +++ b/gtk2_ardour/midi_region_view.h @@ -79,11 +79,15 @@ class MidiRegionView : public RegionView inline double note_height() const { return contents_height() / (double)contents_note_range(); } - inline double note_y(uint8_t note) const + inline double note_to_y(uint8_t note) const { return trackview.height - (contents_height() * (note - midi_stream_view()->lowest_note() + 1)) - footer_height() - 3.0; } + inline uint8_t y_to_note(double y) const + { return (uint8_t)floor((contents_height() - y) + / contents_height() * (double)contents_note_range()); } + void set_y_position_and_height (double, double); void show_region_editor (); @@ -101,11 +105,19 @@ class MidiRegionView : public RegionView void display_model(boost::shared_ptr model); + /* This stuff is a bit boilerplatey ATM. Work in progress. */ + inline void start_remove_command() { _command_mode = Remove; if (!_delta_command) _delta_command = _model->new_delta_command(); } + + inline void start_delta_command() { + _command_mode = Delta; + if (!_delta_command) + _delta_command = _model->new_delta_command(); + } void command_remove_note(ArdourCanvas::CanvasMidiEvent* ev) { if (_delta_command && ev->note()) { @@ -113,6 +125,12 @@ class MidiRegionView : public RegionView ev->selected(true); } } + + void command_add_note(ARDOUR::MidiModel::Note& note) { + if (_delta_command) { + _delta_command->add(note); + } + } void note_entered(ArdourCanvas::CanvasMidiEvent* ev) { if (_command_mode == Remove && _delta_command && ev->note()) @@ -169,7 +187,7 @@ class MidiRegionView : public RegionView ArdourCanvas::CanvasNote** _active_notes; ARDOUR::MidiModel::DeltaCommand* _delta_command; - enum CommandMode { None, Remove }; + enum CommandMode { None, Remove, Delta }; CommandMode _command_mode; };