Update automated Plugin Controlls when seeking and not rolling
[ardour.git] / gtk2_ardour / midi_region_view.cc
index 5c460cdf483d43675fbc3454e4c9ff2aaa414dcc..bf7ff669cb7e621aa95f4040568837fa554e2e15 100644 (file)
@@ -82,7 +82,7 @@
 #include "sys_ex.h"
 #include "ui_config.h"
 
-#include "i18n.h"
+#include "pbd/i18n.h"
 
 using namespace ARDOUR;
 using namespace PBD;
@@ -111,7 +111,6 @@ 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)
@@ -155,7 +154,6 @@ 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)
@@ -204,7 +202,6 @@ MidiRegionView::MidiRegionView (const MidiRegionView& other)
        , _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)
@@ -237,7 +234,6 @@ MidiRegionView::MidiRegionView (const MidiRegionView& other, boost::shared_ptr<M
        , _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)
@@ -935,12 +931,16 @@ MidiRegionView::create_note_at (framepos_t t, double y, Evoral::Beats length, bo
        }
 
        // 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 = Evoral::Beats (trackview.session()->tempo_map().beat_at_frame (_region->position() + t)
+
+       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();
@@ -1184,11 +1184,11 @@ MidiRegionView::redisplay_model()
        _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)) {
@@ -1398,7 +1398,6 @@ MidiRegionView::~MidiRegionView ()
        delete _note_group;
        delete _note_diff_command;
        delete _step_edit_cursor;
-       delete _temporary_note_group;
 }
 
 void
@@ -1410,12 +1409,21 @@ MidiRegionView::region_resized (const PropertyChange& what_changed)
                _region_relative_time_converter.set_origin_b(_region->position());
                _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 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
@@ -1670,12 +1678,9 @@ MidiRegionView::start_playing_midi_chord (vector<boost::shared_ptr<NoteType> > n
 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());
@@ -1702,16 +1707,28 @@ MidiRegionView::update_note (NoteBase* note, bool update_ghost_regions)
 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 x0 = 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 */
