Make stuff in the automation menu apply to the track selection.
[ardour.git] / gtk2_ardour / midi_time_axis.cc
index 9477e5c02e612e5fe1bc6b3dd142cbc6e18aa355..0348e632f81f977fe64eac88e9c41ef1b7e8383e 100644 (file)
 #include "pbd/memento_command.h"
 #include "pbd/stateful_diff_command.h"
 
-#include <gtkmm2ext/gtk_ui.h>
-#include <gtkmm2ext/selector.h>
-#include <gtkmm2ext/stop_signal.h>
-#include <gtkmm2ext/bindable_button.h>
-#include <gtkmm2ext/utils.h>
+#include "gtkmm2ext/gtk_ui.h"
+#include "gtkmm2ext/selector.h"
+#include "gtkmm2ext/bindable_button.h"
+#include "gtkmm2ext/utils.h"
 
+#include "ardour/file_source.h"
 #include "ardour/midi_playlist.h"
 #include "ardour/midi_diskstream.h"
 #include "ardour/midi_patch_manager.h"
@@ -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,6 +81,7 @@
 #include "region_view.h"
 #include "rgb_macros.h"
 #include "selection.h"
+#include "step_editor.h"
 #include "simplerect.h"
 #include "utils.h"
 
@@ -115,6 +117,7 @@ MidiTimeAxisView::MidiTimeAxisView (PublicEditor& ed, Session* sess,
        , _midi_thru_item (0)
        , default_channel_menu (0)
        , controller_menu (0)
+        , _step_editor (0)
 {
        subplugin_menu.set_name ("ArdourContextMenu");
 
@@ -125,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();
@@ -164,7 +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 ();
+               
+               if (!_editor.have_idled()) {
+                       /* first idle will do what we need */
+               } else {
+                       first_idle ();
+               }
        }
 
        HBox* midi_controls_hbox = manage(new HBox());
@@ -218,6 +224,14 @@ MidiTimeAxisView::MidiTimeAxisView (PublicEditor& ed, Session* sess,
        }
 }
 
+void
+MidiTimeAxisView::first_idle ()
+{
+       if (is_track ()) {
+               _view->attach ();
+       }
+}
+
 MidiTimeAxisView::~MidiTimeAxisView ()
 {
        delete _piano_roll_header;
@@ -227,9 +241,34 @@ MidiTimeAxisView::~MidiTimeAxisView ()
        _range_scroomer = 0;
 
        delete controller_menu;
+        delete _step_editor;
 }
 
-void MidiTimeAxisView::model_changed()
+void
+MidiTimeAxisView::enter_internal_edit_mode ()
+{
+        if (midi_view()) {
+                midi_view()->enter_internal_edit_mode ();
+        }
+}
+
+void
+MidiTimeAxisView::leave_internal_edit_mode ()
+{
+        if (midi_view()) {
+                midi_view()->leave_internal_edit_mode ();
+        }
+}
+
+void
+MidiTimeAxisView::check_step_edit ()
+{
+       ensure_step_editor ();
+        _step_editor->check_step_edit ();
+}
+
+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());
@@ -282,7 +321,7 @@ MidiTimeAxisView::set_height (uint32_t h)
        RouteTimeAxisView::set_height (h);
 
        if (height >= MIDI_CONTROLS_BOX_MIN_HEIGHT) {
-               _midi_controls_box.show();
+               _midi_controls_box.show_all ();
        } else {
                _midi_controls_box.hide();
        }
@@ -326,6 +365,8 @@ MidiTimeAxisView::append_extra_display_menu_items ()
 
        items.push_back (CheckMenuElem (_("MIDI Thru"), sigc::mem_fun(*this, &MidiTimeAxisView::toggle_midi_thru)));
        _midi_thru_item = dynamic_cast<CheckMenuItem*>(&items.back());
+
+       items.push_back (SeparatorElem ());
 }
 
 Gtk::Menu*
@@ -373,11 +414,23 @@ MidiTimeAxisView::toggle_midi_thru ()
 }
 
 void
