Fix up undo/redo of MIDI note paste (#3815).
[ardour.git] / gtk2_ardour / midi_region_view.cc
index fd2d629a09919d74ffab249aa5b126e3a0cb607e..24ad86f7d6f18c7a4571bf3af3828252ca6bd87a 100644 (file)
@@ -49,6 +49,7 @@
 #include "canvas-hit.h"
 #include "canvas-note.h"
 #include "canvas_patch_change.h"
+#include "debug.h"
 #include "editor.h"
 #include "ghostregion.h"
 #include "gui_thread.h"
@@ -293,10 +294,6 @@ MidiRegionView::canvas_event(GdkEvent* ev)
                return false;
        }
 
-       /* XXX: note that until version 2.30, the GnomeCanvas did not propagate scroll events
-          to its items, which means that ev->type == GDK_SCROLL will never be seen
-       */
-
        switch (ev->type) {
        case GDK_SCROLL:
                 return scroll (&ev->scroll);
@@ -924,7 +921,7 @@ MidiRegionView::note_diff_add_change (ArdourCanvas::CanvasNoteEvent* ev,
 }
 
 void
-MidiRegionView::apply_diff ()
+MidiRegionView::apply_diff (bool as_subcommand)
 {
         bool add_or_remove;
 
@@ -939,7 +936,12 @@ MidiRegionView::apply_diff ()
                 }
         }
 
-       _model->apply_command(*trackview.session(), _note_diff_command);
+       if (as_subcommand) {
+               _model->apply_command_as_subcommand (*trackview.session(), _note_diff_command);
+       } else {
+               _model->apply_command (*trackview.session(), _note_diff_command);
+       }
+       
        _note_diff_command = 0;
        midi_view()->midi_track()->playlist_modified();
         
@@ -950,33 +952,6 @@ MidiRegionView::apply_diff ()
        _marked_for_velocity.clear();
 }
 
-void
-MidiRegionView::apply_diff_as_subcommand ()
-{
-        bool add_or_remove;
-
-       if (!_note_diff_command) {
-               return;
-       }
-
-        if ((add_or_remove = _note_diff_command->adds_or_removes())) {
-                // Mark all selected notes for selection when model reloads
-                for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
-                        _marked_for_selection.insert((*i)->note());
-                }
-        }
-
-       _model->apply_command_as_subcommand(*trackview.session(), _note_diff_command);
-       _note_diff_command = 0;
-       midi_view()->midi_track()->playlist_modified();
-
-        if (add_or_remove) {
-                _marked_for_selection.clear();
-        }
-       _marked_for_velocity.clear();
-}
-
-
 void
 MidiRegionView::abort_command()
 {
@@ -1142,9 +1117,9 @@ MidiRegionView::display_patch_changes_on_channel (uint8_t channel)
                if (patch != 0) {
                        add_canvas_patch_change (*i, patch->name());
                } else {
-                       char buf[4];
-                        // program_number is zero-based: convert to one-based
-                       snprintf (buf, 4, "%d", (*i)->program() + 1);
+                       char buf[16];
+                        /* program and bank numbers are zero-based: convert to one-based */
+                       snprintf (buf, 16, "%d\n%d", (*i)->program() + 1, (*i)->bank() + 1);
                        add_canvas_patch_change (*i, buf);
                }
        }
@@ -1327,16 +1302,16 @@ MidiRegionView::add_ghost (TimeAxisView& tv)
                ghost = new MidiGhostRegion (tv, trackview, unit_position);
        }
 
-       ghost->set_height ();
-       ghost->set_duration (_region->length() / samples_per_unit);
-       ghosts.push_back (ghost);
-
        for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
                if ((note = dynamic_cast<CanvasNote*>(*i)) != 0) {
                        ghost->add_note(note);
                }
        }
 
+       ghost->set_height ();
+       ghost->set_duration (_region->length() / samples_per_unit);
+       ghosts.push_back (ghost);
+
        GhostRegion::CatchDeletion.connect (*this, invalidator (*this), ui_bind (&RegionView::remove_ghost, this, _1), gui_context());
 
        return ghost;
@@ -1460,8 +1435,12 @@ MidiRegionView::note_in_region_range(const boost::shared_ptr<NoteType> note, boo
        return !outside;
 }
 
