add back-pointer to TempoMap from points, and push dirty=true into map
[ardour.git] / gtk2_ardour / midi_region_view.cc
index a427424614d1165c1c6551ce6944d068071b481f..a8124a9675196d2d714806ead6f2ba956fb7b62e 100644 (file)
@@ -956,7 +956,6 @@ MidiRegionView::create_note_at (framepos_t t, double y, Evoral::Beats length, ui
 
        start_note_diff_command(_("add note"));
 
-       clear_editor_note_selection ();
        note_diff_add_note (new_note, true, false);
 
        apply_diff();
@@ -1094,6 +1093,7 @@ MidiRegionView::abort_command()
 {
        delete _note_diff_command;
        _note_diff_command = 0;
+       trackview.editor().abort_reversible_command();
        clear_editor_note_selection();
 }
 
@@ -1153,7 +1153,7 @@ MidiRegionView::find_canvas_sys_ex (MidiModel::SysExPtr s)
                return f->second;
        }
 
-       return 0;
+       return boost::shared_ptr<SysEx>();
 }
 
 void
@@ -1339,7 +1339,7 @@ MidiRegionView::display_patch_changes_on_channel (uint8_t channel, bool active_c
                                const double x = trackview.editor().sample_to_pixel (region_frames);
                                const string patch_name = instrument_info().get_patch_name ((*i)->bank(), (*i)->program(), channel);
                                p->canvas_item()->set_position (ArdourCanvas::Duple (x, 1.0));
-                               p->flag()->set_text (patch_name);
+                               p->set_text (patch_name);
 
                                p->show();
                        }
@@ -1418,14 +1418,19 @@ MidiRegionView::display_sysexes()
                        sysex = boost::shared_ptr<SysEx>(
                                new SysEx (*this, _note_group, text, height, x, 1.0, sysex_ptr));
                        _sys_exes.insert (make_pair (sysex_ptr, sysex));
+               } else {
+                       sysex->set_height (height);
+                       sysex->item().set_position (ArdourCanvas::Duple (x, 1.0));
                }
 
                // Show unless message is beyond the region bounds
-               if (time - _region->start() >= _region->length() || time < _region->start()) {
-                       sysex->hide();
-               } else {
-                       sysex->show();
-               }
+// XXX REQUIRES APPROPRIATE OPERATORS FOR Evoral::Beats and framepos? say what?
+#warning paul fix this
+//             if (time - _region->start() >= _region->length() || time < _region->start()) {
+//                     sysex->hide();
+//             } else {
+//                     sysex->show();
+//             }
        }
 }
 