-MidiTimeAxisView::build_automation_action_menu ()
+MidiTimeAxisView::build_automation_action_menu (bool for_selection)
 {
        using namespace Menu_Helpers;
 
-       RouteTimeAxisView::build_automation_action_menu ();
+       /* If we have a controller menu, we need to detach it before
+          RouteTimeAxis::build_automation_action_menu destroys the
+          menu it is attached to.  Otherwise GTK destroys
+          controller_menu's gobj, meaning that it can't be reattached
+          below.  See bug #3134.
+       */
+          
+       if (controller_menu) {
+               detach_menu (*controller_menu);
+       }
+
+       _channel_command_menu_map.clear ();
+       RouteTimeAxisView::build_automation_action_menu (for_selection);
 
        MenuList& automation_items = automation_action_menu->items();
        
@@ -387,15 +440,16 @@ 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 PropertyChange"), MidiPgmChangeAutomation, MIDI_CMD_PGM_CHANGE);
-               add_channel_command_menu_item (automation_items, _("Bender"), MidiPitchBenderAutomation, MIDI_CMD_BENDER);
-               add_channel_command_menu_item (automation_items, _("Pressure"), MidiChannelPressureAutomation, MIDI_CMD_CHANNEL_PRESSURE);
+               add_channel_command_menu_item (automation_items, _("Bender"), MidiPitchBenderAutomation, 0);
+               automation_items.back().set_sensitive (!for_selection || _editor.get_selection().tracks.size() == 1);
+               add_channel_command_menu_item (automation_items, _("Pressure"), MidiChannelPressureAutomation, 0);
+               automation_items.back().set_sensitive (!for_selection || _editor.get_selection().tracks.size() == 1);
                
                /* now all MIDI controllers. Always offer the possibility that we will rebuild the controllers menu
                   since it might need to be updated after a channel mode change or other change. Also detach it
@@ -403,12 +457,13 @@ MidiTimeAxisView::build_automation_action_menu ()
                */
                
                build_controller_menu ();
-               detach_menu (*controller_menu);
                
                automation_items.push_back (SeparatorElem());
                automation_items.push_back (MenuElem (_("Controllers"), *controller_menu));
+               automation_items.back().set_sensitive (!for_selection || _editor.get_selection().tracks.size() == 1);
        } else {
                automation_items.push_back (MenuElem (string_compose ("<i>%1</i>", _("No MIDI Channels selected"))));
+               dynamic_cast<Label*> (automation_items.back().get_child())->set_use_markup (true);
        }
                
 }
@@ -422,10 +477,10 @@ MidiTimeAxisView::change_all_channel_tracks_visibility (bool yn, Evoral::Paramet
                if (selected_channels & (0x0001 << chn)) {
                        
                        Evoral::Parameter fully_qualified_param (param.type(), chn, param.id());
-                       RouteAutomationNode* node = automation_track (fully_qualified_param);
+                       Gtk::CheckMenuItem* menu = automation_child_menu_item (fully_qualified_param);
 
-                       if (node && node->menu_item) {
-                               node->menu_item->set_active (yn);
+                       if (menu) {
+                               menu->set_active (yn);
                        }
                }
        }
@@ -477,23 +532,18 @@ MidiTimeAxisView::add_channel_command_menu_item (Menu_Helpers::MenuList& items,
                                                                    sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
                                                                                fully_qualified_param)));
                                
-                               RouteAutomationNode* node = automation_track (fully_qualified_param);
+                               boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
                                bool visible = false;
                                
-                               if (node) {
-                                       if (node->track->marked_for_display()) {
+                               if (track) {
+                                       if (track->marked_for_display()) {
                                                visible = true;
                                        }
                                }
-                               
-                               CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&chn_items.back());
-                               if (node) {
-                                       node->menu_item = cmi;
-                               }
 
+                               CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&chn_items.back());
+                               _channel_command_menu_map[fully_qualified_param] = cmi;
                                cmi->set_active (visible);
-
-                               parameter_menu_map[fully_qualified_param] = cmi;
                        }
                }
                
@@ -513,24 +563,19 @@ MidiTimeAxisView::add_channel_command_menu_item (Menu_Helpers::MenuList& items,
                                                                sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
                                                                            fully_qualified_param)));
                                
-                               RouteAutomationNode* node = automation_track (fully_qualified_param);
+                               boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
                                bool visible = false;
                                
-                               if (node) {
-                                       if (node->track->marked_for_display()) {
+                               if (track) {
+                                       if (track->marked_for_display()) {
                                                visible = true;
                                        }
                                }
                                
                                CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&items.back());
-                               if (node) {
-                                       node->menu_item = cmi;
-                               }
-
+                               _channel_command_menu_map[fully_qualified_param] = cmi;
                                cmi->set_active (visible);
                                
-                               parameter_menu_map[fully_qualified_param] = cmi;
-
                                /* one channel only */
                                break;
                        }