+/** Update a canvas note's size from its model note.
+ *  @param ev Canvas note to update.
+ *  @param update_ghost_regions true to update the note in any ghost regions that we have, otherwise false.
+ */
 void
-MidiRegionView::update_note (CanvasNote* ev)
+MidiRegionView::update_note (CanvasNote* ev, bool update_ghost_regions)
 {
        boost::shared_ptr<NoteType> note = ev->note();
 
@@ -1504,6 +1483,15 @@ MidiRegionView::update_note (CanvasNote* ev)
                /* outline all edges */
                ev->property_outline_what() = (guint32) 0xF;
        }
+
+       if (update_ghost_regions) {
+               for (std::vector<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
+                       MidiGhostRegion* gr = dynamic_cast<MidiGhostRegion*> (*i);
+                       if (gr) {
+                               gr->update_note (ev);
+                       }
+               }
+       }
 }
 
 double
@@ -1873,6 +1861,16 @@ MidiRegionView::unique_select(ArdourCanvas::CanvasNoteEvent* ev)
        }
 }
 
+void
+MidiRegionView::select_all_notes ()
+{
+        clear_selection ();
+
+       for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
+                add_to_selection (*i);
+        }
+}
+
 void
 MidiRegionView::select_matching_notes (uint8_t notenum, uint16_t channel_mask, bool add, bool extend)
 {
@@ -2858,12 +2856,16 @@ MidiRegionView::note_mouse_position (float x_fraction, float /*y_fraction*/, boo
 void
 MidiRegionView::set_frame_color()
 {
-       if (frame) {
-               if (_selected && should_show_selection) {
-                       frame->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_SelectedFrameBase.get();
-               } else {
-                       frame->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_MidiFrameBase.get();
-               }
+       if (!frame) {
+               return;
+       }
+
+       if (_selected) {
+               frame->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_SelectedFrameBase.get();
+       } else if (high_enough_for_name) {
+               frame->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_MidiFrameBase.get();
+       } else {
+               frame->property_fill_color_rgba() = fill_color;
        }
 }
 
@@ -2952,6 +2954,7 @@ MidiRegionView::selection_as_cut_buffer () const
        return cb;
 }
 
+/** This method handles undo */
 void
 MidiRegionView::paste (framepos_t pos, float times, const MidiCutBuffer& mcb)
 {
@@ -2959,6 +2962,10 @@ MidiRegionView::paste (framepos_t pos, float times, const MidiCutBuffer& mcb)
                return;
        }
 
+        DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("MIDI paste @ %1 times %2\n", pos, times));
+
+       trackview.session()->begin_reversible_command (_("paste"));
+
        start_note_diff_command (_("paste"));
 
        Evoral::MusicalTime beat_delta;
@@ -2971,6 +2978,12 @@ MidiRegionView::paste (framepos_t pos, float times, const MidiCutBuffer& mcb)
        beat_delta = (*mcb.notes().begin())->time() - paste_pos_beats;
        paste_pos_beats = 0;
 
+        DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("Paste data spans from %1 to %2 (%3) ; paste pos beats = %4 (based on %5 - %6 ; beat delta = %7\n",
+                                                       (*mcb.notes().begin())->time(),
+                                                       (*mcb.notes().rbegin())->end_time(),
+                                                       duration, pos, _region->position(),
+                                                       paste_pos_beats, beat_delta));
+                                                       
         clear_selection ();
 
        for (int n = 0; n < (int) times; ++n) {
@@ -2996,14 +3009,16 @@ MidiRegionView::paste (framepos_t pos, float times, const MidiCutBuffer& mcb)
 
        if (end_frame > region_end) {
 
-               trackview.session()->begin_reversible_command (_("paste"));
+                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, this);
                trackview.session()->add_command (new StatefulDiffCommand (_region));
        }
 
-       apply_diff ();
+       apply_diff (true);
+       
+       trackview.session()->commit_reversible_command ();
 }
 
 struct EventNoteTimeEarlyFirstComparator {
@@ -3127,7 +3142,8 @@ MidiRegionView::update_ghost_note (double x, double y)
        _ghost_note->note()->set_length (length);
        _ghost_note->note()->set_note (midi_stream_view()->y_to_note (y));
 
-       update_note (_ghost_note);
+       /* the ghost note does not appear in ghost regions, so pass false in here */
+       update_note (_ghost_note, false);
 
        show_verbose_canvas_cursor (_ghost_note->note ());
 }