464ed630f5453741153e19ceaed47fbe799b1b8c
[ardour.git] / gtk2_ardour / midi_time_axis.cc
1 /*
2     Copyright (C) 2000 Paul Davis
3
4     This program is free software; you can redistribute it and/or modify
5     it under the terms of the GNU General Public License as published by
6     the Free Software Foundation; either version 2 of the License, or
7     (at your option) any later version.
8
9     This program is distributed in the hope that it will be useful,
10     but WITHOUT ANY WARRANTY; without even the implied warranty of
11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12     GNU General Public License for more details.
13
14     You should have received a copy of the GNU General Public License
15     along with this program; if not, write to the Free Software
16     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17 */
18
19 #include <cstdlib>
20 #include <cmath>
21
22 #include <algorithm>
23 #include <string>
24 #include <vector>
25
26 #include <sigc++/bind.h>
27
28 #include "pbd/error.h"
29 #include "pbd/stl_delete.h"
30 #include "pbd/whitespace.h"
31 #include "pbd/basename.h"
32 #include "pbd/enumwriter.h"
33 #include "pbd/memento_command.h"
34 #include "pbd/stateful_diff_command.h"
35
36 #include "gtkmm2ext/gtk_ui.h"
37 #include "gtkmm2ext/selector.h"
38 #include "gtkmm2ext/bindable_button.h"
39 #include "gtkmm2ext/utils.h"
40
41 #include "ardour/file_source.h"
42 #include "ardour/midi_playlist.h"
43 #include "ardour/midi_diskstream.h"
44 #include "ardour/midi_patch_manager.h"
45 #include "ardour/midi_source.h"
46 #include "ardour/processor.h"
47 #include "ardour/ladspa_plugin.h"
48 #include "ardour/location.h"
49 #include "ardour/playlist.h"
50 #include "ardour/region_factory.h"
51 #include "ardour/session.h"
52 #include "ardour/session_playlist.h"
53 #include "ardour/tempo.h"
54 #include "ardour/utils.h"
55
56 #include "midi++/names.h"
57
58 #include "add_midi_cc_track_dialog.h"
59 #include "ardour_ui.h"
60 #include "automation_line.h"
61 #include "automation_time_axis.h"
62 #include "canvas-note-event.h"
63 #include "canvas_impl.h"
64 #include "crossfade_view.h"
65 #include "editor.h"
66 #include "enums.h"
67 #include "ghostregion.h"
68 #include "gui_thread.h"
69 #include "keyboard.h"
70 #include "midi_scroomer.h"
71 #include "midi_streamview.h"
72 #include "midi_region_view.h"
73 #include "midi_time_axis.h"
74 #include "piano_roll_header.h"
75 #include "playlist_selector.h"
76 #include "plugin_selector.h"
77 #include "plugin_ui.h"
78 #include "point_selection.h"
79 #include "prompter.h"
80 #include "region_view.h"
81 #include "rgb_macros.h"
82 #include "selection.h"
83 #include "simplerect.h"
84 #include "step_entry.h"
85 #include "utils.h"
86
87 #include "ardour/midi_track.h"
88
89 #include "i18n.h"
90
91 using namespace ARDOUR;
92 using namespace PBD;
93 using namespace Gtk;
94 using namespace Gtkmm2ext;
95 using namespace Editing;
96
97 // Minimum height at which a control is displayed
98 static const uint32_t MIDI_CONTROLS_BOX_MIN_HEIGHT = 162;
99 static const uint32_t KEYBOARD_MIN_HEIGHT = 140;
100
101 MidiTimeAxisView::MidiTimeAxisView (PublicEditor& ed, Session* sess,
102                 boost::shared_ptr<Route> rt, Canvas& canvas)
103         : AxisView(sess) // virtually inherited
104         , RouteTimeAxisView(ed, sess, rt, canvas)
105         , _ignore_signals(false)
106         , _range_scroomer(0)
107         , _piano_roll_header(0)
108         , _note_mode(Sustained)
109         , _note_mode_item(0)
110         , _percussion_mode_item(0)
111         , _color_mode(MeterColors)
112         , _meter_color_mode_item(0)
113         , _channel_color_mode_item(0)
114         , _track_color_mode_item(0)
115         , _step_edit_item (0)
116         , _midi_thru_item (0)
117         , default_channel_menu (0)
118         , controller_menu (0)
119         , step_editor (0)
120 {
121         subplugin_menu.set_name ("ArdourContextMenu");
122
123         _view = new MidiStreamView (*this);
124
125         ignore_toggle = false;
126         
127         mute_button->set_active (false);
128         solo_button->set_active (false);
129
130         step_edit_insert_position = 0;
131
132         if (is_midi_track()) {
133                 controls_ebox.set_name ("MidiTimeAxisViewControlsBaseUnselected");
134                 _note_mode = midi_track()->note_mode();
135         } else { // MIDI bus (which doesn't exist yet..)
136                 controls_ebox.set_name ("MidiBusControlsBaseUnselected");
137         }
138
139         /* map current state of the route */
140
141         processors_changed (RouteProcessorChange ());
142
143         ensure_xml_node ();
144
145         set_state (*xml_node, Stateful::loading_state_version);
146
147         _route->processors_changed.connect (*this, invalidator (*this), ui_bind (&MidiTimeAxisView::processors_changed, this, _1), gui_context());
148
149         if (is_track()) {
150                 _piano_roll_header = new PianoRollHeader(*midi_view());
151
152                 _piano_roll_header->AddNoteSelection.connect (sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection));
153                 _piano_roll_header->ExtendNoteSelection.connect (sigc::mem_fun (*this, &MidiTimeAxisView::extend_note_selection));
154                 _piano_roll_header->ToggleNoteSelection.connect (sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection));
155
156                 _range_scroomer = new MidiScroomer(midi_view()->note_range_adjustment);
157
158                 controls_hbox.pack_start(*_range_scroomer);
159                 controls_hbox.pack_start(*_piano_roll_header);
160
161                 controls_ebox.set_name ("MidiTrackControlsBaseUnselected");
162                 controls_base_selected_name = "MidiTrackControlsBaseSelected";
163                 controls_base_unselected_name = "MidiTrackControlsBaseUnselected";
164
165                 midi_view()->NoteRangeChanged.connect (sigc::mem_fun(*this, &MidiTimeAxisView::update_range));
166
167                 /* ask for notifications of any new RegionViews */
168                 _view->RegionViewAdded.connect (sigc::mem_fun(*this, &MidiTimeAxisView::region_view_added));
169                 _view->attach ();
170                 
171                 midi_track()->PlaylistChanged.connect (*this, invalidator (*this),
172                                                        boost::bind (&MidiTimeAxisView::playlist_changed, this),
173                                                        gui_context());
174                 playlist_changed ();
175
176         }
177
178         HBox* midi_controls_hbox = manage(new HBox());
179
180         MIDI::Name::MidiPatchManager& patch_manager = MIDI::Name::MidiPatchManager::instance();
181
182         MIDI::Name::MasterDeviceNames::Models::const_iterator m = patch_manager.all_models().begin();
183         for (; m != patch_manager.all_models().end(); ++m) {
184                 _model_selector.append_text(m->c_str());
185         }
186
187         _model_selector.signal_changed().connect(sigc::mem_fun(*this, &MidiTimeAxisView::model_changed));
188
189         _custom_device_mode_selector.signal_changed().connect(
190                         sigc::mem_fun(*this, &MidiTimeAxisView::custom_device_mode_changed));
191
192         // TODO: persist the choice
193         // this initializes the comboboxes and sends out the signal
194         _model_selector.set_active(0);
195
196         midi_controls_hbox->pack_start(_channel_selector, true, false);
197         if (!patch_manager.all_models().empty()) {
198                 _midi_controls_box.pack_start(_model_selector, true, false);
199                 _midi_controls_box.pack_start(_custom_device_mode_selector, true, false);
200         }
201
202         _midi_controls_box.pack_start(*midi_controls_hbox, true, true);
203
204         controls_vbox.pack_start(_midi_controls_box, false, false);
205
206         // restore channel selector settings
207         _channel_selector.set_channel_mode(midi_track()->get_channel_mode(), midi_track()->get_channel_mask());
208         _channel_selector.mode_changed.connect(
209                 sigc::mem_fun(*midi_track(), &MidiTrack::set_channel_mode));
210         _channel_selector.mode_changed.connect(
211                 sigc::mem_fun(*this, &MidiTimeAxisView::set_channel_mode));
212
213         XMLProperty *prop;
214         if ((prop = xml_node->property ("color-mode")) != 0) {
215                 _color_mode = ColorMode (string_2_enum(prop->value(), _color_mode));
216                 if (_color_mode == ChannelColors) {
217                         _channel_selector.set_channel_colors(CanvasNoteEvent::midi_channel_colors);
218                 }
219         }
220
221         if ((prop = xml_node->property ("note-mode")) != 0) {
222                 _note_mode = NoteMode (string_2_enum(prop->value(), _note_mode));
223                 if (_percussion_mode_item) {
224                         _percussion_mode_item->set_active (_note_mode == Percussive);
225                 }
226         }
227 }
228
229 MidiTimeAxisView::~MidiTimeAxisView ()
230 {
231         delete _piano_roll_header;
232         _piano_roll_header = 0;
233
234         delete _range_scroomer;
235         _range_scroomer = 0;
236
237         delete controller_menu;
238 }
239
240 void
241 MidiTimeAxisView::playlist_changed ()
242 {
243         step_edit_region_connection.disconnect ();
244         midi_track()->playlist()->RegionRemoved.connect (step_edit_region_connection, invalidator (*this),
245                                                          ui_bind (&MidiTimeAxisView::region_removed, this, _1),
246                                                          gui_context());
247 }
248
249 void
250 MidiTimeAxisView::region_removed (boost::weak_ptr<Region> wr)
251 {
252         boost::shared_ptr<Region> r (wr.lock());
253  
254         if (!r) {
255                 return;
256         }
257
258         if (step_edit_region == r) {
259                 step_edit_region.reset();
260                 // force a recompute of the insert position
261                 step_edit_beat_pos = -1.0;
262         }
263 }
264
265 void MidiTimeAxisView::model_changed()
266 {
267         std::list<std::string> device_modes = MIDI::Name::MidiPatchManager::instance()
268                 .custom_device_mode_names_by_model(_model_selector.get_active_text());
269
270         _custom_device_mode_selector.clear_items();
271
272         for (std::list<std::string>::const_iterator i = device_modes.begin();
273                         i != device_modes.end(); ++i) {
274                 cerr << "found custom device mode " << *i << " thread_id: " << pthread_self() << endl;
275                 _custom_device_mode_selector.append_text(*i);
276         }
277
278         _custom_device_mode_selector.set_active(0);
279 }
280
281 void MidiTimeAxisView::custom_device_mode_changed()
282 {
283         _midi_patch_settings_changed.emit(_model_selector.get_active_text(),
284                         _custom_device_mode_selector.get_active_text());
285 }
286
287 MidiStreamView*
288 MidiTimeAxisView::midi_view()
289 {
290         return dynamic_cast<MidiStreamView*>(_view);
291 }
292
293 guint32
294 MidiTimeAxisView::show_at (double y, int& nth, Gtk::VBox *parent)
295 {
296         ensure_xml_node ();
297         xml_node->add_property ("shown-editor", "yes");
298
299         guint32 ret = TimeAxisView::show_at (y, nth, parent);
300         return ret;
301 }
302
303 void
304 MidiTimeAxisView::hide ()
305 {
306         ensure_xml_node ();
307         xml_node->add_property ("shown-editor", "no");
308
309         TimeAxisView::hide ();
310 }
311
312 void
313 MidiTimeAxisView::set_height (uint32_t h)
314 {
315         RouteTimeAxisView::set_height (h);
316
317         if (height >= MIDI_CONTROLS_BOX_MIN_HEIGHT) {
318                 _midi_controls_box.show_all ();
319         } else {
320                 _midi_controls_box.hide();
321         }
322
323         if (height >= KEYBOARD_MIN_HEIGHT) {
324                 if (is_track() && _range_scroomer)
325                         _range_scroomer->show();
326                 if (is_track() && _piano_roll_header)
327                         _piano_roll_header->show();
328         } else {
329                 if (is_track() && _range_scroomer)
330                         _range_scroomer->hide();
331                 if (is_track() && _piano_roll_header)
332                         _piano_roll_header->hide();
333         }
334 }
335
336 void
337 MidiTimeAxisView::append_extra_display_menu_items ()
338 {
339         using namespace Menu_Helpers;
340
341         MenuList& items = display_menu->items();
342
343         // Note range
344         Menu *range_menu = manage(new Menu);
345         MenuList& range_items = range_menu->items();
346         range_menu->set_name ("ArdourContextMenu");
347
348         range_items.push_back (MenuElem (_("Show Full Range"), sigc::bind (
349                         sigc::mem_fun(*this, &MidiTimeAxisView::set_note_range),
350                         MidiStreamView::FullRange)));
351
352         range_items.push_back (MenuElem (_("Fit Contents"), sigc::bind (
353                         sigc::mem_fun(*this, &MidiTimeAxisView::set_note_range),
354                         MidiStreamView::ContentsRange)));
355
356         items.push_back (MenuElem (_("Note range"), *range_menu));
357         items.push_back (MenuElem (_("Note mode"), *build_note_mode_menu()));
358         items.push_back (MenuElem (_("Default Channel"), *build_def_channel_menu()));
359
360         items.push_back (CheckMenuElem (_("MIDI Thru"), sigc::mem_fun(*this, &MidiTimeAxisView::toggle_midi_thru)));
361         _midi_thru_item = dynamic_cast<CheckMenuItem*>(&items.back());
362 }
363
364 Gtk::Menu*
365 MidiTimeAxisView::build_def_channel_menu ()
366 {
367         using namespace Menu_Helpers;
368
369         default_channel_menu = manage (new Menu ());
370
371         uint8_t defchn = midi_track()->default_channel();
372         MenuList& def_channel_items = default_channel_menu->items();
373         RadioMenuItem* item;
374         RadioMenuItem::Group dc_group;
375
376         for (int i = 0; i < 16; ++i) {
377                 char buf[4];
378                 snprintf (buf, sizeof (buf), "%d", i+1);
379
380                 def_channel_items.push_back (RadioMenuElem (dc_group, buf,
381                                                             sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_default_channel), i)));
382                 item = dynamic_cast<RadioMenuItem*>(&def_channel_items.back());
383                 item->set_active ((i == defchn));
384         }
385
386         return default_channel_menu;
387 }
388
389 void
390 MidiTimeAxisView::set_default_channel (int chn)
391 {
392         midi_track()->set_default_channel (chn);
393 }
394
395 void
396 MidiTimeAxisView::toggle_midi_thru ()
397 {
398         if (!_midi_thru_item) {
399                 return;
400         }
401
402         bool view_yn = _midi_thru_item->get_active();
403         if (view_yn != midi_track()->midi_thru()) {
404                 midi_track()->set_midi_thru (view_yn);
405         }
406 }
407
408 void
409 MidiTimeAxisView::build_automation_action_menu ()
410 {
411         using namespace Menu_Helpers;
412
413         /* If we have a controller menu, we need to detach it before
414            RouteTimeAxis::build_automation_action_menu destroys the
415            menu it is attached to.  Otherwise GTK destroys
416            controller_menu's gobj, meaning that it can't be reattached
417            below.  See bug #3134.
418         */
419            
420         if (controller_menu) {
421                 detach_menu (*controller_menu);
422         }
423
424         _channel_command_menu_map.clear ();
425         RouteTimeAxisView::build_automation_action_menu ();
426
427         MenuList& automation_items = automation_action_menu->items();
428         
429         uint16_t selected_channels = _channel_selector.get_selected_channels();
430
431         if (selected_channels !=  0) {
432
433                 automation_items.push_back (SeparatorElem());
434
435                 /* these 3 MIDI "command" types are semantically more like automation than note data,
436                    but they are not MIDI controllers. We give them special status in this menu, since
437                    they will not show up in the controller list and anyone who actually knows
438                    something about MIDI (!) would not expect to find them there.
439                 */
440
441                 add_channel_command_menu_item (automation_items, _("Program Change"), MidiPgmChangeAutomation, 0);
442                 add_channel_command_menu_item (automation_items, _("Bender"), MidiPitchBenderAutomation, 0);
443                 add_channel_command_menu_item (automation_items, _("Pressure"), MidiChannelPressureAutomation, 0);
444                 
445                 /* now all MIDI controllers. Always offer the possibility that we will rebuild the controllers menu
446                    since it might need to be updated after a channel mode change or other change. Also detach it
447                    first in case it has been used anywhere else.
448                 */
449                 
450                 build_controller_menu ();
451                 
452                 automation_items.push_back (SeparatorElem());
453                 automation_items.push_back (MenuElem (_("Controllers"), *controller_menu));
454         } else {
455                 automation_items.push_back (MenuElem (string_compose ("<i>%1</i>", _("No MIDI Channels selected"))));
456         }
457                 
458 }
459
460 void
461 MidiTimeAxisView::change_all_channel_tracks_visibility (bool yn, Evoral::Parameter param)
462 {
463         uint16_t selected_channels = _channel_selector.get_selected_channels();
464         
465         for (uint8_t chn = 0; chn < 16; chn++) {
466                 if (selected_channels & (0x0001 << chn)) {
467                         
468                         Evoral::Parameter fully_qualified_param (param.type(), chn, param.id());
469                         Gtk::CheckMenuItem* menu = automation_child_menu_item (fully_qualified_param);
470
471                         if (menu) {
472                                 menu->set_active (yn);
473                         }
474                 }
475         }
476 }
477
478 void
479 MidiTimeAxisView::add_channel_command_menu_item (Menu_Helpers::MenuList& items, const string& label, AutomationType auto_type, uint8_t cmd)
480 {
481         using namespace Menu_Helpers;
482
483         /* count the number of selected channels because we will build a different menu structure if there is more than 1 selected.
484          */
485
486         uint16_t selected_channels = _channel_selector.get_selected_channels();
487         int chn_cnt = 0;
488         
489         for (uint8_t chn = 0; chn < 16; chn++) {
490                 if (selected_channels & (0x0001 << chn)) {
491                         if (++chn_cnt > 1) {
492                                 break;
493                         }
494                 }
495         }
496         
497         if (chn_cnt > 1) {
498                 
499                 /* multiple channels - create a submenu, with 1 item per channel */
500                 
501                 Menu* chn_menu = manage (new Menu);
502                 MenuList& chn_items (chn_menu->items());
503                 Evoral::Parameter param_without_channel (auto_type, 0, cmd);
504
505                 /* add a couple of items to hide/show all of them */
506
507                 chn_items.push_back (MenuElem (_("Hide all channels"),
508                                                     sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility), 
509                                                                 false, param_without_channel)));
510                 chn_items.push_back (MenuElem (_("Show all channels"),
511                                                     sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility), 
512                                                                 true, param_without_channel)));
513                 
514                 for (uint8_t chn = 0; chn < 16; chn++) {
515                         if (selected_channels & (0x0001 << chn)) {
516                                 
517                                 /* for each selected channel, add a menu item for this controller */
518                                 
519                                 Evoral::Parameter fully_qualified_param (auto_type, chn, cmd);
520                                 chn_items.push_back (CheckMenuElem (string_compose (_("Channel %1"), chn+1),
521                                                                     sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
522                                                                                 fully_qualified_param)));
523                                 
524                                 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
525                                 bool visible = false;
526                                 
527                                 if (track) {
528                                         if (track->marked_for_display()) {
529                                                 visible = true;
530                                         }
531                                 }
532
533                                 CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&chn_items.back());
534                                 _channel_command_menu_map[fully_qualified_param] = cmi;
535                                 cmi->set_active (visible);
536                         }
537                 }
538                 
539                 /* now create an item in the parent menu that has the per-channel list as a submenu */
540                         
541                 items.push_back (MenuElem (label, *chn_menu));
542                 
543         } else {
544                 
545                 /* just one channel - create a single menu item for this command+channel combination*/
546                 
547                 for (uint8_t chn = 0; chn < 16; chn++) {
548                         if (selected_channels & (0x0001 << chn)) {
549                                 
550                                 Evoral::Parameter fully_qualified_param (auto_type, chn, cmd);
551                                 items.push_back (CheckMenuElem (label,
552                                                                 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
553                                                                             fully_qualified_param)));
554                                 
555                                 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
556                                 bool visible = false;
557                                 
558                                 if (track) {
559                                         if (track->marked_for_display()) {
560                                                 visible = true;
561                                         }
562                                 }
563                                 
564                                 CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&items.back());
565                                 _channel_command_menu_map[fully_qualified_param] = cmi;
566                                 cmi->set_active (visible);
567                                 
568                                 /* one channel only */
569                                 break;
570                         }
571                 }
572         }
573 }
574
575 void
576 MidiTimeAxisView::build_controller_menu ()
577 {
578         using namespace Menu_Helpers;
579
580         if (controller_menu) {
581                 /* it exists and has not been invalidated by a channel mode change, so just return it */
582                 return;
583         }
584
585         controller_menu = new Menu; // explicitly managed by us
586         MenuList& items (controller_menu->items());
587
588         /* create several "top level" menu items for sets of controllers (16 at a time), and populate each one with a submenu 
589            for each controller+channel combination covering the currently selected channels for this track
590         */
591
592         uint16_t selected_channels = _channel_selector.get_selected_channels();
593
594         /* count the number of selected channels because we will build a different menu structure if there is more than 1 selected.
595          */
596
597         int chn_cnt = 0;
598         
599         for (uint8_t chn = 0; chn < 16; chn++) {
600                 if (selected_channels & (0x0001 << chn)) {
601                         if (++chn_cnt > 1) {
602                                 break;
603                         }
604                 }
605         }
606         
607         /* loop over all 127 MIDI controllers, in groups of 16 */
608
609         for (int i = 0; i < 127; i += 16) {
610
611                 Menu* ctl_menu = manage (new Menu);
612                 MenuList& ctl_items (ctl_menu->items());
613
614
615                 /* for each controller, consider whether to create a submenu or a single item */
616
617                 for (int ctl = i; ctl < i+16; ++ctl) {
618
619                         if (chn_cnt > 1) {
620
621                                 /* multiple channels - create a submenu, with 1 item per channel */
622
623                                 Menu* chn_menu = manage (new Menu);
624                                 MenuList& chn_items (chn_menu->items());
625
626                                 /* add a couple of items to hide/show this controller on all channels */
627                                 
628                                 Evoral::Parameter param_without_channel (MidiCCAutomation, 0, ctl);
629                                 chn_items.push_back (MenuElem (_("Hide all channels"),
630                                                                     sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility), 
631                                                                                 false, param_without_channel)));
632                                 chn_items.push_back (MenuElem (_("Show all channels"),
633                                                                     sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility), 
634                                                                                 true, param_without_channel)));
635                 
636                                 for (uint8_t chn = 0; chn < 16; chn++) {
637                                         if (selected_channels & (0x0001 << chn)) {
638                                                 
639                                                 /* for each selected channel, add a menu item for this controller */
640                                                 
641                                                 Evoral::Parameter fully_qualified_param (MidiCCAutomation, chn, ctl);
642                                                 chn_items.push_back (CheckMenuElem (string_compose (_("Channel %1"), chn+1),
643                                                                                     sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
644                                                                                                 fully_qualified_param)));
645
646                                                 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
647                                                 bool visible = false;
648                                                 
649                                                 if (track) {
650                                                         if (track->marked_for_display()) {
651                                                                 visible = true;
652                                                         }
653                                                 }
654                                                 
655                                                 CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&chn_items.back());
656                                                 _controller_menu_map[fully_qualified_param] = cmi;
657                                                 cmi->set_active (visible);
658                                         }
659                                 }
660                                 
661                                 /* add the per-channel menu to the list of controllers, with the name of the controller */
662                                 ctl_items.push_back (MenuElem (string_compose ("<b>%1</b>: %2", ctl, midi_name (ctl)), *chn_menu));
663                                 dynamic_cast<Label*> (ctl_items.back().get_child())->set_use_markup (true);
664                                       
665                         } else {
666
667                                 /* just one channel - create a single menu item for this ctl+channel combination*/
668
669                                 for (uint8_t chn = 0; chn < 16; chn++) {
670                                         if (selected_channels & (0x0001 << chn)) {
671                                                 
672                                                 Evoral::Parameter fully_qualified_param (MidiCCAutomation, chn, ctl);
673                                                 ctl_items.push_back (CheckMenuElem (_route->describe_parameter (fully_qualified_param),
674                                                                                     sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
675                                                                                                 fully_qualified_param)));
676                                                 
677                                                 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
678                                                 bool visible = false;
679                                                 
680                                                 if (track) {
681                                                         if (track->marked_for_display()) {
682                                                                 visible = true;
683                                                         }
684                                                 }
685                                                 
686                                                 CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&ctl_items.back());
687                                                 _controller_menu_map[fully_qualified_param] = cmi;
688                                                 cmi->set_active (visible);
689                                                 
690                                                 /* one channel only */
691                                                 break;
692                                         }
693                                 }
694                         }
695                 }
696                         
697                 /* add the menu for this block of controllers to the overall controller menu */
698
699                 items.push_back (MenuElem (string_compose (_("Controllers %1-%2"), i, i+15), *ctl_menu));
700         }
701 }
702
703 Gtk::Menu*
704 MidiTimeAxisView::build_note_mode_menu()
705 {
706         using namespace Menu_Helpers;
707
708         Menu* mode_menu = manage (new Menu);
709         MenuList& items = mode_menu->items();
710         mode_menu->set_name ("ArdourContextMenu");
711
712         RadioMenuItem::Group mode_group;
713         items.push_back (RadioMenuElem (mode_group, _("Sustained"),
714                                 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_mode), Sustained)));
715         _note_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
716         _note_mode_item->set_active(_note_mode == Sustained);
717
718         items.push_back (RadioMenuElem (mode_group, _("Percussive"),
719                                 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_mode), Percussive)));
720         _percussion_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
721         _percussion_mode_item->set_active(_note_mode == Percussive);
722
723         return mode_menu;
724 }
725
726 Gtk::Menu*
727 MidiTimeAxisView::build_color_mode_menu()
728 {
729         using namespace Menu_Helpers;
730
731         Menu* mode_menu = manage (new Menu);
732         MenuList& items = mode_menu->items();
733         mode_menu->set_name ("ArdourContextMenu");
734
735         RadioMenuItem::Group mode_group;
736         items.push_back (RadioMenuElem (mode_group, _("Meter Colors"),
737                                 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode), MeterColors)));
738         _meter_color_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
739         _meter_color_mode_item->set_active(_color_mode == MeterColors);
740
741         items.push_back (RadioMenuElem (mode_group, _("Channel Colors"),
742                                 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode), ChannelColors)));
743         _channel_color_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
744         _channel_color_mode_item->set_active(_color_mode == ChannelColors);
745
746         items.push_back (RadioMenuElem (mode_group, _("Track Color"),
747                                 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode), TrackColor)));
748         _channel_color_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
749         _channel_color_mode_item->set_active(_color_mode == TrackColor);
750
751         return mode_menu;
752 }
753
754 void
755 MidiTimeAxisView::set_note_mode(NoteMode mode)
756 {
757         if (_note_mode != mode || midi_track()->note_mode() != mode) {
758                 _note_mode = mode;
759                 midi_track()->set_note_mode(mode);
760                 xml_node->add_property ("note-mode", enum_2_string(_note_mode));
761                 _view->redisplay_track();
762         }
763 }
764
765 void
766 MidiTimeAxisView::set_color_mode(ColorMode mode)
767 {
768         if (_color_mode != mode) {
769                 if (mode == ChannelColors) {
770                         _channel_selector.set_channel_colors(CanvasNoteEvent::midi_channel_colors);
771                 } else {
772                         _channel_selector.set_default_channel_color();
773                 }
774
775                 _color_mode = mode;
776                 xml_node->add_property ("color-mode", enum_2_string(_color_mode));
777                 _view->redisplay_track();
778         }
779 }
780
781 void
782 MidiTimeAxisView::set_note_range(MidiStreamView::VisibleNoteRange range)
783 {
784         if (!_ignore_signals)
785                 midi_view()->set_note_range(range);
786 }
787
788
789 void
790 MidiTimeAxisView::update_range()
791 {
792         MidiGhostRegion* mgr;
793
794         for(list<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
795                 if ((mgr = dynamic_cast<MidiGhostRegion*>(*i)) != 0) {
796                         mgr->update_range();
797                 }
798         }
799 }
800
801 void
802 MidiTimeAxisView::show_all_automation ()
803 {
804         if (midi_track()) {
805                 const set<Evoral::Parameter> params = midi_track()->midi_playlist()->contained_automation();
806
807                 for (set<Evoral::Parameter>::const_iterator i = params.begin(); i != params.end(); ++i) {
808                         create_automation_child(*i, true);
809                 }
810         }
811
812         RouteTimeAxisView::show_all_automation ();
813 }
814
815 void
816 MidiTimeAxisView::show_existing_automation ()
817 {
818         if (midi_track()) {
819                 const set<Evoral::Parameter> params = midi_track()->midi_playlist()->contained_automation();
820
821                 for (set<Evoral::Parameter>::const_iterator i = params.begin(); i != params.end(); ++i) {
822                         create_automation_child(*i, true);
823                 }
824         }
825
826         RouteTimeAxisView::show_existing_automation ();
827 }
828
829 /** Create an automation track for the given parameter (pitch bend, channel pressure).
830  */
831 void
832 MidiTimeAxisView::create_automation_child (const Evoral::Parameter& param, bool show)
833 {
834         /* These controllers are region "automation", so we do not create
835          * an AutomationList/Line for the track */
836
837         if (param.type() == NullAutomation) {
838                 cerr << "WARNING: Attempt to create NullAutomation child, ignoring" << endl;
839                 return;
840         }
841
842         AutomationTracks::iterator existing = _automation_tracks.find (param);
843         if (existing != _automation_tracks.end()) {
844                 return;
845         }
846
847         boost::shared_ptr<AutomationControl> c = _route->get_control (param);
848
849         assert(c);
850
851         boost::shared_ptr<AutomationTimeAxisView> track(new AutomationTimeAxisView (_session,
852                         _route, boost::shared_ptr<ARDOUR::Automatable>(), c,
853                         _editor,
854                         *this,
855                         true,
856                         parent_canvas,
857                         _route->describe_parameter(param)));
858
859         add_automation_child (param, track, show);
860 }
861
862
863 void
864 MidiTimeAxisView::route_active_changed ()
865 {
866         RouteUI::route_active_changed ();
867
868         if (is_track()) {
869                 if (_route->active()) {
870                         controls_ebox.set_name ("MidiTrackControlsBaseUnselected");
871                         controls_base_selected_name = "MidiTrackControlsBaseSelected";
872                         controls_base_unselected_name = "MidiTrackControlsBaseUnselected";
873                 } else {
874                         controls_ebox.set_name ("MidiTrackControlsBaseInactiveUnselected");
875                         controls_base_selected_name = "MidiTrackControlsBaseInactiveSelected";
876                         controls_base_unselected_name = "MidiTrackControlsBaseInactiveUnselected";
877                 }
878         } else {
879
880                 throw; // wha?
881
882                 if (_route->active()) {
883                         controls_ebox.set_name ("BusControlsBaseUnselected");
884                         controls_base_selected_name = "BusControlsBaseSelected";
885                         controls_base_unselected_name = "BusControlsBaseUnselected";
886                 } else {
887                         controls_ebox.set_name ("BusControlsBaseInactiveUnselected");
888                         controls_base_selected_name = "BusControlsBaseInactiveSelected";
889                         controls_base_unselected_name = "BusControlsBaseInactiveUnselected";
890                 }
891         }
892 }
893
894 void
895 MidiTimeAxisView::start_step_editing ()
896 {
897         step_edit_insert_position = _editor.get_preferred_edit_position ();
898         _step_edit_triplet_countdown = 0;
899         _step_edit_within_chord = 0;
900         _step_edit_chord_duration = 0.0;
901         
902         boost::shared_ptr<Region> r = playlist()->top_region_at (step_edit_insert_position);
903
904         if (r) {
905                 step_edit_region = boost::dynamic_pointer_cast<MidiRegion>(r);
906         }
907
908         if (step_edit_region) {
909                 RegionView* rv = view()->find_view (step_edit_region);
910                 step_edit_region_view = dynamic_cast<MidiRegionView*> (rv);
911
912         } else {
913                 step_edit_region = add_region (step_edit_insert_position);
914                 RegionView* rv = view()->find_view (step_edit_region);
915                 step_edit_region_view = dynamic_cast<MidiRegionView*>(rv);
916         }
917
918         assert (step_edit_region);
919         assert (step_edit_region_view);
920
921         if (step_editor == 0) {
922                 step_editor = new StepEntry (*this);
923                 step_editor->signal_delete_event().connect (sigc::mem_fun (*this, &MidiTimeAxisView::step_editor_hidden));
924                 step_editor->signal_hide().connect (sigc::mem_fun (*this, &MidiTimeAxisView::step_editor_hide));
925         }
926
927         framecnt_t frames_from_start = _editor.get_preferred_edit_position() - step_edit_region->position();
928
929         if (frames_from_start < 0) {
930                 /* this can happen with snap enabled, and the edit point == Playhead. we snap the
931                    position of the new region, and it can end up after the edit point.
932                 */
933                 frames_from_start = 0;
934         }
935
936         step_edit_beat_pos = step_edit_region_view->frames_to_beats (frames_from_start);
937
938         step_edit_region_view->show_step_edit_cursor (step_edit_beat_pos);
939         step_edit_region_view->set_step_edit_cursor_width (step_editor->note_length());
940
941         step_editor->set_position (WIN_POS_MOUSE);
942         step_editor->present ();
943 }
944
945 bool
946 MidiTimeAxisView::step_editor_hidden (GdkEventAny*)
947 {
948         step_editor_hide ();
949         return true;
950 }
951
952 void
953 MidiTimeAxisView::step_editor_hide ()
954 {
955         /* everything else will follow the change in the model */
956         midi_track()->set_step_editing (false);
957 }
958
959 void
960 MidiTimeAxisView::stop_step_editing ()
961 {
962         if (step_editor) {
963                 step_editor->hide ();
964         }
965
966         if (step_edit_region_view) {
967                 step_edit_region_view->hide_step_edit_cursor();
968         }
969
970         step_edit_region.reset ();
971 }
972
973 void
974 MidiTimeAxisView::check_step_edit ()
975 {
976         MidiRingBuffer<nframes_t>& incoming (midi_track()->step_edit_ring_buffer());
977         uint8_t* buf;
978         uint32_t bufsize = 32;
979
980         buf = new uint8_t[bufsize];
981
982         while (incoming.read_space()) {
983                 nframes_t time;
984                 Evoral::EventType type;
985                 uint32_t size;
986
987                 incoming.read_prefix (&time, &type, &size);
988
989                 if (size > bufsize) {
990                         delete [] buf;
991                         bufsize = size;
992                         buf = new uint8_t[bufsize];
993                 }
994
995                 incoming.read_contents (size, buf);
996
997                 if ((buf[0] & 0xf0) == MIDI_CMD_NOTE_ON) {
998                         step_add_note (buf[0] & 0xf, buf[1], buf[2], 0.0);
999                 }
1000         }
1001 }
1002
1003 int
1004 MidiTimeAxisView::step_add_bank_change (uint8_t channel, uint8_t bank)
1005 {
1006         return 0;
1007 }
1008
1009 int
1010 MidiTimeAxisView::step_add_program_change (uint8_t channel, uint8_t program)
1011 {
1012         return 0;
1013 }
1014
1015 void
1016 MidiTimeAxisView::step_edit_sustain (Evoral::MusicalTime beats)
1017 {
1018         if (step_edit_region_view) {
1019                 step_edit_region_view->step_sustain (beats);
1020         }
1021 }
1022
1023 int
1024 MidiTimeAxisView::step_add_note (uint8_t channel, uint8_t pitch, uint8_t velocity, Evoral::MusicalTime beat_duration)
1025 {
1026         if (step_edit_region && step_edit_region_view) {
1027                 
1028                 if (beat_duration == 0.0) {
1029                         bool success;
1030                         beat_duration = _editor.get_grid_type_as_beats (success, step_edit_insert_position);
1031                         
1032                         if (!success) {
1033                                 return -1;
1034                         }
1035                 }
1036
1037                 MidiStreamView* msv = midi_view();
1038                 
1039                 /* make sure its visible on the vertical axis */
1040                 
1041                 if (pitch < msv->lowest_note() || pitch > msv->highest_note()) {
1042                         msv->update_note_range (pitch);
1043                         msv->set_note_range (MidiStreamView::ContentsRange);
1044                 }
1045
1046                 /* make sure its visible on the horizontal axis */
1047
1048                 nframes64_t fpos = step_edit_region->position() + 
1049                         step_edit_region_view->beats_to_frames (step_edit_beat_pos + beat_duration);
1050
1051                 if (fpos >= (_editor.leftmost_position() + _editor.current_page_frames())) {
1052                         _editor.reset_x_origin (fpos - (_editor.current_page_frames()/4));
1053                 }
1054
1055                 step_edit_region_view->step_add_note (channel, pitch, velocity, step_edit_beat_pos, beat_duration);
1056
1057                 if (_step_edit_triplet_countdown > 0) {
1058                         _step_edit_triplet_countdown--;
1059
1060                         if (_step_edit_triplet_countdown == 0) {
1061                                 _step_edit_triplet_countdown = 3;
1062                         }
1063                 }
1064
1065                 if (!_step_edit_within_chord) {
1066                         step_edit_beat_pos += beat_duration;
1067                         step_edit_region_view->move_step_edit_cursor (step_edit_beat_pos);
1068                 } else {
1069                         step_edit_beat_pos += 1.0/Meter::ticks_per_beat; // tiny, but no longer overlapping
1070                         _step_edit_chord_duration = max (_step_edit_chord_duration, beat_duration);
1071                 }
1072         }
1073
1074         return 0;
1075 }
1076
1077 void
1078 MidiTimeAxisView::set_step_edit_cursor_width (Evoral::MusicalTime beats)
1079 {
1080         if (step_edit_region_view) {
1081                 step_edit_region_view->set_step_edit_cursor_width (beats);
1082         }
1083 }
1084
1085 bool
1086 MidiTimeAxisView::step_edit_within_triplet() const
1087 {
1088         return _step_edit_triplet_countdown > 0;
1089 }
1090
1091 bool
1092 MidiTimeAxisView::step_edit_within_chord() const
1093 {
1094         return _step_edit_within_chord;
1095 }
1096
1097 void
1098 MidiTimeAxisView::step_edit_toggle_triplet ()
1099 {
1100         if (_step_edit_triplet_countdown == 0) {
1101                 _step_edit_within_chord = false;
1102                 _step_edit_triplet_countdown = 3;
1103         } else {
1104                 _step_edit_triplet_countdown = 0;
1105         }
1106 }
1107
1108 void
1109 MidiTimeAxisView::step_edit_toggle_chord ()
1110 {
1111         if (_step_edit_within_chord) {
1112                 _step_edit_within_chord = false;
1113                 step_edit_beat_pos += _step_edit_chord_duration;
1114                 step_edit_region_view->move_step_edit_cursor (step_edit_beat_pos);
1115         } else {
1116                 _step_edit_triplet_countdown = 0;
1117                 _step_edit_within_chord = true;
1118         }
1119         
1120         cerr << "Within chord now: " << _step_edit_within_chord << endl;
1121 }
1122
1123 void
1124 MidiTimeAxisView::step_edit_rest (Evoral::MusicalTime beats)
1125 {
1126         bool success;
1127
1128         if (beats == 0.0) {
1129                 beats = _editor.get_grid_type_as_beats (success, step_edit_insert_position);
1130         } else {
1131                 success = true;
1132         }
1133
1134         if (success) {
1135                 step_edit_beat_pos += beats;
1136                 step_edit_region_view->move_step_edit_cursor (step_edit_beat_pos);
1137         }
1138 }
1139
1140 void 
1141 MidiTimeAxisView::step_edit_beat_sync ()
1142 {
1143         step_edit_beat_pos = ceil (step_edit_beat_pos);
1144         step_edit_region_view->move_step_edit_cursor (step_edit_beat_pos);
1145 }
1146
1147 void 
1148 MidiTimeAxisView::step_edit_bar_sync ()
1149 {
1150         if (!_session || !step_edit_region_view || !step_edit_region) {
1151                 return;
1152         }
1153
1154         framepos_t fpos = step_edit_region->position() + 
1155                 step_edit_region_view->beats_to_frames (step_edit_beat_pos);
1156         fpos = _session->tempo_map().round_to_bar (fpos, 1);
1157         step_edit_beat_pos = ceil (step_edit_region_view->frames_to_beats (fpos - step_edit_region->position()));
1158         step_edit_region_view->move_step_edit_cursor (step_edit_beat_pos);
1159 }
1160
1161 boost::shared_ptr<MidiRegion>
1162 MidiTimeAxisView::add_region (framepos_t pos)
1163 {
1164         Editor* real_editor = dynamic_cast<Editor*> (&_editor);
1165
1166         real_editor->begin_reversible_command (_("create region"));
1167         playlist()->clear_history ();
1168
1169         real_editor->snap_to (pos, 0);
1170         const Meter& m = _session->tempo_map().meter_at(pos);
1171         const Tempo& t = _session->tempo_map().tempo_at(pos);
1172         double length = floor (m.frames_per_bar(t, _session->frame_rate()));
1173
1174         boost::shared_ptr<Source> src = _session->create_midi_source_for_session (view()->trackview().track().get(),
1175                                                                                   view()->trackview().track()->name());
1176         PropertyList plist; 
1177         
1178         plist.add (ARDOUR::Properties::start, 0);
1179         plist.add (ARDOUR::Properties::length, length);
1180         plist.add (ARDOUR::Properties::name, PBD::basename_nosuffix(src->name()));
1181         
1182         boost::shared_ptr<Region> region = (RegionFactory::create (src, plist));
1183         
1184         playlist()->add_region (region, pos);
1185         _session->add_command (new StatefulDiffCommand (playlist()));
1186
1187         real_editor->commit_reversible_command();
1188
1189         return boost::dynamic_pointer_cast<MidiRegion>(region);
1190 }
1191
1192 void
1193 MidiTimeAxisView::add_note_selection (uint8_t note)
1194 {
1195         if (!_editor.internal_editing()) {
1196                 return;
1197         }
1198
1199         uint16_t chn_mask = _channel_selector.get_selected_channels();
1200
1201         if (_view->num_selected_regionviews() == 0) {
1202                 _view->foreach_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection_region_view), note, chn_mask));
1203         } else {
1204                 _view->foreach_selected_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection_region_view), note, chn_mask));
1205         }
1206 }
1207
1208 void
1209 MidiTimeAxisView::extend_note_selection (uint8_t note)
1210 {
1211         if (!_editor.internal_editing()) {
1212                 return;
1213         }
1214
1215         uint16_t chn_mask = _channel_selector.get_selected_channels();
1216
1217         if (_view->num_selected_regionviews() == 0) {
1218                 _view->foreach_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::extend_note_selection_region_view), note, chn_mask));
1219         } else {
1220                 _view->foreach_selected_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::extend_note_selection_region_view), note, chn_mask));
1221         }
1222 }
1223
1224 void
1225 MidiTimeAxisView::toggle_note_selection (uint8_t note)
1226 {
1227         if (!_editor.internal_editing()) {
1228                 return;
1229         }
1230
1231         uint16_t chn_mask = _channel_selector.get_selected_channels();
1232
1233         if (_view->num_selected_regionviews() == 0) {
1234                 _view->foreach_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection_region_view), note, chn_mask));
1235         } else {
1236                 _view->foreach_selected_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection_region_view), note, chn_mask));
1237         }
1238 }
1239
1240 void
1241 MidiTimeAxisView::add_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1242 {
1243         dynamic_cast<MidiRegionView*>(rv)->select_matching_notes (note, chn_mask, false, false);
1244 }
1245
1246 void
1247 MidiTimeAxisView::extend_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1248 {
1249         dynamic_cast<MidiRegionView*>(rv)->select_matching_notes (note, chn_mask, true, true);
1250 }
1251
1252 void
1253 MidiTimeAxisView::toggle_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1254 {
1255         dynamic_cast<MidiRegionView*>(rv)->toggle_matching_notes (note, chn_mask);
1256 }
1257
1258 void
1259 MidiTimeAxisView::set_channel_mode (ChannelMode, uint16_t)
1260 {
1261         /* hide all automation tracks that use the wrong channel(s) and show all those that use
1262            the right ones.
1263         */
1264
1265         uint16_t selected_channels = _channel_selector.get_selected_channels();
1266         bool changed = false;
1267
1268         no_redraw = true;
1269
1270         for (uint32_t ctl = 0; ctl < 127; ++ctl) {
1271
1272                 for (uint32_t chn = 0; chn < 16; ++chn) {
1273                         Evoral::Parameter fully_qualified_param (MidiCCAutomation, chn, ctl);
1274                         boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
1275
1276                         if (!track) {
1277                                 continue;
1278                         }
1279                         
1280                         if ((selected_channels & (0x0001 << chn)) == 0) {
1281                                 /* channel not in use. hiding it will trigger RouteTimeAxisView::automation_track_hidden() 
1282                                    which will cause a redraw. We don't want one per channel, so block that with no_redraw.
1283                                  */
1284                                 changed = track->set_visibility (false) || changed;
1285                         } else {
1286                                 changed = track->set_visibility (true) || changed;
1287                         }
1288                 }
1289         }
1290
1291         no_redraw = false;
1292
1293         /* TODO: Bender, PgmChange, Pressure */
1294
1295         /* invalidate the controller menu, so that we rebuilt it next time */
1296         _controller_menu_map.clear ();
1297         delete controller_menu;
1298         controller_menu = 0;
1299
1300         if (changed) {
1301                 _route->gui_changed ("track_height", this);
1302         }
1303 }
1304
1305 Gtk::CheckMenuItem*
1306 MidiTimeAxisView::automation_child_menu_item (Evoral::Parameter param)
1307 {
1308         Gtk::CheckMenuItem* m = RouteTimeAxisView::automation_child_menu_item (param);
1309         if (m) {
1310                 return m;
1311         }
1312
1313         ParameterMenuMap::iterator i = _controller_menu_map.find (param);
1314         if (i != _controller_menu_map.end()) {
1315                 return i->second;
1316         }
1317
1318         i = _channel_command_menu_map.find (param);
1319         if (i != _channel_command_menu_map.end()) {
1320                 return i->second;
1321         }
1322
1323         return 0;
1324 }