Create the session range location as and when the session first gets some content...
[ardour.git] / gtk2_ardour / midi_region_view.cc
index a8f6f11b42a653505cf6b920a2fa0331c40bc022..26490f76e73ef0e79f26188f2db1bc03f9853ee4 100644 (file)
 #include <sigc++/signal.h>
 
 #include "pbd/memento_command.h"
+#include "pbd/stateful_diff_command.h"
 
 #include "ardour/playlist.h"
 #include "ardour/tempo.h"
 #include "ardour/midi_region.h"
 #include "ardour/midi_source.h"
-#include "ardour/midi_diskstream.h"
 #include "ardour/midi_model.h"
 #include "ardour/midi_patch_manager.h"
 #include "ardour/session.h"
@@ -89,6 +89,8 @@ MidiRegionView::MidiRegionView (ArdourCanvas::Group *parent, RouteTimeAxisView &
        , _pressed_button(0)
        , _sort_needed (true)
        , _optimization_iterator (_events.end())
+       , _list_editor (0)
+       , no_sound_notes (false)
 {
        _note_group->raise_to_top();
 }
@@ -110,6 +112,8 @@ MidiRegionView::MidiRegionView (ArdourCanvas::Group *parent, RouteTimeAxisView &
        , _pressed_button(0)
        , _sort_needed (true)
        , _optimization_iterator (_events.end())
+       , _list_editor (0)
+       , no_sound_notes (false)
 
 {
        _note_group->raise_to_top();
@@ -132,6 +136,8 @@ MidiRegionView::MidiRegionView (const MidiRegionView& other)
        , _pressed_button(0)
        , _sort_needed (true)
        , _optimization_iterator (_events.end())
+       , _list_editor (0)
+       , no_sound_notes (false)
 {
        Gdk::Color c;
        int r,g,b,a;
@@ -157,6 +163,8 @@ MidiRegionView::MidiRegionView (const MidiRegionView& other, boost::shared_ptr<M
        , _pressed_button(0)
        , _sort_needed (true)
        , _optimization_iterator (_events.end())
+       , _list_editor (0)
+       , no_sound_notes (false)
 {
        Gdk::Color c;
        int r,g,b,a;
@@ -185,7 +193,7 @@ MidiRegionView::init (Gdk::Color const & basic_color, bool wfd)
 
        region_muted ();
        region_sync_changed ();
-       region_resized (BoundsChanged);
+       region_resized (ARDOUR::bounds_change);
        region_locked ();
 
        reset_width_dependent_items (_pixel_width);
@@ -517,8 +525,10 @@ MidiRegionView::canvas_event(GdkEvent* ev)
 void
 MidiRegionView::show_list_editor ()
 {
-       MidiListEditor* mle = new MidiListEditor (trackview.session(), midi_region());
-       mle->show ();
+       if (!_list_editor) {
+               _list_editor = new MidiListEditor (trackview.session(), midi_region());
+       }
+       _list_editor->present ();
 }
 
 /** Add a note to the model, and the view, at a canvas (click) coordinate.
@@ -552,7 +562,7 @@ MidiRegionView::create_note_at(double x, double y, double length)
 
        MidiModel::DeltaCommand* cmd = _model->new_delta_command("add note");
        cmd->add(new_note);
-       _model->apply_command(trackview.session(), cmd);
+       _model->apply_command(*trackview.session(), cmd);
 
        play_midi_note (new_note);
 }
@@ -585,8 +595,8 @@ MidiRegionView::display_model(boost::shared_ptr<MidiModel> model)
 {
        _model = model;
        content_connection.disconnect ();
-       content_connection = _model->ContentsChanged.connect(
-                       sigc::mem_fun(this, &MidiRegionView::redisplay_model));
+       _model->ContentsChanged.connect (content_connection, invalidator (*this), boost::bind (&MidiRegionView::redisplay_model, this), gui_context());
+
        clear_events ();
 
        if (_enable_display) {
@@ -665,9 +675,9 @@ MidiRegionView::apply_delta()
                _marked_for_selection.insert((*i)->note());
        }
 
-       _model->apply_command(trackview.session(), _delta_command);
+       _model->apply_command(*trackview.session(), _delta_command);
        _delta_command = 0;
-       midi_view()->midi_track()->diskstream()->playlist_modified();
+       midi_view()->midi_track()->playlist_modified();
 
        _marked_for_selection.clear();
        _marked_for_velocity.clear();
@@ -680,9 +690,9 @@ MidiRegionView::apply_diff ()
                return;
        }
 
-       _model->apply_command(trackview.session(), _diff_command);
+       _model->apply_command(*trackview.session(), _diff_command);
        _diff_command = 0;
-       midi_view()->midi_track()->diskstream()->playlist_modified();
+       midi_view()->midi_track()->playlist_modified();
 
        _marked_for_velocity.clear();
 }
@@ -699,9 +709,9 @@ MidiRegionView::apply_delta_as_subcommand()
                _marked_for_selection.insert((*i)->note());
        }
 
-       _model->apply_command_as_subcommand(trackview.session(), _delta_command);
+       _model->apply_command_as_subcommand(*trackview.session(), _delta_command);
        _delta_command = 0;
-       midi_view()->midi_track()->diskstream()->playlist_modified();
+       midi_view()->midi_track()->playlist_modified();
 
        _marked_for_selection.clear();
        _marked_for_velocity.clear();
@@ -719,9 +729,9 @@ MidiRegionView::apply_diff_as_subcommand()
                _marked_for_selection.insert((*i)->note());
        }
 
-       _model->apply_command_as_subcommand(trackview.session(), _diff_command);
+       _model->apply_command_as_subcommand(*trackview.session(), _diff_command);
        _diff_command = 0;
-       midi_view()->midi_track()->diskstream()->playlist_modified();
+       midi_view()->midi_track()->playlist_modified();
 
        _marked_for_selection.clear();
        _marked_for_velocity.clear();
@@ -938,6 +948,8 @@ MidiRegionView::~MidiRegionView ()
 {
        in_destructor = true;
 
+       delete _list_editor;
+
        RegionViewGoingAway (this); /* EMIT_SIGNAL */
 
        if (_active_notes) {
@@ -951,11 +963,11 @@ MidiRegionView::~MidiRegionView ()
 }
 
 void
-MidiRegionView::region_resized (Change what_changed)
+MidiRegionView::region_resized (const PropertyChange& what_changed)
 {
        RegionView::region_resized(what_changed);
 
-       if (what_changed & ARDOUR::PositionChanged) {
+       if (what_changed.contains (ARDOUR::Properties::position)) {
                set_duration(_region->length(), 0);
                if (_enable_display) {
                        redisplay_model();
@@ -1071,7 +1083,7 @@ MidiRegionView::add_ghost (TimeAxisView& tv)
                }
        }
 
-       ghost->GoingAway.connect (sigc::mem_fun(*this, &MidiRegionView::remove_ghost));
+       GhostRegion::CatchDeletion.connect (*this, invalidator (*this), ui_bind (&RegionView::remove_ghost, this, _1), gui_context());
 
        return ghost;
 }
@@ -1139,7 +1151,7 @@ MidiRegionView::extend_active_notes()
 void
 MidiRegionView::play_midi_note(boost::shared_ptr<NoteType> note)
 {
-       if (!trackview.editor().sound_notes()) {
+       if (no_sound_notes || !trackview.editor().sound_notes()) {
                return;
        }
 
@@ -1151,7 +1163,7 @@ MidiRegionView::play_midi_note(boost::shared_ptr<NoteType> note)
 
        const double note_length_beats = (note->off_event().time() - note->on_event().time());
        nframes_t note_length_ms = beats_to_frames(note_length_beats)
-                       * (1000 / (double)route_ui->session().nominal_frame_rate());
+                       * (1000 / (double)route_ui->session()->nominal_frame_rate());
        Glib::signal_timeout().connect(sigc::bind(sigc::mem_fun(this, &MidiRegionView::play_midi_note_off), note),
                        note_length_ms, G_PRIORITY_DEFAULT);
 }
@@ -1522,6 +1534,91 @@ MidiRegionView::unique_select(ArdourCanvas::CanvasNoteEvent* ev)
        }
 }
 
+void
+MidiRegionView::select_matching_notes (uint8_t notenum, uint16_t channel_mask, bool add, bool extend)
+{
+       uint8_t low_note = 127;
+       uint8_t high_note = 0;
+       MidiModel::Notes& notes (_model->notes());
+       _optimization_iterator = _events.begin();
+
+       if (extend && _selection.empty()) {
+               extend = false;
+       }
+
+       if (extend) {
+
+               /* 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();
+                       }
+                       if ((*i)->note()->note() > high_note) {
+                               high_note = (*i)->note()->note();
+                       }
+               }
+
+               low_note = min (low_note, notenum);
+               high_note = max (high_note, notenum);
+       }
+
+       no_sound_notes = true;
+
+       for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
+
+               boost::shared_ptr<NoteType> note (*n);
+               CanvasNoteEvent* cne;
+               bool select = false;
+
+               if (((0x0001 << note->channel()) & channel_mask) != 0) {
+                       if (extend) {
+                               if ((note->note() >= low_note && note->note() <= high_note)) {
+                                       select = true;
+                               }
+                       } else if (note->note() == notenum) {
+                               select = true;
+                       }
+               }
+
+               if (select) {
+                       if ((cne = find_canvas_note (note)) != 0) {
+                               // extend is false because we've taken care of it, 
+                               // since it extends by time range, not pitch.
+                               note_selected (cne, add, false);
+                       }
+               }
+               
+               add = true; // we need to add all remaining matching notes, even if the passed in value was false (for "set")
+
+       }
+
+       no_sound_notes = false;
+}
+
+void
+MidiRegionView::toggle_matching_notes (uint8_t notenum, uint16_t channel_mask)
+{
+       MidiModel::Notes& notes (_model->notes());
+       _optimization_iterator = _events.begin();
+
+       for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
+
+               boost::shared_ptr<NoteType> note (*n);
+               CanvasNoteEvent* cne;
+
+               if (note->note() == notenum && (((0x0001 << note->channel()) & channel_mask) != 0)) {
+                       if ((cne = find_canvas_note (note)) != 0) {
+                               if (cne->selected()) {
+                                       note_deselected (cne);
+                               } else {
+                                       note_selected (cne, true, false);
+                               }
+                       }
+               }
+       }
+}
+
 void
 MidiRegionView::note_selected(ArdourCanvas::CanvasNoteEvent* ev, bool add, bool extend)
 {
@@ -1659,6 +1756,7 @@ MidiRegionView::add_to_selection (CanvasNoteEvent* ev)
        }
 
        if (_selection.insert (ev).second) {
+                cerr << "Added CNE to selection, size now " << _selection.size() << endl;
                ev->selected (true);
                play_midi_note ((ev)->note());
        }
@@ -2377,8 +2475,12 @@ MidiRegionView::selection_as_cut_buffer () const
 {
        Notes notes;
 
+        cerr << "Convert selection of " << _selection.size() << " into a cut buffer\n";
+
        for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
-               notes.insert (boost::shared_ptr<NoteType> (new NoteType (*((*i)->note().get()))));
+                NoteType* n = (*i)->note().get();
+                cerr << "CNE's note is " << n << endl;
+               notes.insert (boost::shared_ptr<NoteType> (new NoteType (*n)));
        }
 
        MidiCutBuffer* cb = new MidiCutBuffer (trackview.session());
@@ -2431,11 +2533,11 @@ MidiRegionView::paste (nframes64_t pos, float times, const MidiCutBuffer& mcb)
 
        if (end_frame > region_end) {
 
-               trackview.session().begin_reversible_command (_("paste"));
+               trackview.session()->begin_reversible_command (_("paste"));
 
-               XMLNode& before (_region->get_state());
+                _region->clear_history ();
                _region->set_length (end_frame, this);
-               trackview.session().add_command (new MementoCommand<Region>(*_region, &before, &_region->get_state()));
+               trackview.session()->add_command (new StatefulDiffCommand (_region));
        }
 
        apply_delta ();