@@ -1488,7 +1493,7 @@ MidiRegionView::reset_width_dependent_items (double pixel_width)
        bool hide_all = false;
        PatchChanges::iterator x = _patch_changes.begin();
        if (x != _patch_changes.end()) {
-               hide_all = x->second->flag()->width() >= _pixel_width;
+               hide_all = x->second->width() >= _pixel_width;
        }
 
        if (hide_all) {
@@ -2079,7 +2084,6 @@ MidiRegionView::change_patch_change (MidiModel::PatchChangePtr old_change, const
 void
 MidiRegionView::add_patch_change (framecnt_t t, Evoral::PatchChange<Evoral::Beats> const & patch)
 {
-       MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
        string name = _("add patch change");
 
        trackview.editor().begin_reversible_command (name);
@@ -2087,7 +2091,7 @@ MidiRegionView::add_patch_change (framecnt_t t, Evoral::PatchChange<Evoral::Beat
        c->add (MidiModel::PatchChangePtr (
                        new Evoral::PatchChange<Evoral::Beats> (
                                absolute_frames_to_source_beats (_region->position() + t),
-                               mtv->get_channel_for_add(), patch.program(), patch.bank()
+                               patch.channel(), patch.program(), patch.bank()
                                )
                        )
                );
@@ -2393,7 +2397,7 @@ MidiRegionView::note_selected (NoteBase* ev, bool add, bool extend)
        } else {
                /* find end of latest note selected, select all between that and the start of "ev" */
 
-               Evoral::Beats earliest = Evoral::MaxBeats;
+               Evoral::Beats earliest = std::numeric_limits<Evoral::Beats>::max();
                Evoral::Beats latest   = Evoral::Beats();
 
                for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
@@ -2546,12 +2550,10 @@ MidiRegionView::add_to_selection (NoteBase* ev)
        }
 }
 
-void
-MidiRegionView::move_selection(double dx, double dy, double cumulative_dy)
+Evoral::Beats
+MidiRegionView::earliest_in_selection ()
 {
-       typedef vector<boost::shared_ptr<NoteType> > PossibleChord;
-       PossibleChord to_play;
-       Evoral::Beats earliest = Evoral::MaxBeats;
+       Evoral::Beats earliest = std::numeric_limits<Evoral::Beats>::max();
 
        for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
                if ((*i)->note()->time() < earliest) {
@@ -2559,11 +2561,47 @@ MidiRegionView::move_selection(double dx, double dy, double cumulative_dy)
                }
        }
 
+       return earliest;
+}
+
+void
+MidiRegionView::move_selection(double dx_qn, double dy, double cumulative_dy)
+{
+       typedef vector<boost::shared_ptr<NoteType> > PossibleChord;
+       Editor* editor = dynamic_cast<Editor*> (&trackview.editor());
+       TempoMap& tmap (editor->session()->tempo_map());
+       PossibleChord to_play;
+       Evoral::Beats earliest = earliest_in_selection();
+
        for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
-               if ((*i)->note()->time() == earliest) {
-                       to_play.push_back ((*i)->note());
+               NoteBase* n = *i;
+               if (n->note()->time() == earliest) {
+                       to_play.push_back (n->note());
+               }
+               double const note_time_qn = session_relative_qn (n->note()->time().to_double());
+               double dx = 0.0;
+               if (midi_view()->note_mode() == Sustained) {
+                       dx = editor->sample_to_pixel_unrounded (tmap.frame_at_quarter_note (note_time_qn + dx_qn))
+                               - n->item()->item_to_canvas (ArdourCanvas::Duple (n->x0(), 0)).x;
+               } else {
+                       /* Hit::x0() is offset by _position.x, unlike Note::x0() */
+                       Hit* hit = dynamic_cast<Hit*>(n);
+                       if (hit) {
+                               dx = editor->sample_to_pixel_unrounded (tmap.frame_at_quarter_note (note_time_qn + dx_qn))
+                                       - n->item()->item_to_canvas (ArdourCanvas::Duple (((hit->x0() + hit->x1()) / 2.0) - hit->position().x, 0)).x;
+                       }
                }
+
                (*i)->move_event(dx, dy);
+
+               /* update length */
+               if (midi_view()->note_mode() == Sustained) {
+                       Note* sus = dynamic_cast<Note*> (*i);
+                       double const len_dx = editor->sample_to_pixel_unrounded (
+                               tmap.frame_at_quarter_note (note_time_qn + dx_qn + n->note()->length().to_double()));
+
+                       sus->set_x1 (n->item()->canvas_to_item (ArdourCanvas::Duple (len_dx, 0)).x);
+               }
        }
 
        if (dy && !_selection.empty() && !_no_sound_notes && UIConfiguration::instance().get_sound_midi_notes()) {
@@ -2590,15 +2628,17 @@ MidiRegionView::move_selection(double dx, double dy, double cumulative_dy)
 }
 
 NoteBase*
-MidiRegionView::copy_selection ()
+MidiRegionView::copy_selection (NoteBase* primary)
 {
-       NoteBase* note;
        _copy_drag_events.clear ();
 
        if (_selection.empty()) {
                return 0;
        }
 
+       NoteBase* note;
+       NoteBase* ret = 0;
+
        for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
                boost::shared_ptr<NoteType> g (new NoteType (*((*i)->note())));
                if (midi_view()->note_mode() == Sustained) {
@@ -2611,30 +2651,52 @@ MidiRegionView::copy_selection ()
                        note = h;
                }
 
+               if ((*i) == primary) {
+                       ret = note;
+               }
+
                _copy_drag_events.push_back (note);
        }
 
-       return _copy_drag_events.front ();
+       return ret;
 }
 
 void
-MidiRegionView::move_copies (double dx, double dy, double cumulative_dy)
+MidiRegionView::move_copies (double dx_qn, double dy, double cumulative_dy)
 {
        typedef vector<boost::shared_ptr<NoteType> > PossibleChord;
+       Editor* editor = dynamic_cast<Editor*> (&trackview.editor());
+       TempoMap& tmap (editor->session()->tempo_map());
        PossibleChord to_play;
-       Evoral::Beats earliest = Evoral::MaxBeats;
+       Evoral::Beats earliest = earliest_in_selection();
 
        for (CopyDragEvents::iterator i = _copy_drag_events.begin(); i != _copy_drag_events.end(); ++i) {
-               if ((*i)->note()->time() < earliest) {
-                       earliest = (*i)->note()->time();
+               NoteBase* n = *i;
+               if (n->note()->time() == earliest) {
+                       to_play.push_back (n->note());
                }
-       }
-
-       for (CopyDragEvents::iterator i = _copy_drag_events.begin(); i != _copy_drag_events.end(); ++i) {
-               if ((*i)->note()->time() == earliest) {
-                       to_play.push_back ((*i)->note());
+               double const note_time_qn = session_relative_qn (n->note()->time().to_double());
+               double dx = 0.0;
+               if (midi_view()->note_mode() == Sustained) {
+                       dx = editor->sample_to_pixel_unrounded (tmap.frame_at_quarter_note (note_time_qn + dx_qn))
+                               - n->item()->item_to_canvas (ArdourCanvas::Duple (n->x0(), 0)).x;
+               } else {
+                       Hit* hit = dynamic_cast<Hit*>(n);
+                       if (hit) {
+                               dx = editor->sample_to_pixel_unrounded (tmap.frame_at_quarter_note (note_time_qn + dx_qn))
+                                       - n->item()->item_to_canvas (ArdourCanvas::Duple (((hit->x0() + hit->x1()) / 2.0) - hit->position().x, 0)).x;
+                       }
                }
+
                (*i)->move_event(dx, dy);
+
+               if (midi_view()->note_mode() == Sustained) {
+                       Note* sus = dynamic_cast<Note*> (*i);
+                       double const len_dx = editor->sample_to_pixel_unrounded (
+                               tmap.frame_at_quarter_note (note_time_qn + dx_qn + n->note()->length().to_double()));
+
+                       sus->set_x1 (n->item()->canvas_to_item (ArdourCanvas::Duple (len_dx, 0)).x);
+               }
        }
 
        if (dy && !_copy_drag_events.empty() && !_no_sound_notes && UIConfiguration::instance().get_sound_midi_notes()) {
@@ -2661,7 +2723,7 @@ MidiRegionView::move_copies (double dx, double dy, double cumulative_dy)
 }
 
 void
-MidiRegionView::note_dropped(NoteBase *, frameoffset_t dt, int8_t dnote, bool copy)
+MidiRegionView::note_dropped(NoteBase *, double d_qn, int8_t dnote, bool copy)
 {
        uint8_t lowest_note_in_selection  = 127;
        uint8_t highest_note_in_selection = 0;
@@ -2690,15 +2752,13 @@ MidiRegionView::note_dropped(NoteBase *, frameoffset_t dt, int8_t dnote, bool co
                if (highest_note_in_selection + dnote > 127) {
                        highest_note_difference = highest_note_in_selection - 127;
                }
-               TempoMap& map (trackview.session()->tempo_map());
 
                start_note_diff_command (_("move notes"));
 
                for (Selection::iterator i = _selection.begin(); i != _selection.end() ; ++i) {
 
-                       double const start_qn = _region->quarter_note() - midi_region()->start_beats();
-                       framepos_t new_frames = map.frame_at_quarter_note (start_qn + (*i)->note()->time().to_double()) + dt;
-                       Evoral::Beats new_time = Evoral::Beats (map.quarter_note_at_frame (new_frames) - start_qn);
+                       Evoral::Beats new_time = Evoral::Beats ((*i)->note()->time().to_double() + d_qn);
+
                        if (new_time < 0) {
                                continue;
                        }
@@ -2731,16 +2791,12 @@ MidiRegionView::note_dropped(NoteBase *, frameoffset_t dt, int8_t dnote, bool co
                        highest_note_difference = highest_note_in_selection - 127;
                }
 
-               TempoMap& map (trackview.session()->tempo_map());
                start_note_diff_command (_("copy notes"));
 
                for (CopyDragEvents::iterator i = _copy_drag_events.begin(); i != _copy_drag_events.end() ; ++i) {
 
                        /* update time */
-
-                       double const start_qn = _region->quarter_note() - midi_region()->start_beats();
-                       framepos_t new_frames = map.frame_at_quarter_note (start_qn + (*i)->note()->time().to_double()) + dt;
-                       Evoral::Beats new_time = Evoral::Beats (map.quarter_note_at_frame (new_frames) - start_qn);
+                       Evoral::Beats new_time = Evoral::Beats ((*i)->note()->time().to_double() + d_qn);
 
                        if (new_time < 0) {
                                continue;
@@ -2787,7 +2843,7 @@ framepos_t
 MidiRegionView::snap_pixel_to_sample(double x, bool ensure_snap)
 {
        PublicEditor& editor (trackview.editor());
-       return snap_frame_to_frame (editor.pixel_to_sample (x), ensure_snap);
+       return snap_frame_to_frame (editor.pixel_to_sample (x), ensure_snap).frame;
 }
 
 /** @param x Pixel relative to the region position.
@@ -4366,3 +4422,9 @@ MidiRegionView::note_to_y(uint8_t note) const
 {
        return contents_height() - (note + 1 - _current_range_min) * note_height() + 1;
 }
+
+double
+MidiRegionView::session_relative_qn (double qn) const
+{
+       return qn + (region()->quarter_note() - midi_region()->start_beats());
+}