-
        if (note->length() > 0) {
-               const framepos_t note_end_frames = min (source_beats_to_region_frames (note->end_time()), _region->length());
+               Evoral::Beats note_end_time = note->end_time();
+
+               if (note->end_time() > mr->start_beats() + mr->length_beats()) {
+                       note_end_time = mr->start_beats() + mr->length_beats();
+               }
+
+               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 {
                x1 = std::max(1., trackview.editor().sample_to_pixel (_region->length())) - 1;
@@ -1719,10 +1736,8 @@ MidiRegionView::update_sustained (Note* ev, bool update_ghost_regions)
 
        y1 = y0 + std::max(1., floor(midi_stream_view()->note_height()) - 1);
 
-       ev->set_x0 (x0);
-       ev->set_x1 (x1);
-       ev->set_y0 (y0);
-       ev->set_y1 (y1);
+       ArdourCanvas::Rect rect (x0, y0, x1, y1);
+       ev->set (rect);
 
        if (!note->length()) {
                if (_active_notes && note->note() < 128) {
@@ -1746,8 +1761,9 @@ MidiRegionView::update_sustained (Note* ev, bool update_ghost_regions)
        }
 
        // 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) {
@@ -1764,7 +1780,8 @@ MidiRegionView::update_hit (Hit* ev, bool update_ghost_regions)
 {
        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;
@@ -2732,6 +2749,7 @@ MidiRegionView::begin_resizing (bool /*at_front*/)
 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;
 
@@ -2793,9 +2811,19 @@ MidiRegionView::update_resizing (NoteBase* primary, bool at_front, double delta_
                                sign = -1;
                        }
 
-                       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();
+                       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()) {
@@ -2828,6 +2856,7 @@ void
 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;
@@ -2875,16 +2904,19 @@ MidiRegionView::commit_resizing (NoteBase* primary, bool at_front, double delta_
                        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_fr + _region->start());
+               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));
@@ -3491,14 +3523,16 @@ MidiRegionView::selection_as_cut_buffer () const
 
 /** This method handles undo */
 bool
-MidiRegionView::paste (framepos_t pos, const ::Selection& selection, PasteContext& ctx, const int32_t& sub_num)
+MidiRegionView::paste (framepos_t pos, const ::Selection& selection, PasteContext& ctx, const int32_t sub_num)
 {
        bool commit = false;
        // Paste notes, if available
        MidiNoteSelection::const_iterator m = selection.midi_notes.get_nth(ctx.counts.n_notes());
        if (m != selection.midi_notes.end()) {
                ctx.counts.increase_n_notes();
-               if (!(*m)->empty()) { commit = true; }
+               if (!(*m)->empty()) {
+                       commit = true;
+               }
                paste_internal(pos, ctx.count, ctx.times, **m);
        }
 
@@ -3507,6 +3541,9 @@ MidiRegionView::paste (framepos_t pos, const ::Selection& selection, PasteContex
        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, sub_num)) {
+                       if(!commit) {
+                               trackview.editor().begin_reversible_command (Operations::paste);
+                       }
                        commit = true;
                }
        }
@@ -3711,9 +3748,12 @@ MidiRegionView::update_ghost_note (double x, double y)
 
        /* calculate time in beats relative to start of source */
        const Evoral::Beats length = get_grid_beats(unsnapped_frame);
-       const Evoral::Beats time   = std::max(
+       const uint32_t divisions   = editor.get_grid_music_divisions (0);
+
+       Evoral::Beats time         = std::max(
                Evoral::Beats(),
-               absolute_frames_to_source_beats (f + _region->position ()));
+               Evoral::Beats (trackview.session()->tempo_map().exact_beat_at_frame (f + _region->position(), divisions))
+               - (_region->beat() - midi_region()->start_beats().to_double()));
 
        _ghost_note->note()->set_time (time);
        _ghost_note->note()->set_length (length);
@@ -3831,11 +3871,7 @@ MidiRegionView::color_handler ()
 void
 MidiRegionView::enable_display (bool yn)
 {
-
        RegionView::enable_display (yn);
-       if (yn) {
-               redisplay_model ();
-       }
 }
 
 void
@@ -3951,21 +3987,14 @@ MidiRegionView::data_recorded (boost::weak_ptr<MidiSource> w)
 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 ();
@@ -4028,7 +4057,7 @@ MidiRegionView::get_note_name (boost::shared_ptr<NoteType> n, uint8_t note_value
        char buf[128];
        snprintf (buf, sizeof (buf), "%d %s\nCh %d Vel %d",
                  (int) note_value,
-                 name.empty() ? Evoral::midi_note_name (note_value).c_str() : name.c_str(),
+                 name.empty() ? ParameterDescriptor::midi_note_name (note_value).c_str() : name.c_str(),
                  (int) n->channel() + 1,
                  (int) n->velocity());
 
@@ -4096,19 +4125,20 @@ framepos_t
 MidiRegionView::snap_frame_to_grid_underneath (framepos_t p, framecnt_t& grid_frames) const
 {
        PublicEditor& editor = trackview.editor ();
-       const Evoral::Beats p_beat = region_frames_to_region_beats (p);
        const Evoral::Beats grid_beats = get_grid_beats(p);
-
-       grid_frames = region_beats_to_region_frames (p_beat + grid_beats) - region_beats_to_region_frames (p_beat);
+       Evoral::Beats p_beat = max (Evoral::Beats(), region_frames_to_region_beats (p));
+       const double rem = fmod (p_beat.to_double(), grid_beats.to_double());
 
        /* 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.
        */
-       if (editor.snap_mode() == SnapNormal && p >= grid_frames / 2) {
-               p -= grid_frames / 2;
+       if (editor.snap_mode() == SnapNormal && rem >= grid_beats.to_double() / 2.0) {
+               p_beat -= Evoral::Beats(grid_beats.to_double() / 2.0);
        }
 
-       return snap_frame_to_frame (p);
+       const framepos_t snap_me = trackview.session()->tempo_map().frame_at_beat (p_beat.to_double() + _region->beat()) - _region->position();
+
+       return snap_frame_to_frame (snap_me);
 }
 
 ChannelMode