step editor: add double, triple dotted note support + back + resync-to-ep buttons
[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                 step_edit_region_view = 0;
261                 // force a recompute of the insert position
262                 step_edit_beat_pos = -1.0;
263         }
264 }
265
266 void MidiTimeAxisView::model_changed()
267 {
268         std::list<std::string> device_modes = MIDI::Name::MidiPatchManager::instance()
269                 .custom_device_mode_names_by_model(_model_selector.get_active_text());
270
271         _custom_device_mode_selector.clear_items();
272
273         for (std::list<std::string>::const_iterator i = device_modes.begin();
274                         i != device_modes.end(); ++i) {
275                 cerr << "found custom device mode " << *i << " thread_id: " << pthread_self() << endl;
276                 _custom_device_mode_selector.append_text(*i);
277         }
278
279         _custom_device_mode_selector.set_active(0);
280 }
281
282 void MidiTimeAxisView::custom_device_mode_changed()
283 {
284         _midi_patch_settings_changed.emit(_model_selector.get_active_text(),
285                         _custom_device_mode_selector.get_active_text());
286 }
287
288 MidiStreamView*
289 MidiTimeAxisView::midi_view()
290 {
291         return dynamic_cast<MidiStreamView*>(_view);
292 }
293
294 guint32
295 MidiTimeAxisView::show_at (double y, int& nth, Gtk::VBox *parent)
296 {
297         ensure_xml_node ();
298         xml_node->add_property ("shown-editor", "yes");
299
300         guint32 ret = TimeAxisView::show_at (y, nth, parent);
301         return ret;
302 }
303
304 void
305 MidiTimeAxisView::hide ()
306 {
307         ensure_xml_node ();
308         xml_node->add_property ("shown-editor", "no");
309
310         TimeAxisView::hide ();
311 }
312
313 void
314 MidiTimeAxisView::set_height (uint32_t h)
315 {
316         RouteTimeAxisView::set_height (h);
317
318         if (height >= MIDI_CONTROLS_BOX_MIN_HEIGHT) {
319                 _midi_controls_box.show_all ();
320         } else {
321                 _midi_controls_box.hide();
322         }
323
324         if (height >= KEYBOARD_MIN_HEIGHT) {
325                 if (is_track() && _range_scroomer)
326                         _range_scroomer->show();
327                 if (is_track() && _piano_roll_header)
328                         _piano_roll_header->show();
329         } else {
330                 if (is_track() && _range_scroomer)
331                         _range_scroomer->hide();
332                 if (is_track() && _piano_roll_header)
333                         _piano_roll_header->hide();
334         }
335 }
336
337 void
338 MidiTimeAxisView::append_extra_display_menu_items ()
339 {
340         using namespace Menu_Helpers;
341
342         MenuList& items = display_menu->items();
343
344         // Note range
345         Menu *range_menu = manage(new Menu);
346         MenuList& range_items = range_menu->items();
347         range_menu->set_name ("ArdourContextMenu");
348
349         range_items.push_back (MenuElem (_("Show Full Range"), sigc::bind (
350                         sigc::mem_fun(*this, &MidiTimeAxisView::set_note_range),
351                         MidiStreamView::FullRange)));
352
353         range_items.push_back (MenuElem (_("Fit Contents"), sigc::bind (
354                         sigc::mem_fun(*this, &MidiTimeAxisView::set_note_range),
355                         MidiStreamView::ContentsRange)));
356
357         items.push_back (MenuElem (_("Note range"), *range_menu));
358         items.push_back (MenuElem (_("Note mode"), *build_note_mode_menu()));
359         items.push_back (MenuElem (_("Default Channel"), *build_def_channel_menu()));
360
361         items.push_back (CheckMenuElem (_("MIDI Thru"), sigc::mem_fun(*this, &MidiTimeAxisView::toggle_midi_thru)));
362         _midi_thru_item = dynamic_cast<CheckMenuItem*>(&items.back());
363 }
364
365 Gtk::Menu*
366 MidiTimeAxisView::build_def_channel_menu ()
367 {
368         using namespace Menu_Helpers;
369
370         default_channel_menu = manage (new Menu ());
371
372         uint8_t defchn = midi_track()->default_channel();
373         MenuList& def_channel_items = default_channel_menu->items();
374         RadioMenuItem* item;
375         RadioMenuItem::Group dc_group;
376
377         for (int i = 0; i < 16; ++i) {
378                 char buf[4];
379                 snprintf (buf, sizeof (buf), "%d", i+1);
380
381                 def_channel_items.push_back (RadioMenuElem (dc_group, buf,
382                                                             sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_default_channel), i)));
383                 item = dynamic_cast<RadioMenuItem*>(&def_channel_items.back());
384                 item->set_active ((i == defchn));
385         }
386
387         return default_channel_menu;
388 }
389
390 void
391 MidiTimeAxisView::set_default_channel (int chn)
392 {
393         midi_track()->set_default_channel (chn);
394 }
395
396 void
397 MidiTimeAxisView::toggle_midi_thru ()
398 {
399         if (!_midi_thru_item) {
400                 return;
401         }
402
403         bool view_yn = _midi_thru_item->get_active();
404         if (view_yn != midi_track()->midi_thru()) {
405                 midi_track()->set_midi_thru (view_yn);
406         }
407 }
408
409 void
410 MidiTimeAxisView::build_automation_action_menu ()
411 {
412         using namespace Menu_Helpers;
413
414         /* If we have a controller menu, we need to detach it before
415            RouteTimeAxis::build_automation_action_menu destroys the
416            menu it is attached to.  Otherwise GTK destroys
417            controller_menu's gobj, meaning that it can't be reattached
418            below.  See bug #3134.
419         */
420            
421         if (controller_menu) {
422                 detach_menu (*controller_menu);
423         }
424
425         _channel_command_menu_map.clear ();
426         RouteTimeAxisView::build_automation_action_menu ();
427
428         MenuList& automation_items = automation_action_menu->items();
429         
430         uint16_t selected_channels = _channel_selector.get_selected_channels();
431
432         if (selected_channels !=  0) {
433
434                 automation_items.push_back (SeparatorElem());
435
436                 /* these 3 MIDI "command" types are semantically more like automation than note data,
437                    but they are not MIDI controllers. We give them special status in this menu, since
438                    they will not show up in the controller list and anyone who actually knows
439                    something about MIDI (!) would not expect to find them there.
440                 */
441
442                 add_channel_command_menu_item (automation_items, _("Program Change"), MidiPgmChangeAutomation, 0);
443                 add_channel_command_menu_item (automation_items, _("Bender"), MidiPitchBenderAutomation, 0);
444                 add_channel_command_menu_item (automation_items, _("Pressure"), MidiChannelPressureAutomation, 0);
445                 
446                 /* now all MIDI controllers. Always offer the possibility that we will rebuild the controllers menu
447                    since it might need to be updated after a channel mode change or other change. Also detach it
448                    first in case it has been used anywhere else.
449                 */
450                 
451                 build_controller_menu ();
452                 
453                 automation_items.push_back (SeparatorElem());
454                 automation_items.push_back (MenuElem (_("Controllers"), *controller_menu));
455         } else {
456                 automation_items.push_back (MenuElem (string_compose ("<i>%1</i>", _("No MIDI Channels selected"))));
457         }
458                 
459 }
460
461 void
462 MidiTimeAxisView::change_all_channel_tracks_visibility (bool yn, Evoral::Parameter param)
463 {
464         uint16_t selected_channels = _channel_selector.get_selected_channels();
465         
466         for (uint8_t chn = 0; chn < 16; chn++) {
467                 if (selected_channels & (0x0001 << chn)) {
468                         
469                         Evoral::Parameter fully_qualified_param (param.type(), chn, param.id());
470                         Gtk::CheckMenuItem* menu = automation_child_menu_item (fully_qualified_param);
471
472                         if (menu) {
473                                 menu->set_active (yn);
474                         }
475                 }
476         }
477 }
478
479 void
480 MidiTimeAxisView::add_channel_command_menu_item (Menu_Helpers::MenuList& items, const string& label, AutomationType auto_type, uint8_t cmd)
481 {
482         using namespace Menu_Helpers;
483
484         /* count the number of selected channels because we will build a different menu structure if there is more than 1 selected.
485          */
486
487         uint16_t selected_channels = _channel_selector.get_selected_channels();
488         int chn_cnt = 0;
489         
490         for (uint8_t chn = 0; chn < 16; chn++) {
491                 if (selected_channels & (0x0001 << chn)) {
492                         if (++chn_cnt > 1) {
493                                 break;
494                         }
495                 }
496         }
497         
498         if (chn_cnt > 1) {
499                 
500                 /* multiple channels - create a submenu, with 1 item per channel */
501                 
502                 Menu* chn_menu = manage (new Menu);
503                 MenuList& chn_items (chn_menu->items());
504                 Evoral::Parameter param_without_channel (auto_type, 0, cmd);
505
506                 /* add a couple of items to hide/show all of them */
507
508                 chn_items.push_back (MenuElem (_("Hide all channels"),
509                                                     sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility), 
510                                                                 false, param_without_channel)));
511                 chn_items.push_back (MenuElem (_("Show all channels"),
512                                                     sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility), 
513                                                                 true, param_without_channel)));
514                 
515                 for (uint8_t chn = 0; chn < 16; chn++) {
516                         if (selected_channels & (0x0001 << chn)) {
517                                 
518                                 /* for each selected channel, add a menu item for this controller */
519                                 
520                                 Evoral::Parameter fully_qualified_param (auto_type, chn, cmd);
521                                 chn_items.push_back (CheckMenuElem (string_compose (_("Channel %1"), chn+1),
522                                                                     sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
523                                                                                 fully_qualified_param)));
524                                 
525                                 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
526                                 bool visible = false;
527                                 
528                                 if (track) {
529                                         if (track->marked_for_display()) {
530                                                 visible = true;
531                                         }
532                                 }
533
534                                 CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&chn_items.back());
535                                 _channel_command_menu_map[fully_qualified_param] = cmi;
536                                 cmi->set_active (visible);
537                         }
538                 }
539                 
540                 /* now create an item in the parent menu that has the per-channel list as a submenu */
541                         
542                 items.push_back (MenuElem (label, *chn_menu));
543                 
544         } else {
545                 
546                 /* just one channel - create a single menu item for this command+channel combination*/
547                 
548                 for (uint8_t chn = 0; chn < 16; chn++) {
549                         if (selected_channels & (0x0001 << chn)) {
550                                 
551                                 Evoral::Parameter fully_qualified_param (auto_type, chn, cmd);
552                                 items.push_back (CheckMenuElem (label,
553                                                                 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
554                                                                             fully_qualified_param)));
555                                 
556                                 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
557                                 bool visible = false;
558                                 
559                                 if (track) {
560                                         if (track->marked_for_display()) {
561                                                 visible = true;
562                                         }
563                                 }
564                                 
565                                 CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&items.back());
566                                 _channel_command_menu_map[fully_qualified_param] = cmi;
567                                 cmi->set_active (visible);
568                                 
569                                 /* one channel only */
570                                 break;
571                         }
572                 }
573         }
574 }
575
576 void
577 MidiTimeAxisView::build_controller_menu ()
578 {
579         using namespace Menu_Helpers;
580
581         if (controller_menu) {
582                 /* it exists and has not been invalidated by a channel mode change, so just return it */
583                 return;
584         }
585
586         controller_menu = new Menu; // explicitly managed by us
587         MenuList& items (controller_menu->items());
588
589         /* create several "top level" menu items for sets of controllers (16 at a time), and populate each one with a submenu 
590            for each controller+channel combination covering the currently selected channels for this track
591         */
592
593         uint16_t selected_channels = _channel_selector.get_selected_channels();
594
595         /* count the number of selected channels because we will build a different menu structure if there is more than 1 selected.
596          */
597
598         int chn_cnt = 0;
599         
600         for (uint8_t chn = 0; chn < 16; chn++) {
601                 if (selected_channels & (0x0001 << chn)) {
602                         if (++chn_cnt > 1) {
603                                 break;
604                         }
605                 }
606         }
607         
608         /* loop over all 127 MIDI controllers, in groups of 16 */
609
610         for (int i = 0; i < 127; i += 16) {
611
612                 Menu* ctl_menu = manage (new Menu);
613                 MenuList& ctl_items (ctl_menu->items());
614
615
616                 /* for each controller, consider whether to create a submenu or a single item */
617
618                 for (int ctl = i; ctl < i+16; ++ctl) {
619
620                         if (chn_cnt > 1) {
621
622                                 /* multiple channels - create a submenu, with 1 item per channel */
623
624                                 Menu* chn_menu = manage (new Menu);
625                                 MenuList& chn_items (chn_menu->items());
626
627                                 /* add a couple of items to hide/show this controller on all channels */
628                                 
629                                 Evoral::Parameter param_without_channel (MidiCCAutomation, 0, ctl);
630                                 chn_items.push_back (MenuElem (_("Hide all channels"),
631                                                                     sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility), 
632                                                                                 false, param_without_channel)));
633                                 chn_items.push_back (MenuElem (_("Show all channels"),
634                                                                     sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility), 
635                                                                                 true, param_without_channel)));
636                 
637                                 for (uint8_t chn = 0; chn < 16; chn++) {
638                                         if (selected_channels & (0x0001 << chn)) {
639                                                 
640                                                 /* for each selected channel, add a menu item for this controller */
641                                                 
642                                                 Evoral::Parameter fully_qualified_param (MidiCCAutomation, chn, ctl);
643                                                 chn_items.push_back (CheckMenuElem (string_compose (_("Channel %1"), chn+1),
644                                                                                     sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
645                                                                                                 fully_qualified_param)));
646
647                                                 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
648                                                 bool visible = false;
649                                                 
650                                                 if (track) {
651                                                         if (track->marked_for_display()) {
652                                                                 visible = true;
653                                                         }
654                                                 }
655                                                 
656                                                 CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&chn_items.back());
657                                                 _controller_menu_map[fully_qualified_param] = cmi;
658                                                 cmi->set_active (visible);
659                                         }
660                                 }
661                                 
662                                 /* add the per-channel menu to the list of controllers, with the name of the controller */
663                                 ctl_items.push_back (MenuElem (string_compose ("<b>%1</b>: %2", ctl, midi_name (ctl)), *chn_menu));
664                                 dynamic_cast<Label*> (ctl_items.back().get_child())->set_use_markup (true);
665                                       
666                         } else {
667
668                                 /* just one channel - create a single menu item for this ctl+channel combination*/
669
670                                 for (uint8_t chn = 0; chn < 16; chn++) {
671                                         if (selected_channels & (0x0001 << chn)) {
672                                                 
673                                                 Evoral::Parameter fully_qualified_param (MidiCCAutomation, chn, ctl);
674                                                 ctl_items.push_back (CheckMenuElem (_route->describe_parameter (fully_qualified_param),
675                                                                                     sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
676                                                                                                 fully_qualified_param)));
677                                                 
678                                                 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
679                                                 bool visible = false;
680                                                 
681                                                 if (track) {
682                                                         if (track->marked_for_display()) {
683                                                                 visible = true;
684                                                         }
685                                                 }
686                                                 
687                                                 CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&ctl_items.back());
688                                                 _controller_menu_map[fully_qualified_param] = cmi;
689                                                 cmi->set_active (visible);
690                                                 
691                                                 /* one channel only */
692                                                 break;
693                                         }
694                                 }
695                         }
696                 }
697                         
698                 /* add the menu for this block of controllers to the overall controller menu */
699
700                 items.push_back (MenuElem (string_compose (_("Controllers %1-%2"), i, i+15), *ctl_menu));
701         }
702 }
703
704 Gtk::Menu*
705 MidiTimeAxisView::build_note_mode_menu()
706 {
707         using namespace Menu_Helpers;
708
709         Menu* mode_menu = manage (new Menu);
710         MenuList& items = mode_menu->items();
711         mode_menu->set_name ("ArdourContextMenu");
712
713         RadioMenuItem::Group mode_group;
714         items.push_back (RadioMenuElem (mode_group, _("Sustained"),
715                                 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_mode), Sustained)));
716         _note_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
717         _note_mode_item->set_active(_note_mode == Sustained);
718
719         items.push_back (RadioMenuElem (mode_group, _("Percussive"),
720                                 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_mode), Percussive)));
721         _percussion_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
722         _percussion_mode_item->set_active(_note_mode == Percussive);
723
724         return mode_menu;
725 }
726
727 Gtk::Menu*
728 MidiTimeAxisView::build_color_mode_menu()
729 {
730         using namespace Menu_Helpers;
731
732         Menu* mode_menu = manage (new Menu);
733         MenuList& items = mode_menu->items();
734         mode_menu->set_name ("ArdourContextMenu");
735
736         RadioMenuItem::Group mode_group;
737         items.push_back (RadioMenuElem (mode_group, _("Meter Colors"),
738                                 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode), MeterColors)));
739         _meter_color_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
740         _meter_color_mode_item->set_active(_color_mode == MeterColors);
741
742         items.push_back (RadioMenuElem (mode_group, _("Channel Colors"),
743                                 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode), ChannelColors)));
744         _channel_color_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
745         _channel_color_mode_item->set_active(_color_mode == ChannelColors);
746
747         items.push_back (RadioMenuElem (mode_group, _("Track Color"),
748                                 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode), TrackColor)));
749         _channel_color_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
750         _channel_color_mode_item->set_active(_color_mode == TrackColor);
751
752         return mode_menu;
753 }
754
755 void
756 MidiTimeAxisView::set_note_mode(NoteMode mode)
757 {
758         if (_note_mode != mode || midi_track()->note_mode() != mode) {
759                 _note_mode = mode;
760                 midi_track()->set_note_mode(mode);
761                 xml_node->add_property ("note-mode", enum_2_string(_note_mode));
762                 _view->redisplay_track();
763         }
764 }
765
766 void
767 MidiTimeAxisView::set_color_mode(ColorMode mode)
768 {
769         if (_color_mode != mode) {
770                 if (mode == ChannelColors) {
771                         _channel_selector.set_channel_colors(CanvasNoteEvent::midi_channel_colors);
772                 } else {
773                         _channel_selector.set_default_channel_color();
774                 }
775
776                 _color_mode = mode;
777                 xml_node->add_property ("color-mode", enum_2_string(_color_mode));
778                 _view->redisplay_track();
779         }
780 }
781
782 void
783 MidiTimeAxisView::set_note_range(MidiStreamView::VisibleNoteRange range)
784 {
785         if (!_ignore_signals)
786                 midi_view()->set_note_range(range);
787 }
788
789
790 void
791 MidiTimeAxisView::update_range()
792 {
793         MidiGhostRegion* mgr;
794
795         for(list<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
796                 if ((mgr = dynamic_cast<MidiGhostRegion*>(*i)) != 0) {
797                         mgr->update_range();
798                 }
799         }
800 }
801
802 void
803 MidiTimeAxisView::show_all_automation ()
804 {
805         if (midi_track()) {
806                 const set<Evoral::Parameter> params = midi_track()->midi_playlist()->contained_automation();
807
808                 for (set<Evoral::Parameter>::const_iterator i = params.begin(); i != params.end(); ++i) {
809                         create_automation_child(*i, true);
810                 }
811         }
812
813         RouteTimeAxisView::show_all_automation ();
814 }
815
816 void
817 MidiTimeAxisView::show_existing_automation ()
818 {
819         if (midi_track()) {
820                 const set<Evoral::Parameter> params = midi_track()->midi_playlist()->contained_automation();
821
822                 for (set<Evoral::Parameter>::const_iterator i = params.begin(); i != params.end(); ++i) {
823                         create_automation_child(*i, true);
824                 }
825         }
826
827         RouteTimeAxisView::show_existing_automation ();
828 }
829
830 /** Create an automation track for the given parameter (pitch bend, channel pressure).
831  */
832 void
833 MidiTimeAxisView::create_automation_child (const Evoral::Parameter& param, bool show)
834 {
835         /* These controllers are region "automation", so we do not create
836          * an AutomationList/Line for the track */
837
838         if (param.type() == NullAutomation) {
839                 cerr << "WARNING: Attempt to create NullAutomation child, ignoring" << endl;
840                 return;
841         }
842
843         AutomationTracks::iterator existing = _automation_tracks.find (param);
844         if (existing != _automation_tracks.end()) {
845                 return;
846         }
847
848         boost::shared_ptr<AutomationControl> c = _route->get_control (param);
849
850         assert(c);
851
852         boost::shared_ptr<AutomationTimeAxisView> track(new AutomationTimeAxisView (_session,
853                         _route, boost::shared_ptr<ARDOUR::Automatable>(), c,
854                         _editor,
855                         *this,
856                         true,
857                         parent_canvas,
858                         _route->describe_parameter(param)));
859
860         add_automation_child (param, track, show);
861 }
862
863
864 void
865 MidiTimeAxisView::route_active_changed ()
866 {
867         RouteUI::route_active_changed ();
868
869         if (is_track()) {
870                 if (_route->active()) {
871                         controls_ebox.set_name ("MidiTrackControlsBaseUnselected");
872                         controls_base_selected_name = "MidiTrackControlsBaseSelected";
873                         controls_base_unselected_name = "MidiTrackControlsBaseUnselected";
874                 } else {
875                         controls_ebox.set_name ("MidiTrackControlsBaseInactiveUnselected");
876                         controls_base_selected_name = "MidiTrackControlsBaseInactiveSelected";
877                         controls_base_unselected_name = "MidiTrackControlsBaseInactiveUnselected";
878                 }
879         } else {
880
881                 throw; // wha?
882
883                 if (_route->active()) {
884                         controls_ebox.set_name ("BusControlsBaseUnselected");
885                         controls_base_selected_name = "BusControlsBaseSelected";
886                         controls_base_unselected_name = "BusControlsBaseUnselected";
887                 } else {
888                         controls_ebox.set_name ("BusControlsBaseInactiveUnselected");
889                         controls_base_selected_name = "BusControlsBaseInactiveSelected";
890                         controls_base_unselected_name = "BusControlsBaseInactiveUnselected";
891                 }
892         }
893 }
894
895 void
896 MidiTimeAxisView::start_step_editing ()
897 {
898         _step_edit_triplet_countdown = 0;
899         _step_edit_within_chord = 0;
900         _step_edit_chord_duration = 0.0;
901         step_edit_region.reset ();
902         step_edit_region_view = 0;
903
904         resync_step_edit_position ();
905         prepare_step_edit_region ();
906         reset_step_edit_beat_pos ();
907
908         assert (step_edit_region);
909         assert (step_edit_region_view);
910
911         if (step_editor == 0) {
912                 step_editor = new StepEntry (*this);
913                 step_editor->signal_delete_event().connect (sigc::mem_fun (*this, &MidiTimeAxisView::step_editor_hidden));
914                 step_editor->signal_hide().connect (sigc::mem_fun (*this, &MidiTimeAxisView::step_editor_hide));
915         }
916
917         step_edit_region_view->show_step_edit_cursor (step_edit_beat_pos);
918         step_edit_region_view->set_step_edit_cursor_width (step_editor->note_length());
919
920         step_editor->set_position (WIN_POS_MOUSE);
921         step_editor->present ();
922 }
923
924 void
925 MidiTimeAxisView::resync_step_edit_position ()
926 {
927         step_edit_insert_position = _editor.get_preferred_edit_position ();
928 }
929
930 void
931 MidiTimeAxisView::resync_step_edit_to_edit_point ()
932 {
933         resync_step_edit_position ();
934         if (step_edit_region) {
935                 reset_step_edit_beat_pos ();
936         }
937 }
938
939 void
940 MidiTimeAxisView::prepare_step_edit_region ()
941 {
942         boost::shared_ptr<Region> r = playlist()->top_region_at (step_edit_insert_position);
943
944         if (r) {
945                 step_edit_region = boost::dynamic_pointer_cast<MidiRegion>(r);
946         }
947
948         if (step_edit_region) {
949                 RegionView* rv = view()->find_view (step_edit_region);
950                 step_edit_region_view = dynamic_cast<MidiRegionView*> (rv);
951
952         } else {
953                 step_edit_region = add_region (step_edit_insert_position);
954                 RegionView* rv = view()->find_view (step_edit_region);
955                 step_edit_region_view = dynamic_cast<MidiRegionView*>(rv);
956         }
957 }
958
959
960 void
961 MidiTimeAxisView::reset_step_edit_beat_pos ()
962 {
963         assert (step_edit_region);
964         assert (step_edit_region_view);
965
966         framecnt_t frames_from_start = _editor.get_preferred_edit_position() - step_edit_region->position();
967         
968         if (frames_from_start < 0) {
969                 /* this can happen with snap enabled, and the edit point == Playhead. we snap the
970                    position of the new region, and it can end up after the edit point.
971                 */
972                 frames_from_start = 0;
973         }
974         
975         step_edit_beat_pos = step_edit_region_view->frames_to_beats (frames_from_start);
976         step_edit_region_view->move_step_edit_cursor (step_edit_beat_pos);
977 }
978
979 bool
980 MidiTimeAxisView::step_editor_hidden (GdkEventAny*)
981 {
982         step_editor_hide ();
983         return true;
984 }
985
986 void
987 MidiTimeAxisView::step_editor_hide ()
988 {
989         /* everything else will follow the change in the model */
990         midi_track()->set_step_editing (false);
991 }
992
993 void
994 MidiTimeAxisView::stop_step_editing ()
995 {
996         if (step_editor) {
997                 step_editor->hide ();
998         }
999
1000         if (step_edit_region_view) {
1001                 step_edit_region_view->hide_step_edit_cursor();
1002         }
1003
1004         step_edit_region.reset ();
1005 }
1006
1007 void
1008 MidiTimeAxisView::check_step_edit ()
1009 {
1010         MidiRingBuffer<nframes_t>& incoming (midi_track()->step_edit_ring_buffer());
1011         uint8_t* buf;
1012         uint32_t bufsize = 32;
1013
1014         buf = new uint8_t[bufsize];
1015
1016         while (incoming.read_space()) {
1017                 nframes_t time;
1018                 Evoral::EventType type;
1019                 uint32_t size;
1020
1021                 incoming.read_prefix (&time, &type, &size);
1022
1023                 if (size > bufsize) {
1024                         delete [] buf;
1025                         bufsize = size;
1026                         buf = new uint8_t[bufsize];
1027                 }
1028
1029                 incoming.read_contents (size, buf);
1030
1031                 if ((buf[0] & 0xf0) == MIDI_CMD_NOTE_ON) {
1032                         step_add_note (buf[0] & 0xf, buf[1], buf[2], 0.0);
1033                 }
1034         }
1035 }
1036
1037 int
1038 MidiTimeAxisView::step_add_bank_change (uint8_t channel, uint8_t bank)
1039 {
1040         return 0;
1041 }
1042
1043 int
1044 MidiTimeAxisView::step_add_program_change (uint8_t channel, uint8_t program)
1045 {
1046         return 0;
1047 }
1048
1049 void
1050 MidiTimeAxisView::step_edit_sustain (Evoral::MusicalTime beats)
1051 {
1052         if (step_edit_region_view) {
1053                 step_edit_region_view->step_sustain (beats);
1054         }
1055 }
1056
1057 void
1058 MidiTimeAxisView::move_step_edit_beat_pos (Evoral::MusicalTime beats)
1059 {
1060         if (beats > 0.0) {
1061                 step_edit_beat_pos = min (step_edit_beat_pos + beats, 
1062                                           step_edit_region_view->frames_to_beats (step_edit_region->length()));
1063         } else if (beats < 0.0) {
1064                 if (beats < step_edit_beat_pos) {
1065                         step_edit_beat_pos += beats; // its negative, remember
1066                 } else {
1067                         step_edit_beat_pos = 0;
1068                 }
1069         }
1070         step_edit_region_view->move_step_edit_cursor (step_edit_beat_pos);
1071 }
1072
1073 int
1074 MidiTimeAxisView::step_add_note (uint8_t channel, uint8_t pitch, uint8_t velocity, Evoral::MusicalTime beat_duration)
1075 {
1076         /* do these things in case undo removed the step edit region
1077         */
1078         if (!step_edit_region) {
1079                 resync_step_edit_position ();
1080                 prepare_step_edit_region ();
1081                 reset_step_edit_beat_pos ();
1082                 step_edit_region_view->show_step_edit_cursor (step_edit_beat_pos);
1083                 step_edit_region_view->set_step_edit_cursor_width (step_editor->note_length());
1084         }
1085
1086         assert (step_edit_region);
1087         assert (step_edit_region_view);
1088                 
1089         if (beat_duration == 0.0) {
1090                 bool success;
1091                 beat_duration = _editor.get_grid_type_as_beats (success, step_edit_insert_position);
1092                 
1093                 if (!success) {
1094                         return -1;
1095                 }
1096         }
1097         
1098         MidiStreamView* msv = midi_view();
1099         
1100         /* make sure its visible on the vertical axis */
1101         
1102         if (pitch < msv->lowest_note() || pitch > msv->highest_note()) {
1103                 msv->update_note_range (pitch);
1104                 msv->set_note_range (MidiStreamView::ContentsRange);
1105         }
1106         
1107         /* make sure its visible on the horizontal axis */
1108         
1109         nframes64_t fpos = step_edit_region->position() + 
1110                 step_edit_region_view->beats_to_frames (step_edit_beat_pos + beat_duration);
1111         
1112         if (fpos >= (_editor.leftmost_position() + _editor.current_page_frames())) {
1113                 _editor.reset_x_origin (fpos - (_editor.current_page_frames()/4));
1114         }
1115         
1116         step_edit_region_view->step_add_note (channel, pitch, velocity, step_edit_beat_pos, beat_duration);
1117         
1118         if (_step_edit_triplet_countdown > 0) {
1119                 _step_edit_triplet_countdown--;
1120                 
1121                 if (_step_edit_triplet_countdown == 0) {
1122                         _step_edit_triplet_countdown = 3;
1123                 }
1124         }
1125         
1126         if (!_step_edit_within_chord) {
1127                 step_edit_beat_pos += beat_duration;
1128                 step_edit_region_view->move_step_edit_cursor (step_edit_beat_pos);
1129         } else {
1130                 step_edit_beat_pos += 1.0/Meter::ticks_per_beat; // tiny, but no longer overlapping
1131                 _step_edit_chord_duration = max (_step_edit_chord_duration, beat_duration);
1132         }
1133
1134         return 0;
1135 }
1136
1137 void
1138 MidiTimeAxisView::set_step_edit_cursor_width (Evoral::MusicalTime beats)
1139 {
1140         if (step_edit_region_view) {
1141                 step_edit_region_view->set_step_edit_cursor_width (beats);
1142         }
1143 }
1144
1145 bool
1146 MidiTimeAxisView::step_edit_within_triplet() const
1147 {
1148         return _step_edit_triplet_countdown > 0;
1149 }
1150
1151 bool
1152 MidiTimeAxisView::step_edit_within_chord() const
1153 {
1154         return _step_edit_within_chord;
1155 }
1156
1157 void
1158 MidiTimeAxisView::step_edit_toggle_triplet ()
1159 {
1160         if (_step_edit_triplet_countdown == 0) {
1161                 _step_edit_within_chord = false;
1162                 _step_edit_triplet_countdown = 3;
1163         } else {
1164                 _step_edit_triplet_countdown = 0;
1165         }
1166 }
1167
1168 void
1169 MidiTimeAxisView::step_edit_toggle_chord ()
1170 {
1171         if (_step_edit_within_chord) {
1172                 _step_edit_within_chord = false;
1173                 step_edit_beat_pos += _step_edit_chord_duration;
1174                 step_edit_region_view->move_step_edit_cursor (step_edit_beat_pos);
1175         } else {
1176                 _step_edit_triplet_countdown = 0;
1177                 _step_edit_within_chord = true;
1178         }
1179 }
1180
1181 void
1182 MidiTimeAxisView::step_edit_rest (Evoral::MusicalTime beats)
1183 {
1184         bool success;
1185
1186         if (beats == 0.0) {
1187                 beats = _editor.get_grid_type_as_beats (success, step_edit_insert_position);
1188         } else {
1189                 success = true;
1190         }
1191
1192         if (success) {
1193                 step_edit_beat_pos += beats;
1194                 step_edit_region_view->move_step_edit_cursor (step_edit_beat_pos);
1195         }
1196 }
1197
1198 void 
1199 MidiTimeAxisView::step_edit_beat_sync ()
1200 {
1201         step_edit_beat_pos = ceil (step_edit_beat_pos);
1202         step_edit_region_view->move_step_edit_cursor (step_edit_beat_pos);
1203 }
1204
1205 void 
1206 MidiTimeAxisView::step_edit_bar_sync ()
1207 {
1208         if (!_session || !step_edit_region_view || !step_edit_region) {
1209                 return;
1210         }
1211
1212         framepos_t fpos = step_edit_region->position() + 
1213                 step_edit_region_view->beats_to_frames (step_edit_beat_pos);
1214         fpos = _session->tempo_map().round_to_bar (fpos, 1);
1215         step_edit_beat_pos = ceil (step_edit_region_view->frames_to_beats (fpos - step_edit_region->position()));
1216         step_edit_region_view->move_step_edit_cursor (step_edit_beat_pos);
1217 }
1218
1219 boost::shared_ptr<MidiRegion>
1220 MidiTimeAxisView::add_region (framepos_t pos)
1221 {
1222         Editor* real_editor = dynamic_cast<Editor*> (&_editor);
1223
1224         real_editor->begin_reversible_command (_("create region"));
1225         playlist()->clear_history ();
1226
1227         real_editor->snap_to (pos, 0);
1228         const Meter& m = _session->tempo_map().meter_at(pos);
1229         const Tempo& t = _session->tempo_map().tempo_at(pos);
1230         double length = floor (m.frames_per_bar(t, _session->frame_rate()));
1231
1232         boost::shared_ptr<Source> src = _session->create_midi_source_for_session (view()->trackview().track().get(),
1233                                                                                   view()->trackview().track()->name());
1234         PropertyList plist; 
1235         
1236         plist.add (ARDOUR::Properties::start, 0);
1237         plist.add (ARDOUR::Properties::length, length);
1238         plist.add (ARDOUR::Properties::name, PBD::basename_nosuffix(src->name()));
1239         
1240         boost::shared_ptr<Region> region = (RegionFactory::create (src, plist));
1241         
1242         playlist()->add_region (region, pos);
1243         _session->add_command (new StatefulDiffCommand (playlist()));
1244
1245         real_editor->commit_reversible_command();
1246
1247         return boost::dynamic_pointer_cast<MidiRegion>(region);
1248 }
1249
1250 void
1251 MidiTimeAxisView::add_note_selection (uint8_t note)
1252 {
1253         if (!_editor.internal_editing()) {
1254                 return;
1255         }
1256
1257         uint16_t chn_mask = _channel_selector.get_selected_channels();
1258
1259         if (_view->num_selected_regionviews() == 0) {
1260                 _view->foreach_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection_region_view), note, chn_mask));
1261         } else {
1262                 _view->foreach_selected_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection_region_view), note, chn_mask));
1263         }
1264 }
1265
1266 void
1267 MidiTimeAxisView::extend_note_selection (uint8_t note)
1268 {
1269         if (!_editor.internal_editing()) {
1270                 return;
1271         }
1272
1273         uint16_t chn_mask = _channel_selector.get_selected_channels();
1274
1275         if (_view->num_selected_regionviews() == 0) {
1276                 _view->foreach_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::extend_note_selection_region_view), note, chn_mask));
1277         } else {
1278                 _view->foreach_selected_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::extend_note_selection_region_view), note, chn_mask));
1279         }
1280 }
1281
1282 void
1283 MidiTimeAxisView::toggle_note_selection (uint8_t note)
1284 {
1285         if (!_editor.internal_editing()) {
1286                 return;
1287         }
1288
1289         uint16_t chn_mask = _channel_selector.get_selected_channels();
1290
1291         if (_view->num_selected_regionviews() == 0) {
1292                 _view->foreach_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection_region_view), note, chn_mask));
1293         } else {
1294                 _view->foreach_selected_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection_region_view), note, chn_mask));
1295         }
1296 }
1297
1298 void
1299 MidiTimeAxisView::add_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1300 {
1301         dynamic_cast<MidiRegionView*>(rv)->select_matching_notes (note, chn_mask, false, false);
1302 }
1303
1304 void
1305 MidiTimeAxisView::extend_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1306 {
1307         dynamic_cast<MidiRegionView*>(rv)->select_matching_notes (note, chn_mask, true, true);
1308 }
1309
1310 void
1311 MidiTimeAxisView::toggle_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1312 {
1313         dynamic_cast<MidiRegionView*>(rv)->toggle_matching_notes (note, chn_mask);
1314 }
1315
1316 void
1317 MidiTimeAxisView::set_channel_mode (ChannelMode, uint16_t)
1318 {
1319         /* hide all automation tracks that use the wrong channel(s) and show all those that use
1320            the right ones.
1321         */
1322
1323         uint16_t selected_channels = _channel_selector.get_selected_channels();
1324         bool changed = false;
1325
1326         no_redraw = true;
1327
1328         for (uint32_t ctl = 0; ctl < 127; ++ctl) {
1329
1330                 for (uint32_t chn = 0; chn < 16; ++chn) {
1331                         Evoral::Parameter fully_qualified_param (MidiCCAutomation, chn, ctl);
1332                         boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
1333
1334                         if (!track) {
1335                                 continue;
1336                         }
1337                         
1338                         if ((selected_channels & (0x0001 << chn)) == 0) {
1339                                 /* channel not in use. hiding it will trigger RouteTimeAxisView::automation_track_hidden() 
1340                                    which will cause a redraw. We don't want one per channel, so block that with no_redraw.
1341                                  */
1342                                 changed = track->set_visibility (false) || changed;
1343                         } else {
1344                                 changed = track->set_visibility (true) || changed;
1345                         }
1346                 }
1347         }
1348
1349         no_redraw = false;
1350
1351         /* TODO: Bender, PgmChange, Pressure */
1352
1353         /* invalidate the controller menu, so that we rebuilt it next time */
1354         _controller_menu_map.clear ();
1355         delete controller_menu;
1356         controller_menu = 0;
1357
1358         if (changed) {
1359                 _route->gui_changed ("track_height", this);
1360         }
1361 }
1362
1363 Gtk::CheckMenuItem*
1364 MidiTimeAxisView::automation_child_menu_item (Evoral::Parameter param)
1365 {
1366         Gtk::CheckMenuItem* m = RouteTimeAxisView::automation_child_menu_item (param);
1367         if (m) {
1368                 return m;
1369         }
1370
1371         ParameterMenuMap::iterator i = _controller_menu_map.find (param);
1372         if (i != _controller_menu_map.end()) {
1373                 return i->second;
1374         }
1375
1376         i = _channel_command_menu_map.find (param);
1377         if (i != _channel_command_menu_map.end()) {
1378                 return i->second;
1379         }
1380
1381         return 0;
1382 }