@@ -570,7 +615,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) {
 
@@ -582,6 +628,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 */
@@ -609,31 +659,25 @@ MidiTimeAxisView::build_controller_menu ()
                                                                                    sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
                                                                                                fully_qualified_param)));
 
-                                               RouteAutomationNode* node = automation_track (fully_qualified_param);
+                                               boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
                                                bool visible = false;
                                                
-                                               if (node) {
-                                                       if (node->track->marked_for_display()) {
+                                               if (track) {
+                                                       if (track->marked_for_display()) {
                                                                visible = true;
                                                        }
                                                }
                                                
                                                CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&chn_items.back());
-
-                                               if (node) {
-                                                       node->menu_item = cmi;
-                                               }
-
+                                               _controller_menu_map[fully_qualified_param] = cmi;
                                                cmi->set_active (visible);
-                                               
-                                               parameter_menu_map[fully_qualified_param] = cmi;
                                        }
                                }
                                
                                /* add the per-channel menu to the list of controllers, with the name of the controller */
-                               
-                               ctl_items.push_back (MenuElem (midi_name (ctl), *chn_menu));
-
+                               ctl_items.push_back (MenuElem (string_compose ("<b>%1</b>: %2", ctl, midi_name (ctl)), *chn_menu));
+                               dynamic_cast<Label*> (ctl_items.back().get_child())->set_use_markup (true);
+                                     
                        } else {
 
                                /* just one channel - create a single menu item for this ctl+channel combination*/
@@ -642,28 +686,28 @@ MidiTimeAxisView::build_controller_menu ()
                                        if (selected_channels & (0x0001 << chn)) {
                                                
                                                Evoral::Parameter fully_qualified_param (MidiCCAutomation, chn, ctl);
-                                               ctl_items.push_back (CheckMenuElem (_route->describe_parameter (fully_qualified_param),
-                                                                                   sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
-                                                                                               fully_qualified_param)));
+                                               ctl_items.push_back (
+                                                       CheckMenuElem (
+                                                               string_compose ("<b>%1</b>: %2 [%3]", ctl, midi_name (ctl), int (chn)),
+                                                               sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
+                                                                           fully_qualified_param)
+                                                               )
+                                                       );
+                                               dynamic_cast<Label*> (ctl_items.back().get_child())->set_use_markup (true);
                                                
-                                               RouteAutomationNode* node = automation_track (fully_qualified_param);
+                                               boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
                                                bool visible = false;
                                                
-                                               if (node) {
-                                                       if (node->track->marked_for_display()) {
+                                               if (track) {
+                                                       if (track->marked_for_display()) {
                                                                visible = true;
                                                        }
                                                }
                                                
                                                CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&ctl_items.back());
-                                               if (node) {
-                                                       node->menu_item = cmi;
-                                               }
-
+                                               _controller_menu_map[fully_qualified_param] = cmi;
                                                cmi->set_active (visible);
                                                
-
-                                               parameter_menu_map[fully_qualified_param] = cmi;
                                                /* one channel only */
                                                break;
                                        }
@@ -673,7 +717,7 @@ MidiTimeAxisView::build_controller_menu ()
                        
                /* add the menu for this block of controllers to the overall controller menu */
 
-               items.push_back (MenuElem (string_compose (_("Controllers %1-%2"), i+1, i+16), *ctl_menu));
+               items.push_back (MenuElem (string_compose (_("Controllers %1-%2"), i, i+15), *ctl_menu));
        }
 }
 
@@ -803,37 +847,49 @@ MidiTimeAxisView::show_existing_automation ()
        RouteTimeAxisView::show_existing_automation ();
 }
 
-/** Hide an automation track for the given parameter (pitch bend, channel pressure).
+/** Create an automation track for the given parameter (pitch bend, channel pressure).
  */
 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<AutomationTimeAxisView> track (
+                       new AutomationTimeAxisView (
+                               _session,
+                               _route, 
+                               boost::shared_ptr<Automatable> (),
+                               boost::shared_ptr<AutomationControl> (),
+                               param,
+                               _editor,
+                               *this,
+                               true,
+                               parent_canvas,
+                               _route->describe_parameter(param)
+                               )
+                       );
+
+               if (_view) {
+                       _view->foreach_regionview (sigc::mem_fun (*track.get(), &TimeAxisView::add_ghost));
+               }
+               
+               add_automation_child (param, track, show);
+       }
 }
 
 
