a radically new approach to sizing the track header layout that now allows ardour...
[ardour.git] / gtk2_ardour / midi_time_axis.cc
index bc36d6fbe83697fe8c233931f70dda475cfb387e..41c85a193d615d84ea47fd9194d7cc21a60df9fa 100644 (file)
@@ -52,6 +52,7 @@
 #include "ardour/session_playlist.h"
 #include "ardour/tempo.h"
 #include "ardour/utils.h"
+#include "ardour/operations.h"
 
 #include "midi++/names.h"
 
@@ -80,8 +81,8 @@
 #include "region_view.h"
 #include "rgb_macros.h"
 #include "selection.h"
+#include "step_editor.h"
 #include "simplerect.h"
-#include "step_entry.h"
 #include "utils.h"
 
 #include "ardour/midi_track.h"
@@ -116,7 +117,7 @@ MidiTimeAxisView::MidiTimeAxisView (PublicEditor& ed, Session* sess,
        , _midi_thru_item (0)
        , default_channel_menu (0)
        , controller_menu (0)
-        , step_editor (0)
+        , _step_editor (0)
 {
        subplugin_menu.set_name ("ArdourContextMenu");
 
@@ -127,8 +128,6 @@ MidiTimeAxisView::MidiTimeAxisView (PublicEditor& ed, Session* sess,
        mute_button->set_active (false);
        solo_button->set_active (false);
 
-       step_edit_insert_position = 0;
-
        if (is_midi_track()) {
                controls_ebox.set_name ("MidiTimeAxisViewControlsBaseUnselected");
                _note_mode = midi_track()->note_mode();
@@ -166,13 +165,12 @@ MidiTimeAxisView::MidiTimeAxisView (PublicEditor& ed, Session* sess,
 
                /* ask for notifications of any new RegionViews */
                _view->RegionViewAdded.connect (sigc::mem_fun(*this, &MidiTimeAxisView::region_view_added));
-               _view->attach ();
-                
-                midi_track()->PlaylistChanged.connect (*this, invalidator (*this),
-                                                       boost::bind (&MidiTimeAxisView::playlist_changed, this),
-                                                       gui_context());
-                playlist_changed ();
-
+               
+               if (!_editor.have_idled()) {
+                       /* first idle will do what we need */
+               } else {
+                       first_idle ();
+               }
        }
 
        HBox* midi_controls_hbox = manage(new HBox());
@@ -226,6 +224,14 @@ MidiTimeAxisView::MidiTimeAxisView (PublicEditor& ed, Session* sess,
        }
 }
 
+void
+MidiTimeAxisView::first_idle ()
+{
+       if (is_track ()) {
+               _view->attach ();
+       }
+}
+
 MidiTimeAxisView::~MidiTimeAxisView ()
 {
        delete _piano_roll_header;
@@ -235,34 +241,17 @@ MidiTimeAxisView::~MidiTimeAxisView ()
        _range_scroomer = 0;
 
        delete controller_menu;
+        delete _step_editor;
 }
 
 void
-MidiTimeAxisView::playlist_changed ()
-{
-        step_edit_region_connection.disconnect ();
-        midi_track()->playlist()->RegionRemoved.connect (step_edit_region_connection, invalidator (*this),
-                                                         ui_bind (&MidiTimeAxisView::region_removed, this, _1),
-                                                         gui_context());
-}
-
-void
-MidiTimeAxisView::region_removed (boost::weak_ptr<Region> wr)
+MidiTimeAxisView::check_step_edit ()
 {
-        boost::shared_ptr<Region> r (wr.lock());
-        if (!r) {
-                return;
-        }
-
-        if (step_edit_region == r) {
-                step_edit_region.reset();
-                // force a recompute of the insert position
-                step_edit_beat_pos = -1.0;
-        }
+        _step_editor->check_step_edit ();
 }
 
-void MidiTimeAxisView::model_changed()
+void 
+MidiTimeAxisView::model_changed()
 {
        std::list<std::string> device_modes = MIDI::Name::MidiPatchManager::instance()
                .custom_device_mode_names_by_model(_model_selector.get_active_text());
@@ -432,13 +421,12 @@ MidiTimeAxisView::build_automation_action_menu ()
 
                automation_items.push_back (SeparatorElem());
 
-               /* these 3 MIDI "command" types are semantically more like automation than note data,
+               /* these 2 MIDI "command" types are semantically more like automation than note data,
                   but they are not MIDI controllers. We give them special status in this menu, since
                   they will not show up in the controller list and anyone who actually knows
                   something about MIDI (!) would not expect to find them there.
                */
 
-               add_channel_command_menu_item (automation_items, _("Program Change"), MidiPgmChangeAutomation, 0);
                add_channel_command_menu_item (automation_items, _("Bender"), MidiPitchBenderAutomation, 0);
                add_channel_command_menu_item (automation_items, _("Pressure"), MidiChannelPressureAutomation, 0);
                
@@ -604,7 +592,8 @@ MidiTimeAxisView::build_controller_menu ()
                }
        }
        
-       /* loop over all 127 MIDI controllers, in groups of 16 */
+       /* loop over all 127 MIDI controllers, in groups of 16; except don't offer
+          bank select controllers, as they are handled by the `patch' code */
 
        for (int i = 0; i < 127; i += 16) {
 
@@ -616,6 +605,10 @@ MidiTimeAxisView::build_controller_menu ()
 
                for (int ctl = i; ctl < i+16; ++ctl) {
 
+                       if (ctl == MIDI_CTL_MSB_BANK || ctl == MIDI_CTL_LSB_BANK) {
+                               continue;
+                       }
+
                        if (chn_cnt > 1) {
 
                                /* multiple channels - create a submenu, with 1 item per channel */
@@ -831,32 +824,36 @@ MidiTimeAxisView::show_existing_automation ()
 void
 MidiTimeAxisView::create_automation_child (const Evoral::Parameter& param, bool show)
 {
-       /* These controllers are region "automation", so we do not create
-        * an AutomationList/Line for the track */
-
        if (param.type() == NullAutomation) {
                cerr << "WARNING: Attempt to create NullAutomation child, ignoring" << endl;
                return;
        }
-
+       
        AutomationTracks::iterator existing = _automation_tracks.find (param);
        if (existing != _automation_tracks.end()) {
                return;
        }
-
-       boost::shared_ptr<AutomationControl> c = _route->get_control (param);
-
-       assert(c);
-
-       boost::shared_ptr<AutomationTimeAxisView> track(new AutomationTimeAxisView (_session,
-                       _route, boost::shared_ptr<ARDOUR::Automatable>(), c,
-                       _editor,
-                       *this,
-                       true,
-                       parent_canvas,
-                       _route->describe_parameter(param)));
-
-       add_automation_child (param, track, show);
+               
+       if (param.type() == GainAutomation) {
+               create_gain_automation_child (param, show);
+       } else {
+               
+               /* These controllers are region "automation", so we do not create
+                * an AutomationList/Line for the track */
+               
+               boost::shared_ptr<AutomationControl> c = _route->get_control (param);
+               assert (c);
+               
+               boost::shared_ptr<AutomationTimeAxisView> track(new AutomationTimeAxisView (_session,
+                                                                                           _route, boost::shared_ptr<ARDOUR::Automatable>(), c,
+                                                                                           _editor,
+                                                                                           *this,
+                                                                                           true,
+                                                                                           parent_canvas,
+                                                                                           _route->describe_parameter(param)));
+               
+               add_automation_child (param, track, show);
+       }
 }
 
 
@@ -891,203 +888,7 @@ MidiTimeAxisView::route_active_changed ()
        }
 }
 
-void
-MidiTimeAxisView::start_step_editing ()
-{
-       step_edit_insert_position = _editor.get_preferred_edit_position ();
-        step_edit_beat_pos = -1.0;
-        _step_edit_triplet_countdown = 0;
-        _step_edit_within_chord = 0;
-        _step_edit_chord_duration = 0.0;
-
-       step_edit_region = playlist()->top_region_at (step_edit_insert_position);
-
-       if (step_edit_region) {
-               RegionView* rv = view()->find_view (step_edit_region);
-               step_edit_region_view = dynamic_cast<MidiRegionView*> (rv);
-       } else {
-               step_edit_region_view = 0;
-       }
-
-
-        if (step_editor == 0) {
-                step_editor = new StepEntry (*this);
-                step_editor->signal_delete_event().connect (sigc::mem_fun (*this, &MidiTimeAxisView::step_editor_hidden));
-        }
-
-        step_editor->set_position (WIN_POS_MOUSE);
-        step_editor->present ();
-}
-
-bool
-MidiTimeAxisView::step_editor_hidden (GdkEventAny*)
-{
-        /* everything else will follow the change in the model */
-       midi_track()->set_step_editing (false);
-        return true;
-}
-
-void
-MidiTimeAxisView::stop_step_editing ()
-{
-        if (step_editor) {
-                step_editor->hide ();
-        }
-}
-
-void
-MidiTimeAxisView::check_step_edit ()
-{
-       MidiRingBuffer<nframes_t>& incoming (midi_track()->step_edit_ring_buffer());
-       uint8_t* buf;
-       uint32_t bufsize = 32;
-
-       buf = new uint8_t[bufsize];
-
-       while (incoming.read_space()) {
-               nframes_t time;
-               Evoral::EventType type;
-               uint32_t size;
-
-               incoming.read_prefix (&time, &type, &size);
-
-               if (size > bufsize) {
-                       delete [] buf;
-                       bufsize = size;
-                       buf = new uint8_t[bufsize];
-               }
-
-               incoming.read_contents (size, buf);
-
-               if ((buf[0] & 0xf0) == MIDI_CMD_NOTE_ON) {
-                        step_add_note (buf[0] & 0xf, buf[1], buf[2], 0.0);
-               }
-       }
-}
-
-int
-MidiTimeAxisView::step_add_note (uint8_t channel, uint8_t pitch, uint8_t velocity, Evoral::MusicalTime beat_duration)
-{
-        if (step_edit_region == 0) {
-                
-                step_edit_region = add_region (step_edit_insert_position);
-                RegionView* rv = view()->find_view (step_edit_region);
-                step_edit_region_view = dynamic_cast<MidiRegionView*>(rv);
-        }
-        
-        if (step_edit_region && step_edit_region_view) {
-                if (step_edit_beat_pos < 0.0) {
-                        framecnt_t frames_from_start = _editor.get_preferred_edit_position() - step_edit_region->position();
-                        if (frames_from_start < 0) {
-                                return 1;
-                        }
-                        step_edit_beat_pos = step_edit_region_view->frames_to_beats (frames_from_start);
-                }
-                
-                if (beat_duration == 0.0) {
-                        bool success;
-                        beat_duration = _editor.get_grid_type_as_beats (success, step_edit_insert_position);
-                        
-                        if (!success) {
-                                return -1;
-                        }
-                }
-
-                step_edit_region_view->step_add_note (channel, pitch, velocity, step_edit_beat_pos, beat_duration);
-
-                if (_step_edit_triplet_countdown > 0) {
-                        _step_edit_triplet_countdown--;
-
-                        if (_step_edit_triplet_countdown == 0) {
-                                _step_edit_triplet_countdown = 3;
-                        }
-                }
-
-                if (!_step_edit_within_chord) {
-                        step_edit_beat_pos += beat_duration;
-                } else {
-                        step_edit_beat_pos += 1.0/Meter::ticks_per_beat; // tiny, but no longer overlapping
-                        _step_edit_chord_duration = beat_duration;
-                }
-        }
-
-        return 0;
-}
-
-bool
-MidiTimeAxisView::step_edit_within_triplet() const
-{
-        return _step_edit_triplet_countdown > 0;
-}
-
-bool
-MidiTimeAxisView::step_edit_within_chord() const
-{
-        return _step_edit_within_chord;
-}
 
-void
-MidiTimeAxisView::step_edit_toggle_triplet ()
-{
-        if (_step_edit_triplet_countdown == 0) {
-                _step_edit_within_chord = false;
-                _step_edit_triplet_countdown = 3;
-        } else {
-                _step_edit_triplet_countdown = 0;
-        }
-}
-
-void
-MidiTimeAxisView::step_edit_toggle_chord ()
-{
-        if (_step_edit_within_chord) {
-                _step_edit_within_chord = false;
-                step_edit_beat_pos += _step_edit_chord_duration;
-        } else {
-                _step_edit_triplet_countdown = 0;
-                _step_edit_within_chord = true;
-        }
-}
-
-void
-MidiTimeAxisView::step_edit_rest ()
-{
-       bool success;
-       Evoral::MusicalTime beats = _editor.get_grid_type_as_beats (success, step_edit_insert_position);
-       step_edit_beat_pos += beats;
-}
-
-boost::shared_ptr<Region>
-MidiTimeAxisView::add_region (framepos_t pos)
-{
-       Editor* real_editor = dynamic_cast<Editor*> (&_editor);
-
-       real_editor->begin_reversible_command (_("create region"));
-        playlist()->clear_history ();
-
-       framepos_t start = pos;
-       real_editor->snap_to (start, -1);
-       const Meter& m = _session->tempo_map().meter_at(start);
-       const Tempo& t = _session->tempo_map().tempo_at(start);
-       double length = floor (m.frames_per_bar(t, _session->frame_rate()));
-
-       boost::shared_ptr<Source> src = _session->create_midi_source_for_session (view()->trackview().track().get(),
-                                                                                  view()->trackview().track()->name());
-       PropertyList plist; 
-       
-       plist.add (ARDOUR::Properties::start, 0);
-       plist.add (ARDOUR::Properties::length, length);
-       plist.add (ARDOUR::Properties::name, PBD::basename_nosuffix(src->name()));
-       
-       boost::shared_ptr<Region> region = (RegionFactory::create (src, plist));
-        
-       playlist()->add_region (region, start);
-       _session->add_command (new StatefulDiffCommand (playlist()));
-
-       real_editor->commit_reversible_command();
-
-       return region;
-}
 
 void
 MidiTimeAxisView::add_note_selection (uint8_t note)
@@ -1190,7 +991,7 @@ MidiTimeAxisView::set_channel_mode (ChannelMode, uint16_t)
 
        no_redraw = false;
 
-       /* TODO: Bender, PgmChange, Pressure */
+       /* TODO: Bender, Pressure */
 
        /* invalidate the controller menu, so that we rebuilt it next time */
        _controller_menu_map.clear ();
@@ -1222,3 +1023,51 @@ MidiTimeAxisView::automation_child_menu_item (Evoral::Parameter param)
 
        return 0;
 }
+
+boost::shared_ptr<MidiRegion>
+MidiTimeAxisView::add_region (framepos_t pos, framecnt_t length, bool commit)
+{
+       Editor* real_editor = dynamic_cast<Editor*> (&_editor);
+
+       real_editor->begin_reversible_command (Operations::create_region);
+        playlist()->clear_changes ();
+
+       real_editor->snap_to (pos, 0);
+       
+       boost::shared_ptr<Source> src = _session->create_midi_source_for_session (view()->trackview().track().get(),
+                                                                                  view()->trackview().track()->name());
+       PropertyList plist; 
+       
+       plist.add (ARDOUR::Properties::start, 0);
+       plist.add (ARDOUR::Properties::length, length);
+       plist.add (ARDOUR::Properties::name, PBD::basename_nosuffix(src->name()));
+       
+       boost::shared_ptr<Region> region = (RegionFactory::create (src, plist));
+        
+       playlist()->add_region (region, pos);
+       _session->add_command (new StatefulDiffCommand (playlist()));
+
+       if (commit) {
+               real_editor->commit_reversible_command ();
+       }
+
+       return boost::dynamic_pointer_cast<MidiRegion>(region);
+}
+
+void 
+MidiTimeAxisView::start_step_editing ()
+{
+        if (!_step_editor) {
+                _step_editor = new StepEditor (_editor, midi_track(), *this);
+        }
+
+        _step_editor->start_step_editing ();
+
+}
+void 
+MidiTimeAxisView::stop_step_editing ()
+{
+        if (_step_editor) {
+                _step_editor->stop_step_editing ();
+        }
+}