a33b50ef64b3a4ec9864dcaa8726aea059e6393e
[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/stop_signal.h>
39 #include <gtkmm2ext/bindable_button.h>
40 #include <gtkmm2ext/utils.h>
41
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 "utils.h"
85
86 #include "ardour/midi_track.h"
87
88 #include "i18n.h"
89
90 using namespace ARDOUR;
91 using namespace PBD;
92 using namespace Gtk;
93 using namespace Gtkmm2ext;
94 using namespace Editing;
95
96 // Minimum height at which a control is displayed
97 static const uint32_t MIDI_CONTROLS_BOX_MIN_HEIGHT = 162;
98 static const uint32_t KEYBOARD_MIN_HEIGHT = 140;
99
100 MidiTimeAxisView::MidiTimeAxisView (PublicEditor& ed, Session* sess,
101                 boost::shared_ptr<Route> rt, Canvas& canvas)
102         : AxisView(sess) // virtually inherited
103         , RouteTimeAxisView(ed, sess, rt, canvas)
104         , _ignore_signals(false)
105         , _range_scroomer(0)
106         , _piano_roll_header(0)
107         , _note_mode(Sustained)
108         , _note_mode_item(0)
109         , _percussion_mode_item(0)
110         , _color_mode(MeterColors)
111         , _meter_color_mode_item(0)
112         , _channel_color_mode_item(0)
113         , _track_color_mode_item(0)
114         , _step_edit_item (0)
115         , _midi_thru_item (0)
116         , default_channel_menu (0)
117         , controller_menu (0)
118 {
119         subplugin_menu.set_name ("ArdourContextMenu");
120
121         _view = new MidiStreamView (*this);
122
123         ignore_toggle = false;
124         
125         mute_button->set_active (false);
126         solo_button->set_active (false);
127
128         step_edit_insert_position = 0;
129
130         if (is_midi_track()) {
131                 controls_ebox.set_name ("MidiTimeAxisViewControlsBaseUnselected");
132                 _note_mode = midi_track()->note_mode();
133         } else { // MIDI bus (which doesn't exist yet..)
134                 controls_ebox.set_name ("MidiBusControlsBaseUnselected");
135         }
136
137         /* map current state of the route */
138
139         processors_changed (RouteProcessorChange ());
140
141         ensure_xml_node ();
142
143         set_state (*xml_node, Stateful::loading_state_version);
144
145         _route->processors_changed.connect (*this, invalidator (*this), ui_bind (&MidiTimeAxisView::processors_changed, this, _1), gui_context());
146
147         if (is_track()) {
148                 _piano_roll_header = new PianoRollHeader(*midi_view());
149
150                 _piano_roll_header->AddNoteSelection.connect (sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection));
151                 _piano_roll_header->ExtendNoteSelection.connect (sigc::mem_fun (*this, &MidiTimeAxisView::extend_note_selection));
152                 _piano_roll_header->ToggleNoteSelection.connect (sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection));
153
154                 _range_scroomer = new MidiScroomer(midi_view()->note_range_adjustment);
155
156                 controls_hbox.pack_start(*_range_scroomer);
157                 controls_hbox.pack_start(*_piano_roll_header);
158
159                 controls_ebox.set_name ("MidiTrackControlsBaseUnselected");
160                 controls_base_selected_name = "MidiTrackControlsBaseSelected";
161                 controls_base_unselected_name = "MidiTrackControlsBaseUnselected";
162
163                 midi_view()->NoteRangeChanged.connect (sigc::mem_fun(*this, &MidiTimeAxisView::update_range));
164
165                 /* ask for notifications of any new RegionViews */
166                 _view->RegionViewAdded.connect (sigc::mem_fun(*this, &MidiTimeAxisView::region_view_added));
167                 _view->attach ();
168         }
169
170         HBox* midi_controls_hbox = manage(new HBox());
171
172         MIDI::Name::MidiPatchManager& patch_manager = MIDI::Name::MidiPatchManager::instance();
173
174         MIDI::Name::MasterDeviceNames::Models::const_iterator m = patch_manager.all_models().begin();
175         for (; m != patch_manager.all_models().end(); ++m) {
176                 _model_selector.append_text(m->c_str());
177         }
178
179         _model_selector.signal_changed().connect(sigc::mem_fun(*this, &MidiTimeAxisView::model_changed));
180
181         _custom_device_mode_selector.signal_changed().connect(
182                         sigc::mem_fun(*this, &MidiTimeAxisView::custom_device_mode_changed));
183
184         // TODO: persist the choice
185         // this initializes the comboboxes and sends out the signal
186         _model_selector.set_active(0);
187
188         midi_controls_hbox->pack_start(_channel_selector, true, false);
189         if (!patch_manager.all_models().empty()) {
190                 _midi_controls_box.pack_start(_model_selector, true, false);
191                 _midi_controls_box.pack_start(_custom_device_mode_selector, true, false);
192         }
193
194         _midi_controls_box.pack_start(*midi_controls_hbox, true, true);
195
196         controls_vbox.pack_start(_midi_controls_box, false, false);
197
198         // restore channel selector settings
199         _channel_selector.set_channel_mode(midi_track()->get_channel_mode(), midi_track()->get_channel_mask());
200         _channel_selector.mode_changed.connect(
201                 sigc::mem_fun(*midi_track(), &MidiTrack::set_channel_mode));
202         _channel_selector.mode_changed.connect(
203                 sigc::mem_fun(*this, &MidiTimeAxisView::set_channel_mode));
204
205         XMLProperty *prop;
206         if ((prop = xml_node->property ("color-mode")) != 0) {
207                 _color_mode = ColorMode (string_2_enum(prop->value(), _color_mode));
208                 if (_color_mode == ChannelColors) {
209                         _channel_selector.set_channel_colors(CanvasNoteEvent::midi_channel_colors);
210                 }
211         }
212
213         if ((prop = xml_node->property ("note-mode")) != 0) {
214                 _note_mode = NoteMode (string_2_enum(prop->value(), _note_mode));
215                 if (_percussion_mode_item) {
216                         _percussion_mode_item->set_active (_note_mode == Percussive);
217                 }
218         }
219 }
220
221 MidiTimeAxisView::~MidiTimeAxisView ()
222 {
223         delete _piano_roll_header;
224         _piano_roll_header = 0;
225
226         delete _range_scroomer;
227         _range_scroomer = 0;
228
229         delete controller_menu;
230 }
231
232 void MidiTimeAxisView::model_changed()
233 {
234         std::list<std::string> device_modes = MIDI::Name::MidiPatchManager::instance()
235                 .custom_device_mode_names_by_model(_model_selector.get_active_text());
236
237         _custom_device_mode_selector.clear_items();
238
239         for (std::list<std::string>::const_iterator i = device_modes.begin();
240                         i != device_modes.end(); ++i) {
241                 cerr << "found custom device mode " << *i << " thread_id: " << pthread_self() << endl;
242                 _custom_device_mode_selector.append_text(*i);
243         }
244
245         _custom_device_mode_selector.set_active(0);
246 }
247
248 void MidiTimeAxisView::custom_device_mode_changed()
249 {
250         _midi_patch_settings_changed.emit(_model_selector.get_active_text(),
251                         _custom_device_mode_selector.get_active_text());
252 }
253
254 MidiStreamView*
255 MidiTimeAxisView::midi_view()
256 {
257         return dynamic_cast<MidiStreamView*>(_view);
258 }
259
260 guint32
261 MidiTimeAxisView::show_at (double y, int& nth, Gtk::VBox *parent)
262 {
263         ensure_xml_node ();
264         xml_node->add_property ("shown-editor", "yes");
265
266         guint32 ret = TimeAxisView::show_at (y, nth, parent);
267         return ret;
268 }
269
270 void
271 MidiTimeAxisView::hide ()
272 {
273         ensure_xml_node ();
274         xml_node->add_property ("shown-editor", "no");
275
276         TimeAxisView::hide ();
277 }
278
279 void
280 MidiTimeAxisView::set_height (uint32_t h)
281 {
282         RouteTimeAxisView::set_height (h);
283
284         if (height >= MIDI_CONTROLS_BOX_MIN_HEIGHT) {
285                 _midi_controls_box.show();
286         } else {
287                 _midi_controls_box.hide();
288         }
289
290         if (height >= KEYBOARD_MIN_HEIGHT) {
291                 if (is_track() && _range_scroomer)
292                         _range_scroomer->show();
293                 if (is_track() && _piano_roll_header)
294                         _piano_roll_header->show();
295         } else {
296                 if (is_track() && _range_scroomer)
297                         _range_scroomer->hide();
298                 if (is_track() && _piano_roll_header)
299                         _piano_roll_header->hide();
300         }
301 }
302
303 void
304 MidiTimeAxisView::append_extra_display_menu_items ()
305 {
306         using namespace Menu_Helpers;
307
308         MenuList& items = display_menu->items();
309
310         // Note range
311         Menu *range_menu = manage(new Menu);
312         MenuList& range_items = range_menu->items();
313         range_menu->set_name ("ArdourContextMenu");
314
315         range_items.push_back (MenuElem (_("Show Full Range"), sigc::bind (
316                         sigc::mem_fun(*this, &MidiTimeAxisView::set_note_range),
317                         MidiStreamView::FullRange)));
318
319         range_items.push_back (MenuElem (_("Fit Contents"), sigc::bind (
320                         sigc::mem_fun(*this, &MidiTimeAxisView::set_note_range),
321                         MidiStreamView::ContentsRange)));
322
323         items.push_back (MenuElem (_("Note range"), *range_menu));
324         items.push_back (MenuElem (_("Note mode"), *build_note_mode_menu()));
325         items.push_back (MenuElem (_("Default Channel"), *build_def_channel_menu()));
326
327         items.push_back (CheckMenuElem (_("MIDI Thru"), sigc::mem_fun(*this, &MidiTimeAxisView::toggle_midi_thru)));
328         _midi_thru_item = dynamic_cast<CheckMenuItem*>(&items.back());
329 }
330
331 Gtk::Menu*
332 MidiTimeAxisView::build_def_channel_menu ()
333 {
334         using namespace Menu_Helpers;
335
336         default_channel_menu = manage (new Menu ());
337
338         uint8_t defchn = midi_track()->default_channel();
339         MenuList& def_channel_items = default_channel_menu->items();
340         RadioMenuItem* item;
341         RadioMenuItem::Group dc_group;
342
343         for (int i = 0; i < 16; ++i) {
344                 char buf[4];
345                 snprintf (buf, sizeof (buf), "%d", i+1);
346
347                 def_channel_items.push_back (RadioMenuElem (dc_group, buf,
348                                                             sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_default_channel), i)));
349                 item = dynamic_cast<RadioMenuItem*>(&def_channel_items.back());
350                 item->set_active ((i == defchn));
351         }
352
353         return default_channel_menu;
354 }
355
356 void
357 MidiTimeAxisView::set_default_channel (int chn)
358 {
359         midi_track()->set_default_channel (chn);
360 }
361
362 void
363 MidiTimeAxisView::toggle_midi_thru ()
364 {
365         if (!_midi_thru_item) {
366                 return;
367         }
368
369         bool view_yn = _midi_thru_item->get_active();
370         if (view_yn != midi_track()->midi_thru()) {
371                 midi_track()->set_midi_thru (view_yn);
372         }
373 }
374
375 void
376 MidiTimeAxisView::build_automation_action_menu ()
377 {
378         using namespace Menu_Helpers;
379
380         /* If we have a controller menu, we need to detach it before
381            RouteTimeAxis::build_automation_action_menu destroys the
382            menu it is attached to.  Otherwise GTK destroys
383            controller_menu's gobj, meaning that it can't be reattached
384            below.  See bug #3134.
385         */
386            
387         if (controller_menu) {
388                 detach_menu (*controller_menu);
389         }
390
391         RouteTimeAxisView::build_automation_action_menu ();
392
393         MenuList& automation_items = automation_action_menu->items();
394         
395         uint16_t selected_channels = _channel_selector.get_selected_channels();
396
397         if (selected_channels !=  0) {
398
399                 automation_items.push_back (SeparatorElem());
400
401                 /* these 3 MIDI "command" types are semantically more like automation than note data,
402                    but they are not MIDI controllers. We give them special status in this menu, since
403                    they will not show up in the controller list and anyone who actually knows
404                    something about MIDI (!) would not expect to find them there.
405                 */
406
407                 add_channel_command_menu_item (automation_items, _("Program Change"), MidiPgmChangeAutomation, MIDI_CMD_PGM_CHANGE);
408                 add_channel_command_menu_item (automation_items, _("Bender"), MidiPitchBenderAutomation, MIDI_CMD_BENDER);
409                 add_channel_command_menu_item (automation_items, _("Pressure"), MidiChannelPressureAutomation, MIDI_CMD_CHANNEL_PRESSURE);
410                 
411                 /* now all MIDI controllers. Always offer the possibility that we will rebuild the controllers menu
412                    since it might need to be updated after a channel mode change or other change. Also detach it
413                    first in case it has been used anywhere else.
414                 */
415                 
416                 build_controller_menu ();
417                 
418                 automation_items.push_back (SeparatorElem());
419                 automation_items.push_back (MenuElem (_("Controllers"), *controller_menu));
420         } else {
421                 automation_items.push_back (MenuElem (string_compose ("<i>%1</i>", _("No MIDI Channels selected"))));
422         }
423                 
424 }
425
426 void
427 MidiTimeAxisView::change_all_channel_tracks_visibility (bool yn, Evoral::Parameter param)
428 {
429         uint16_t selected_channels = _channel_selector.get_selected_channels();
430         
431         for (uint8_t chn = 0; chn < 16; chn++) {
432                 if (selected_channels & (0x0001 << chn)) {
433                         
434                         Evoral::Parameter fully_qualified_param (param.type(), chn, param.id());
435                         RouteAutomationNode* node = automation_track (fully_qualified_param);
436
437                         if (node && node->menu_item) {
438                                 node->menu_item->set_active (yn);
439                         }
440                 }
441         }
442 }
443
444 void
445 MidiTimeAxisView::add_channel_command_menu_item (Menu_Helpers::MenuList& items, const string& label, AutomationType auto_type, uint8_t cmd)
446 {
447         using namespace Menu_Helpers;
448
449         /* count the number of selected channels because we will build a different menu structure if there is more than 1 selected.
450          */
451
452         uint16_t selected_channels = _channel_selector.get_selected_channels();
453         int chn_cnt = 0;
454         
455         for (uint8_t chn = 0; chn < 16; chn++) {
456                 if (selected_channels & (0x0001 << chn)) {
457                         if (++chn_cnt > 1) {
458                                 break;
459                         }
460                 }
461         }
462         
463         if (chn_cnt > 1) {
464                 
465                 /* multiple channels - create a submenu, with 1 item per channel */
466                 
467                 Menu* chn_menu = manage (new Menu);
468                 MenuList& chn_items (chn_menu->items());
469                 Evoral::Parameter param_without_channel (auto_type, 0, cmd);
470
471                 /* add a couple of items to hide/show all of them */
472
473                 chn_items.push_back (MenuElem (_("Hide all channels"),
474                                                     sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility), 
475                                                                 false, param_without_channel)));
476                 chn_items.push_back (MenuElem (_("Show all channels"),
477                                                     sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility), 
478                                                                 true, param_without_channel)));
479                 
480                 for (uint8_t chn = 0; chn < 16; chn++) {
481                         if (selected_channels & (0x0001 << chn)) {
482                                 
483                                 /* for each selected channel, add a menu item for this controller */
484                                 
485                                 Evoral::Parameter fully_qualified_param (auto_type, chn, cmd);
486                                 chn_items.push_back (CheckMenuElem (string_compose (_("Channel %1"), chn+1),
487                                                                     sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
488                                                                                 fully_qualified_param)));
489                                 
490                                 RouteAutomationNode* node = automation_track (fully_qualified_param);
491                                 bool visible = false;
492                                 
493                                 if (node) {
494                                         if (node->track->marked_for_display()) {
495                                                 visible = true;
496                                         }
497                                 }
498                                 
499                                 CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&chn_items.back());
500                                 if (node) {
501                                         node->menu_item = cmi;
502                                 }
503
504                                 cmi->set_active (visible);
505
506                                 parameter_menu_map[fully_qualified_param] = cmi;
507                         }
508                 }
509                 
510                 /* now create an item in the parent menu that has the per-channel list as a submenu */
511                         
512                 items.push_back (MenuElem (label, *chn_menu));
513                 
514         } else {
515                 
516                 /* just one channel - create a single menu item for this command+channel combination*/
517                 
518                 for (uint8_t chn = 0; chn < 16; chn++) {
519                         if (selected_channels & (0x0001 << chn)) {
520                                 
521                                 Evoral::Parameter fully_qualified_param (auto_type, chn, cmd);
522                                 items.push_back (CheckMenuElem (label,
523                                                                 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
524                                                                             fully_qualified_param)));
525                                 
526                                 RouteAutomationNode* node = automation_track (fully_qualified_param);
527                                 bool visible = false;
528                                 
529                                 if (node) {
530                                         if (node->track->marked_for_display()) {
531                                                 visible = true;
532                                         }
533                                 }
534                                 
535                                 CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&items.back());
536                                 if (node) {
537                                         node->menu_item = cmi;
538                                 }
539
540                                 cmi->set_active (visible);
541                                 
542                                 parameter_menu_map[fully_qualified_param] = cmi;
543
544                                 /* one channel only */
545                                 break;
546                         }
547                 }
548         }
549 }
550
551 void
552 MidiTimeAxisView::build_controller_menu ()
553 {
554         using namespace Menu_Helpers;
555
556         if (controller_menu) {
557                 /* it exists and has not been invalidated by a channel mode change, so just return it */
558                 return;
559         }
560
561         controller_menu = new Menu; // explicitly managed by us
562         MenuList& items (controller_menu->items());
563
564         /* create several "top level" menu items for sets of controllers (16 at a time), and populate each one with a submenu 
565            for each controller+channel combination covering the currently selected channels for this track
566         */
567
568         uint16_t selected_channels = _channel_selector.get_selected_channels();
569
570         /* count the number of selected channels because we will build a different menu structure if there is more than 1 selected.
571          */
572
573         int chn_cnt = 0;
574         
575         for (uint8_t chn = 0; chn < 16; chn++) {
576                 if (selected_channels & (0x0001 << chn)) {
577                         if (++chn_cnt > 1) {
578                                 break;
579                         }
580                 }
581         }
582         
583         /* loop over all 127 MIDI controllers, in groups of 16 */
584
585         for (int i = 0; i < 127; i += 16) {
586
587                 Menu* ctl_menu = manage (new Menu);
588                 MenuList& ctl_items (ctl_menu->items());
589
590
591                 /* for each controller, consider whether to create a submenu or a single item */
592
593                 for (int ctl = i; ctl < i+16; ++ctl) {
594
595                         if (chn_cnt > 1) {
596
597                                 /* multiple channels - create a submenu, with 1 item per channel */
598
599                                 Menu* chn_menu = manage (new Menu);
600                                 MenuList& chn_items (chn_menu->items());
601
602                                 /* add a couple of items to hide/show this controller on all channels */
603                                 
604                                 Evoral::Parameter param_without_channel (MidiCCAutomation, 0, ctl);
605                                 chn_items.push_back (MenuElem (_("Hide all channels"),
606                                                                     sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility), 
607                                                                                 false, param_without_channel)));
608                                 chn_items.push_back (MenuElem (_("Show all channels"),
609                                                                     sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility), 
610                                                                                 true, param_without_channel)));
611                 
612                                 for (uint8_t chn = 0; chn < 16; chn++) {
613                                         if (selected_channels & (0x0001 << chn)) {
614                                                 
615                                                 /* for each selected channel, add a menu item for this controller */
616                                                 
617                                                 Evoral::Parameter fully_qualified_param (MidiCCAutomation, chn, ctl);
618                                                 chn_items.push_back (CheckMenuElem (string_compose (_("Channel %1"), chn+1),
619                                                                                     sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
620                                                                                                 fully_qualified_param)));
621
622                                                 RouteAutomationNode* node = automation_track (fully_qualified_param);
623                                                 bool visible = false;
624                                                 
625                                                 if (node) {
626                                                         if (node->track->marked_for_display()) {
627                                                                 visible = true;
628                                                         }
629                                                 }
630                                                 
631                                                 CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&chn_items.back());
632
633                                                 if (node) {
634                                                         node->menu_item = cmi;
635                                                 }
636
637                                                 cmi->set_active (visible);
638                                                 
639                                                 parameter_menu_map[fully_qualified_param] = cmi;
640                                         }
641                                 }
642                                 
643                                 /* add the per-channel menu to the list of controllers, with the name of the controller */
644                                 ctl_items.push_back (MenuElem (string_compose ("<b>%1</b>: %2", ctl, midi_name (ctl)), *chn_menu));
645                                 dynamic_cast<Label*> (ctl_items.back().get_child())->set_use_markup (true);
646                                       
647                         } else {
648
649                                 /* just one channel - create a single menu item for this ctl+channel combination*/
650
651                                 for (uint8_t chn = 0; chn < 16; chn++) {
652                                         if (selected_channels & (0x0001 << chn)) {
653                                                 
654                                                 Evoral::Parameter fully_qualified_param (MidiCCAutomation, chn, ctl);
655                                                 ctl_items.push_back (CheckMenuElem (_route->describe_parameter (fully_qualified_param),
656                                                                                     sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
657                                                                                                 fully_qualified_param)));
658                                                 
659                                                 RouteAutomationNode* node = automation_track (fully_qualified_param);
660                                                 bool visible = false;
661                                                 
662                                                 if (node) {
663                                                         if (node->track->marked_for_display()) {
664                                                                 visible = true;
665                                                         }
666                                                 }
667                                                 
668                                                 CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&ctl_items.back());
669                                                 if (node) {
670                                                         node->menu_item = cmi;
671                                                 }
672
673                                                 cmi->set_active (visible);
674                                                 
675
676                                                 parameter_menu_map[fully_qualified_param] = cmi;
677                                                 /* one channel only */
678                                                 break;
679                                         }
680                                 }
681                         }
682                 }
683                         
684                 /* add the menu for this block of controllers to the overall controller menu */
685
686                 items.push_back (MenuElem (string_compose (_("Controllers %1-%2"), i, i+15), *ctl_menu));
687         }
688 }
689
690 Gtk::Menu*
691 MidiTimeAxisView::build_note_mode_menu()
692 {
693         using namespace Menu_Helpers;
694
695         Menu* mode_menu = manage (new Menu);
696         MenuList& items = mode_menu->items();
697         mode_menu->set_name ("ArdourContextMenu");
698
699         RadioMenuItem::Group mode_group;
700         items.push_back (RadioMenuElem (mode_group, _("Sustained"),
701                                 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_mode), Sustained)));
702         _note_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
703         _note_mode_item->set_active(_note_mode == Sustained);
704
705         items.push_back (RadioMenuElem (mode_group, _("Percussive"),
706                                 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_mode), Percussive)));
707         _percussion_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
708         _percussion_mode_item->set_active(_note_mode == Percussive);
709
710         return mode_menu;
711 }
712
713 Gtk::Menu*
714 MidiTimeAxisView::build_color_mode_menu()
715 {
716         using namespace Menu_Helpers;
717
718         Menu* mode_menu = manage (new Menu);
719         MenuList& items = mode_menu->items();
720         mode_menu->set_name ("ArdourContextMenu");
721
722         RadioMenuItem::Group mode_group;
723         items.push_back (RadioMenuElem (mode_group, _("Meter Colors"),
724                                 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode), MeterColors)));
725         _meter_color_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
726         _meter_color_mode_item->set_active(_color_mode == MeterColors);
727
728         items.push_back (RadioMenuElem (mode_group, _("Channel Colors"),
729                                 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode), ChannelColors)));
730         _channel_color_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
731         _channel_color_mode_item->set_active(_color_mode == ChannelColors);
732
733         items.push_back (RadioMenuElem (mode_group, _("Track Color"),
734                                 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode), TrackColor)));
735         _channel_color_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
736         _channel_color_mode_item->set_active(_color_mode == TrackColor);
737
738         return mode_menu;
739 }
740
741 void
742 MidiTimeAxisView::set_note_mode(NoteMode mode)
743 {
744         if (_note_mode != mode || midi_track()->note_mode() != mode) {
745                 _note_mode = mode;
746                 midi_track()->set_note_mode(mode);
747                 xml_node->add_property ("note-mode", enum_2_string(_note_mode));
748                 _view->redisplay_track();
749         }
750 }
751
752 void
753 MidiTimeAxisView::set_color_mode(ColorMode mode)
754 {
755         if (_color_mode != mode) {
756                 if (mode == ChannelColors) {
757                         _channel_selector.set_channel_colors(CanvasNoteEvent::midi_channel_colors);
758                 } else {
759                         _channel_selector.set_default_channel_color();
760                 }
761
762                 _color_mode = mode;
763                 xml_node->add_property ("color-mode", enum_2_string(_color_mode));
764                 _view->redisplay_track();
765         }
766 }
767
768 void
769 MidiTimeAxisView::set_note_range(MidiStreamView::VisibleNoteRange range)
770 {
771         if (!_ignore_signals)
772                 midi_view()->set_note_range(range);
773 }
774
775
776 void
777 MidiTimeAxisView::update_range()
778 {
779         MidiGhostRegion* mgr;
780
781         for(list<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
782                 if ((mgr = dynamic_cast<MidiGhostRegion*>(*i)) != 0) {
783                         mgr->update_range();
784                 }
785         }
786 }
787
788 void
789 MidiTimeAxisView::show_all_automation ()
790 {
791         if (midi_track()) {
792                 const set<Evoral::Parameter> params = midi_track()->midi_playlist()->contained_automation();
793
794                 for (set<Evoral::Parameter>::const_iterator i = params.begin(); i != params.end(); ++i) {
795                         create_automation_child(*i, true);
796                 }
797         }
798
799         RouteTimeAxisView::show_all_automation ();
800 }
801
802 void
803 MidiTimeAxisView::show_existing_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_existing_automation ();
814 }
815
816 /** Hide an automation track for the given parameter (pitch bend, channel pressure).
817  */
818 void
819 MidiTimeAxisView::create_automation_child (const Evoral::Parameter& param, bool show)
820 {
821         /* These controllers are region "automation", so we do not create
822          * an AutomationList/Line for the track */
823
824         if (param.type() == NullAutomation) {
825                 cerr << "WARNING: Attempt to create NullAutomation child, ignoring" << endl;
826                 return;
827         }
828
829         AutomationTracks::iterator existing = _automation_tracks.find (param);
830         if (existing != _automation_tracks.end()) {
831                 return;
832         }
833
834         boost::shared_ptr<AutomationControl> c = _route->get_control (param);
835
836         assert(c);
837
838         boost::shared_ptr<AutomationTimeAxisView> track(new AutomationTimeAxisView (_session,
839                         _route, boost::shared_ptr<ARDOUR::Automatable>(), c,
840                         _editor,
841                         *this,
842                         true,
843                         parent_canvas,
844                         _route->describe_parameter(param)));
845
846         add_automation_child (param, track, show);
847 }
848
849
850 void
851 MidiTimeAxisView::route_active_changed ()
852 {
853         RouteUI::route_active_changed ();
854
855         if (is_track()) {
856                 if (_route->active()) {
857                         controls_ebox.set_name ("MidiTrackControlsBaseUnselected");
858                         controls_base_selected_name = "MidiTrackControlsBaseSelected";
859                         controls_base_unselected_name = "MidiTrackControlsBaseUnselected";
860                 } else {
861                         controls_ebox.set_name ("MidiTrackControlsBaseInactiveUnselected");
862                         controls_base_selected_name = "MidiTrackControlsBaseInactiveSelected";
863                         controls_base_unselected_name = "MidiTrackControlsBaseInactiveUnselected";
864                 }
865         } else {
866
867                 throw; // wha?
868
869                 if (_route->active()) {
870                         controls_ebox.set_name ("BusControlsBaseUnselected");
871                         controls_base_selected_name = "BusControlsBaseSelected";
872                         controls_base_unselected_name = "BusControlsBaseUnselected";
873                 } else {
874                         controls_ebox.set_name ("BusControlsBaseInactiveUnselected");
875                         controls_base_selected_name = "BusControlsBaseInactiveSelected";
876                         controls_base_unselected_name = "BusControlsBaseInactiveUnselected";
877                 }
878         }
879 }
880
881 void
882 MidiTimeAxisView::start_step_editing ()
883 {
884         step_edit_insert_position = _editor.get_preferred_edit_position ();
885         step_edit_beat_pos = 0;
886         step_edit_region = playlist()->top_region_at (step_edit_insert_position);
887
888         if (step_edit_region) {
889                 RegionView* rv = view()->find_view (step_edit_region);
890                 step_edit_region_view = dynamic_cast<MidiRegionView*> (rv);
891         } else {
892                 step_edit_region_view = 0;
893         }
894
895         midi_track()->set_step_editing (true);
896 }
897
898 void
899 MidiTimeAxisView::stop_step_editing ()
900 {
901         midi_track()->set_step_editing (false);
902 }
903
904 void
905 MidiTimeAxisView::check_step_edit ()
906 {
907         MidiRingBuffer<nframes_t>& incoming (midi_track()->step_edit_ring_buffer());
908         Evoral::Note<Evoral::MusicalTime> note;
909         uint8_t* buf;
910         uint32_t bufsize = 32;
911
912         buf = new uint8_t[bufsize];
913
914         while (incoming.read_space()) {
915                 nframes_t time;
916                 Evoral::EventType type;
917                 uint32_t size;
918
919                 incoming.read_prefix (&time, &type, &size);
920
921                 if (size > bufsize) {
922                         delete [] buf;
923                         bufsize = size;
924                         buf = new uint8_t[bufsize];
925                 }
926
927                 incoming.read_contents (size, buf);
928
929                 if ((buf[0] & 0xf0) == MIDI_CMD_NOTE_ON) {
930
931                         if (step_edit_region == 0) {
932
933                                 step_edit_region = add_region (step_edit_insert_position);
934                                 RegionView* rv = view()->find_view (step_edit_region);
935
936                                 if (rv) {
937                                         step_edit_region_view = dynamic_cast<MidiRegionView*>(rv);
938                                 } else {
939                                         fatal << X_("programming error: no view found for new MIDI region") << endmsg;
940                                         /*NOTREACHED*/
941                                 }
942                         }
943
944                         if (step_edit_region_view) {
945
946                                 bool success;
947                                 Evoral::MusicalTime beats = _editor.get_grid_type_as_beats (success, step_edit_insert_position);
948
949                                 if (!success) {
950                                         continue;
951                                 }
952
953                                 step_edit_region_view->add_note (buf[0] & 0xf, buf[1], buf[2], step_edit_beat_pos, beats);
954                                 step_edit_beat_pos += beats;
955                         }
956                 }
957
958         }
959 }
960
961 void
962 MidiTimeAxisView::step_edit_rest ()
963 {
964         bool success;
965         Evoral::MusicalTime beats = _editor.get_grid_type_as_beats (success, step_edit_insert_position);
966         step_edit_beat_pos += beats;
967 }
968
969 boost::shared_ptr<Region>
970 MidiTimeAxisView::add_region (nframes64_t pos)
971 {
972         Editor* real_editor = dynamic_cast<Editor*> (&_editor);
973
974         real_editor->begin_reversible_command (_("create region"));
975         playlist()->clear_history ();
976
977         framepos_t start = pos;
978         real_editor->snap_to (start, -1);
979         const Meter& m = _session->tempo_map().meter_at(start);
980         const Tempo& t = _session->tempo_map().tempo_at(start);
981         double length = floor (m.frames_per_bar(t, _session->frame_rate()));
982
983         boost::shared_ptr<Source> src = _session->create_midi_source_for_session (view()->trackview().track()->name());
984
985         PropertyList plist; 
986         
987         plist.add (ARDOUR::Properties::start, 0);
988         plist.add (ARDOUR::Properties::length, length);
989         plist.add (ARDOUR::Properties::name, PBD::basename_nosuffix(src->name()));
990         
991         boost::shared_ptr<Region> region = (RegionFactory::create (src, plist));
992
993         playlist()->add_region (region, start);
994         _session->add_command (new StatefulDiffCommand (playlist()));
995
996         real_editor->commit_reversible_command();
997
998         return region;
999 }
1000
1001 void
1002 MidiTimeAxisView::add_note_selection (uint8_t note)
1003 {
1004         if (!_editor.internal_editing()) {
1005                 return;
1006         }
1007
1008         uint16_t chn_mask = _channel_selector.get_selected_channels();
1009
1010         if (_view->num_selected_regionviews() == 0) {
1011                 _view->foreach_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection_region_view), note, chn_mask));
1012         } else {
1013                 _view->foreach_selected_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection_region_view), note, chn_mask));
1014         }
1015 }
1016
1017 void
1018 MidiTimeAxisView::extend_note_selection (uint8_t note)
1019 {
1020         if (!_editor.internal_editing()) {
1021                 return;
1022         }
1023
1024         uint16_t chn_mask = _channel_selector.get_selected_channels();
1025
1026         if (_view->num_selected_regionviews() == 0) {
1027                 _view->foreach_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::extend_note_selection_region_view), note, chn_mask));
1028         } else {
1029                 _view->foreach_selected_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::extend_note_selection_region_view), note, chn_mask));
1030         }
1031 }
1032
1033 void
1034 MidiTimeAxisView::toggle_note_selection (uint8_t note)
1035 {
1036         if (!_editor.internal_editing()) {
1037                 return;
1038         }
1039
1040         uint16_t chn_mask = _channel_selector.get_selected_channels();
1041
1042         if (_view->num_selected_regionviews() == 0) {
1043                 _view->foreach_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection_region_view), note, chn_mask));
1044         } else {
1045                 _view->foreach_selected_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection_region_view), note, chn_mask));
1046         }
1047 }
1048
1049 void
1050 MidiTimeAxisView::add_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1051 {
1052         dynamic_cast<MidiRegionView*>(rv)->select_matching_notes (note, chn_mask, false, false);
1053 }
1054
1055 void
1056 MidiTimeAxisView::extend_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1057 {
1058         dynamic_cast<MidiRegionView*>(rv)->select_matching_notes (note, chn_mask, true, true);
1059 }
1060
1061 void
1062 MidiTimeAxisView::toggle_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1063 {
1064         dynamic_cast<MidiRegionView*>(rv)->toggle_matching_notes (note, chn_mask);
1065 }
1066
1067 void
1068 MidiTimeAxisView::set_channel_mode (ChannelMode, uint16_t)
1069 {
1070         /* hide all automation tracks that use the wrong channel(s) and show all those that use
1071            the right ones.
1072         */
1073
1074         uint16_t selected_channels = _channel_selector.get_selected_channels();
1075         bool changed = false;
1076
1077         no_redraw = true;
1078
1079         for (uint32_t ctl = 0; ctl < 127; ++ctl) {
1080
1081                 for (uint32_t chn = 0; chn < 16; ++chn) {
1082                         Evoral::Parameter fully_qualified_param (MidiCCAutomation, chn, ctl);
1083                         RouteAutomationNode* node = automation_track (fully_qualified_param);
1084
1085                         if (!node) {
1086                                 continue;
1087                         }
1088                         
1089                         if ((selected_channels & (0x0001 << chn)) == 0) {
1090                                 /* channel not in use. hiding it will trigger RouteTimeAxisView::automation_track_hidden() 
1091                                    which will cause a redraw. We don't want one per channel, so block that with no_redraw.
1092                                  */
1093                                 changed = node->track->set_visibility (false) || changed;
1094                         } else {
1095                                 changed = node->track->set_visibility (true) || changed;
1096                         }
1097                 }
1098         }
1099
1100         no_redraw = false;
1101
1102         /* TODO: Bender, PgmChange, Pressure */
1103
1104         /* invalidate the controller menu, so that we rebuilt it next time */
1105         delete controller_menu;
1106         controller_menu = 0;
1107
1108         if (changed) {
1109                 _route->gui_changed ("track_height", this);
1110         }
1111 }