@@ -868,125 +924,7 @@ MidiTimeAxisView::route_active_changed ()
        }
 }
 
-void
-MidiTimeAxisView::start_step_editing ()
-{
-       step_edit_insert_position = _editor.get_preferred_edit_position ();
-       step_edit_beat_pos = 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;
-       }
-
-       midi_track()->set_step_editing (true);
-}
-
-void
-MidiTimeAxisView::stop_step_editing ()
-{
-       midi_track()->set_step_editing (false);
-}
-
-void
-MidiTimeAxisView::check_step_edit ()
-{
-       MidiRingBuffer<nframes_t>& incoming (midi_track()->step_edit_ring_buffer());
-       Evoral::Note<Evoral::MusicalTime> note;
-       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) {
-
-                       if (step_edit_region == 0) {
-
-                               step_edit_region = add_region (step_edit_insert_position);
-                               RegionView* rv = view()->find_view (step_edit_region);
-
-                               if (rv) {
-                                       step_edit_region_view = dynamic_cast<MidiRegionView*>(rv);
-                               } else {
-                                       fatal << X_("programming error: no view found for new MIDI region") << endmsg;
-                                       /*NOTREACHED*/
-                               }
-                       }
-
-                       if (step_edit_region_view) {
-
-                               bool success;
-                               Evoral::MusicalTime beats = _editor.get_grid_type_as_beats (success, step_edit_insert_position);
-
-                               if (!success) {
-                                       continue;
-                               }
-
-                               step_edit_region_view->add_note (buf[0] & 0xf, buf[1], buf[2], step_edit_beat_pos, beats);
-                               step_edit_beat_pos += beats;
-                       }
-               }
-
-       }
-}
-
-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 (nframes64_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()->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)
@@ -1070,9 +1008,9 @@ MidiTimeAxisView::set_channel_mode (ChannelMode, uint16_t)
 
                for (uint32_t chn = 0; chn < 16; ++chn) {
                        Evoral::Parameter fully_qualified_param (MidiCCAutomation, chn, ctl);
-                       RouteAutomationNode* node = automation_track (fully_qualified_param);
+                       boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
 
-                       if (!node) {
+                       if (!track) {
                                continue;
                        }
                        
@@ -1080,18 +1018,19 @@ MidiTimeAxisView::set_channel_mode (ChannelMode, uint16_t)
                                /* channel not in use. hiding it will trigger RouteTimeAxisView::automation_track_hidden() 
                                   which will cause a redraw. We don't want one per channel, so block that with no_redraw.
                                 */
-                               changed = node->track->set_visibility (false) || changed;
+                               changed = track->set_visibility (false) || changed;
                        } else {
-                               changed = node->track->set_visibility (true) || changed;
+                               changed = track->set_visibility (true) || changed;
                        }
                }
        }
 
        no_redraw = false;
 
-       /* TODO: Bender, PgmChange, Pressure */
+       /* TODO: Bender, Pressure */
 
-       /* invalidate the controller menu, so that we rebuilt it next time */
+       /* invalidate the controller menu, so that we rebuild it next time */
+       _controller_menu_map.clear ();
        delete controller_menu;
        controller_menu = 0;
 
@@ -1099,3 +1038,77 @@ MidiTimeAxisView::set_channel_mode (ChannelMode, uint16_t)
                _route->gui_changed ("track_height", this);
        }
 }
+
+Gtk::CheckMenuItem*
+MidiTimeAxisView::automation_child_menu_item (Evoral::Parameter param)
+{
+       Gtk::CheckMenuItem* m = RouteTimeAxisView::automation_child_menu_item (param);
+       if (m) {
+               return m;
+       }
+
+       ParameterMenuMap::iterator i = _controller_menu_map.find (param);
+       if (i != _controller_menu_map.end()) {
+               return i->second;
+       }
+
+       i = _channel_command_menu_map.find (param);
+       if (i != _channel_command_menu_map.end()) {
+               return i->second;
+       }
+
+       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::ensure_step_editor ()
+{
+       if (!_step_editor) {
+               _step_editor = new StepEditor (_editor, midi_track(), *this);
+       }
+}
+
+void 
+MidiTimeAxisView::start_step_editing ()
+{
+       ensure_step_editor ();
+        _step_editor->start_step_editing ();
+
+}
+void 
+MidiTimeAxisView::stop_step_editing ()
+{
+        if (_step_editor) {
+                _step_editor->stop_step_editing ();
+        }
+}