remove craziness of propagating MIDI 7 bit limits into MIDNAM handling by replacing...
[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/event_type_map.h"
42 #include "ardour/midi_patch_manager.h"
43 #include "ardour/midi_playlist.h"
44 #include "ardour/midi_region.h"
45 #include "ardour/midi_source.h"
46 #include "ardour/midi_track.h"
47 #include "ardour/operations.h"
48 #include "ardour/playlist.h"
49 #include "ardour/region.h"
50 #include "ardour/region_factory.h"
51 #include "ardour/route.h"
52 #include "ardour/session.h"
53 #include "ardour/session_object.h"
54 #include "ardour/source.h"
55 #include "ardour/track.h"
56 #include "ardour/types.h"
57
58 #include "midi++/names.h"
59
60 #include "ardour_ui.h"
61 #include "ardour_button.h"
62 #include "automation_line.h"
63 #include "automation_time_axis.h"
64 #include "canvas-note-event.h"
65 #include "canvas_impl.h"
66 #include "editor.h"
67 #include "enums.h"
68 #include "ghostregion.h"
69 #include "gui_thread.h"
70 #include "keyboard.h"
71 #include "midi_scroomer.h"
72 #include "midi_streamview.h"
73 #include "midi_region_view.h"
74 #include "midi_time_axis.h"
75 #include "piano_roll_header.h"
76 #include "playlist_selector.h"
77 #include "plugin_selector.h"
78 #include "plugin_ui.h"
79 #include "point_selection.h"
80 #include "prompter.h"
81 #include "region_view.h"
82 #include "rgb_macros.h"
83 #include "selection.h"
84 #include "step_editor.h"
85 #include "simplerect.h"
86 #include "utils.h"
87
88 #include "ardour/midi_track.h"
89
90 #include "i18n.h"
91
92 using namespace ARDOUR;
93 using namespace PBD;
94 using namespace Gtk;
95 using namespace Gtkmm2ext;
96 using namespace Editing;
97
98 // Minimum height at which a control is displayed
99 static const uint32_t MIDI_CONTROLS_BOX_MIN_HEIGHT = 162;
100 static const uint32_t KEYBOARD_MIN_HEIGHT = 140;
101
102 MidiTimeAxisView::MidiTimeAxisView (PublicEditor& ed, Session* sess, Canvas& canvas)
103         : AxisView(sess) // virtually inherited
104         , RouteTimeAxisView(ed, sess, 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         , controller_menu (0)
118         , _step_editor (0)
119 {
120 }
121
122 void
123 MidiTimeAxisView::set_route (boost::shared_ptr<Route> rt)
124 {
125         _route = rt;
126         
127         _view = new MidiStreamView (*this);
128         
129         if (is_track ()) {
130                 _piano_roll_header = new PianoRollHeader(*midi_view());
131                 _range_scroomer = new MidiScroomer(midi_view()->note_range_adjustment);
132         }
133
134         /* This next call will result in our height being set up, so it must come after
135            the creation of the piano roll / range scroomer as their visibility is set up
136            when our height is.
137         */
138         RouteTimeAxisView::set_route (rt);
139
140         _view->apply_color (_color, StreamView::RegionColor);
141
142         subplugin_menu.set_name ("ArdourContextMenu");
143
144         if (!gui_property ("note-range-min").empty ()) {
145                 midi_view()->apply_note_range (atoi (gui_property ("note-range-min").c_str()), atoi (gui_property ("note-range-max").c_str()), true);
146         }
147         midi_view()->NoteRangeChanged.connect (sigc::mem_fun (*this, &MidiTimeAxisView::note_range_changed));
148
149         ignore_toggle = false;
150
151         if (is_midi_track()) {
152                 controls_ebox.set_name ("MidiTimeAxisViewControlsBaseUnselected");
153                 _note_mode = midi_track()->note_mode();
154         } else { // MIDI bus (which doesn't exist yet..)
155                 controls_ebox.set_name ("MidiBusControlsBaseUnselected");
156         }
157
158         /* map current state of the route */
159
160         processors_changed (RouteProcessorChange ());
161
162         _route->processors_changed.connect (*this, invalidator (*this), boost::bind (&MidiTimeAxisView::processors_changed, this, _1), gui_context());
163
164         if (is_track()) {
165                 _piano_roll_header->SetNoteSelection.connect (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_selection));
166                 _piano_roll_header->AddNoteSelection.connect (sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection));
167                 _piano_roll_header->ExtendNoteSelection.connect (sigc::mem_fun (*this, &MidiTimeAxisView::extend_note_selection));
168                 _piano_roll_header->ToggleNoteSelection.connect (sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection));
169
170                 /* Suspend updates of the StreamView during scroomer drags to speed things up */
171                 _range_scroomer->DragStarting.connect (sigc::mem_fun (*midi_view(), &MidiStreamView::suspend_updates));
172                 _range_scroomer->DragFinishing.connect (sigc::mem_fun (*midi_view(), &MidiStreamView::resume_updates));
173
174                 controls_hbox.pack_start(*_range_scroomer);
175                 controls_hbox.pack_start(*_piano_roll_header);
176
177                 controls_ebox.set_name ("MidiTrackControlsBaseUnselected");
178                 controls_base_selected_name = "MidiTrackControlsBaseSelected";
179                 controls_base_unselected_name = "MidiTrackControlsBaseUnselected";
180
181                 midi_view()->NoteRangeChanged.connect (sigc::mem_fun(*this, &MidiTimeAxisView::update_range));
182
183                 /* ask for notifications of any new RegionViews */
184                 _view->RegionViewAdded.connect (sigc::mem_fun(*this, &MidiTimeAxisView::region_view_added));
185
186                 if (!_editor.have_idled()) {
187                         /* first idle will do what we need */
188                 } else {
189                         first_idle ();
190                 }
191         }
192
193         HBox* midi_controls_hbox = manage(new HBox());
194
195         MIDI::Name::MidiPatchManager& patch_manager = MIDI::Name::MidiPatchManager::instance();
196
197         MIDI::Name::MasterDeviceNames::Models::const_iterator m = patch_manager.all_models().begin();
198         for (; m != patch_manager.all_models().end(); ++m) {
199                 _model_selector.append_text(m->c_str());
200         }
201
202
203         _model_selector.signal_changed().connect(sigc::mem_fun(*this, &MidiTimeAxisView::model_changed));
204
205         _custom_device_mode_selector.signal_changed().connect(
206                         sigc::mem_fun(*this, &MidiTimeAxisView::custom_device_mode_changed));
207
208         _model_selector.set_active_text (gui_property (X_("midnam-model-name")));
209         _custom_device_mode_selector.set_active_text (gui_property (X_("midnam-custom-device-mode")));
210
211         midi_controls_hbox->pack_start(_channel_selector, true, false);
212         if (!patch_manager.all_models().empty()) {
213                 _midi_controls_box.pack_start(_model_selector, true, false);
214                 _midi_controls_box.pack_start(_custom_device_mode_selector, true, false);
215         }
216
217         _midi_controls_box.pack_start(*midi_controls_hbox, true, true);
218
219         controls_vbox.pack_start(_midi_controls_box, false, false);
220
221         // restore channel selector settings
222         _channel_selector.set_channel_mode(midi_track()->get_channel_mode(), midi_track()->get_channel_mask());
223         _channel_selector.mode_changed.connect(
224                 sigc::mem_fun(*midi_track(), &MidiTrack::set_channel_mode));
225         _channel_selector.mode_changed.connect(
226                 sigc::mem_fun(*this, &MidiTimeAxisView::set_channel_mode));
227
228         string prop = gui_property ("color-mode");
229         if (!prop.empty()) {
230                 _color_mode = ColorMode (string_2_enum(prop, _color_mode));
231                 if (_color_mode == ChannelColors) {
232                         _channel_selector.set_channel_colors(CanvasNoteEvent::midi_channel_colors);
233                 }
234         }
235
236         set_color_mode (_color_mode, true, false);
237
238         prop = gui_property ("note-mode");
239         if (!prop.empty()) {
240                 _note_mode = NoteMode (string_2_enum (prop, _note_mode));
241                 if (_percussion_mode_item) {
242                         _percussion_mode_item->set_active (_note_mode == Percussive);
243                 }
244         }
245
246         /* Look for any GUI object state nodes that represent automation children that should exist, and create
247          * the children.
248          */
249
250         list<string> gui_ids = gui_object_state().all_ids ();
251         for (list<string>::const_iterator i = gui_ids.begin(); i != gui_ids.end(); ++i) {
252                 PBD::ID route_id;
253                 bool has_parameter;
254                 Evoral::Parameter parameter (0, 0, 0);
255
256                 bool const p = AutomationTimeAxisView::parse_state_id (*i, route_id, has_parameter, parameter);
257                 if (p && route_id == _route->id () && has_parameter) {
258                         create_automation_child (parameter, string_is_affirmative (gui_object_state().get_string (*i, X_("visible"))));
259                 }
260         }
261 }
262
263 void
264 MidiTimeAxisView::first_idle ()
265 {
266         if (is_track ()) {
267                 _view->attach ();
268         }
269 }
270
271 MidiTimeAxisView::~MidiTimeAxisView ()
272 {
273         delete _piano_roll_header;
274         _piano_roll_header = 0;
275
276         delete _range_scroomer;
277         _range_scroomer = 0;
278
279         delete controller_menu;
280         delete _step_editor;
281 }
282
283 void
284 MidiTimeAxisView::enter_internal_edit_mode ()
285 {
286         if (midi_view()) {
287                 midi_view()->enter_internal_edit_mode ();
288         }
289 }
290
291 void
292 MidiTimeAxisView::leave_internal_edit_mode ()
293 {
294         if (midi_view()) {
295                 midi_view()->leave_internal_edit_mode ();
296         }
297 }
298
299 void
300 MidiTimeAxisView::check_step_edit ()
301 {
302         ensure_step_editor ();
303         _step_editor->check_step_edit ();
304 }
305
306 void
307 MidiTimeAxisView::model_changed()
308 {
309         std::list<std::string> device_modes = MIDI::Name::MidiPatchManager::instance()
310                 .custom_device_mode_names_by_model(_model_selector.get_active_text());
311
312         _custom_device_mode_selector.clear_items();
313
314         for (std::list<std::string>::const_iterator i = device_modes.begin();
315                         i != device_modes.end(); ++i) {
316                 _custom_device_mode_selector.append_text(*i);
317         }
318
319         _custom_device_mode_selector.set_active(0);
320
321         set_gui_property (X_("midnam-model-name"), midi_patch_model ());
322 }
323
324 void
325 MidiTimeAxisView::custom_device_mode_changed()
326 {
327         _midi_patch_settings_changed.emit (midi_patch_model (), midi_patch_custom_device_mode ());
328         set_gui_property (X_("midnam-custom-device-mode"), midi_patch_custom_device_mode ());
329 }
330
331 MidiStreamView*
332 MidiTimeAxisView::midi_view()
333 {
334         return dynamic_cast<MidiStreamView*>(_view);
335 }
336
337 void
338 MidiTimeAxisView::set_height (uint32_t h)
339 {
340         if (h >= MIDI_CONTROLS_BOX_MIN_HEIGHT) {
341                 _midi_controls_box.show_all ();
342         } else {
343                 _midi_controls_box.hide();
344         }
345         
346         if (h >= KEYBOARD_MIN_HEIGHT) {
347                 if (is_track() && _range_scroomer) {
348                         _range_scroomer->show();
349                 }
350                 if (is_track() && _piano_roll_header) {
351                         _piano_roll_header->show();
352                 }
353         } else {
354                 if (is_track() && _range_scroomer) {
355                         _range_scroomer->hide();
356                 }
357                 if (is_track() && _piano_roll_header) {
358                         _piano_roll_header->hide();
359                 }
360         }
361
362         /* We need to do this after changing visibility of our stuff, as it will
363            eventually trigger a call to Editor::reset_controls_layout_width(),
364            which needs to know if we have just shown or hidden a scroomer /
365            piano roll.
366         */
367         RouteTimeAxisView::set_height (h);
368 }
369
370 void
371 MidiTimeAxisView::append_extra_display_menu_items ()
372 {
373         using namespace Menu_Helpers;
374
375         MenuList& items = display_menu->items();
376
377         // Note range
378         Menu *range_menu = manage(new Menu);
379         MenuList& range_items = range_menu->items();
380         range_menu->set_name ("ArdourContextMenu");
381
382         range_items.push_back (MenuElem (_("Show Full Range"), sigc::bind (
383                         sigc::mem_fun(*this, &MidiTimeAxisView::set_note_range),
384                         MidiStreamView::FullRange)));
385
386         range_items.push_back (MenuElem (_("Fit Contents"), sigc::bind (
387                         sigc::mem_fun(*this, &MidiTimeAxisView::set_note_range),
388                         MidiStreamView::ContentsRange)));
389
390         items.push_back (MenuElem (_("Note Range"), *range_menu));
391         items.push_back (MenuElem (_("Note Mode"), *build_note_mode_menu()));
392
393         items.push_back (CheckMenuElem (_("MIDI Thru"), sigc::mem_fun(*this, &MidiTimeAxisView::toggle_midi_thru)));
394         _midi_thru_item = dynamic_cast<CheckMenuItem*>(&items.back());
395
396         items.push_back (SeparatorElem ());
397 }
398
399 void
400 MidiTimeAxisView::toggle_midi_thru ()
401 {
402         if (!_midi_thru_item) {
403                 return;
404         }
405
406         bool view_yn = _midi_thru_item->get_active();
407         if (view_yn != midi_track()->midi_thru()) {
408                 midi_track()->set_midi_thru (view_yn);
409         }
410 }
411
412 void
413 MidiTimeAxisView::build_automation_action_menu (bool for_selection)
414 {
415         using namespace Menu_Helpers;
416
417         /* If we have a controller menu, we need to detach it before
418            RouteTimeAxis::build_automation_action_menu destroys the
419            menu it is attached to.  Otherwise GTK destroys
420            controller_menu's gobj, meaning that it can't be reattached
421            below.  See bug #3134.
422         */
423
424         if (controller_menu) {
425                 detach_menu (*controller_menu);
426         }
427
428         _channel_command_menu_map.clear ();
429         RouteTimeAxisView::build_automation_action_menu (for_selection);
430
431         MenuList& automation_items = automation_action_menu->items();
432
433         uint16_t selected_channels = _channel_selector.get_selected_channels();
434
435         if (selected_channels !=  0) {
436
437                 automation_items.push_back (SeparatorElem());
438
439                 /* these 2 MIDI "command" types are semantically more like automation than note data,
440                    but they are not MIDI controllers. We give them special status in this menu, since
441                    they will not show up in the controller list and anyone who actually knows
442                    something about MIDI (!) would not expect to find them there.
443                 */
444
445                 add_channel_command_menu_item (automation_items, _("Bender"), MidiPitchBenderAutomation, 0);
446                 automation_items.back().set_sensitive (!for_selection || _editor.get_selection().tracks.size() == 1);
447                 add_channel_command_menu_item (automation_items, _("Pressure"), MidiChannelPressureAutomation, 0);
448                 automation_items.back().set_sensitive (!for_selection || _editor.get_selection().tracks.size() == 1);
449
450                 /* now all MIDI controllers. Always offer the possibility that we will rebuild the controllers menu
451                    since it might need to be updated after a channel mode change or other change. Also detach it
452                    first in case it has been used anywhere else.
453                 */
454
455                 build_controller_menu ();
456
457                 automation_items.push_back (SeparatorElem());
458                 automation_items.push_back (MenuElem (_("Controllers"), *controller_menu));
459                 automation_items.back().set_sensitive (!for_selection || _editor.get_selection().tracks.size() == 1);
460         } else {
461                 automation_items.push_back (MenuElem (string_compose ("<i>%1</i>", _("No MIDI Channels selected"))));
462                 dynamic_cast<Label*> (automation_items.back().get_child())->set_use_markup (true);
463         }
464
465 }
466
467 void
468 MidiTimeAxisView::change_all_channel_tracks_visibility (bool yn, Evoral::Parameter param)
469 {
470         uint16_t selected_channels = _channel_selector.get_selected_channels();
471
472         for (uint8_t chn = 0; chn < 16; chn++) {
473                 if (selected_channels & (0x0001 << chn)) {
474
475                         Evoral::Parameter fully_qualified_param (param.type(), chn, param.id());
476                         Gtk::CheckMenuItem* menu = automation_child_menu_item (fully_qualified_param);
477
478                         if (menu) {
479                                 menu->set_active (yn);
480                         }
481                 }
482         }
483 }
484
485 void
486 MidiTimeAxisView::add_channel_command_menu_item (Menu_Helpers::MenuList& items, const string& label, AutomationType auto_type, uint8_t cmd)
487 {
488         using namespace Menu_Helpers;
489
490         /* count the number of selected channels because we will build a different menu structure if there is more than 1 selected.
491          */
492
493         uint16_t selected_channels = _channel_selector.get_selected_channels();
494         int chn_cnt = 0;
495
496         for (uint8_t chn = 0; chn < 16; chn++) {
497                 if (selected_channels & (0x0001 << chn)) {
498                         if (++chn_cnt > 1) {
499                                 break;
500                         }
501                 }
502         }
503
504         if (chn_cnt > 1) {
505
506                 /* multiple channels - create a submenu, with 1 item per channel */
507
508                 Menu* chn_menu = manage (new Menu);
509                 MenuList& chn_items (chn_menu->items());
510                 Evoral::Parameter param_without_channel (auto_type, 0, cmd);
511
512                 /* add a couple of items to hide/show all of them */
513
514                 chn_items.push_back (MenuElem (_("Hide all channels"),
515                                                     sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
516                                                                 false, param_without_channel)));
517                 chn_items.push_back (MenuElem (_("Show all channels"),
518                                                     sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
519                                                                 true, param_without_channel)));
520
521                 for (uint8_t chn = 0; chn < 16; chn++) {
522                         if (selected_channels & (0x0001 << chn)) {
523
524                                 /* for each selected channel, add a menu item for this controller */
525
526                                 Evoral::Parameter fully_qualified_param (auto_type, chn, cmd);
527                                 chn_items.push_back (CheckMenuElem (string_compose (_("Channel %1"), chn+1),
528                                                                     sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
529                                                                                 fully_qualified_param)));
530
531                                 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
532                                 bool visible = false;
533
534                                 if (track) {
535                                         if (track->marked_for_display()) {
536                                                 visible = true;
537                                         }
538                                 }
539
540                                 CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&chn_items.back());
541                                 _channel_command_menu_map[fully_qualified_param] = cmi;
542                                 cmi->set_active (visible);
543                         }
544                 }
545
546                 /* now create an item in the parent menu that has the per-channel list as a submenu */
547
548                 items.push_back (MenuElem (label, *chn_menu));
549
550         } else {
551
552                 /* just one channel - create a single menu item for this command+channel combination*/
553
554                 for (uint8_t chn = 0; chn < 16; chn++) {
555                         if (selected_channels & (0x0001 << chn)) {
556
557                                 Evoral::Parameter fully_qualified_param (auto_type, chn, cmd);
558                                 items.push_back (CheckMenuElem (label,
559                                                                 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
560                                                                             fully_qualified_param)));
561
562                                 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
563                                 bool visible = false;
564
565                                 if (track) {
566                                         if (track->marked_for_display()) {
567                                                 visible = true;
568                                         }
569                                 }
570
571                                 CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&items.back());
572                                 _channel_command_menu_map[fully_qualified_param] = cmi;
573                                 cmi->set_active (visible);
574
575                                 /* one channel only */
576                                 break;
577                         }
578                 }
579         }
580 }
581
582 void
583 MidiTimeAxisView::build_controller_menu ()
584 {
585         using namespace Menu_Helpers;
586
587         if (controller_menu) {
588                 /* it exists and has not been invalidated by a channel mode change, so just return it */
589                 return;
590         }
591
592         controller_menu = new Menu; // explicitly managed by us
593         MenuList& items (controller_menu->items());
594
595         /* create several "top level" menu items for sets of controllers (16 at a time), and populate each one with a submenu
596            for each controller+channel combination covering the currently selected channels for this track
597         */
598
599         uint16_t selected_channels = _channel_selector.get_selected_channels();
600
601         /* count the number of selected channels because we will build a different menu structure if there is more than 1 selected.
602          */
603
604         int chn_cnt = 0;
605
606         for (uint8_t chn = 0; chn < 16; chn++) {
607                 if (selected_channels & (0x0001 << chn)) {
608                         if (++chn_cnt > 1) {
609                                 break;
610                         }
611                 }
612         }
613
614         /* loop over all 127 MIDI controllers, in groups of 16; except don't offer
615            bank select controllers, as they are handled by the `patch' code */
616
617         for (int i = 0; i < 127; i += 16) {
618
619                 Menu* ctl_menu = manage (new Menu);
620                 MenuList& ctl_items (ctl_menu->items());
621
622
623                 /* for each controller, consider whether to create a submenu or a single item */
624
625                 for (int ctl = i; ctl < i+16; ++ctl) {
626
627                         if (ctl == MIDI_CTL_MSB_BANK || ctl == MIDI_CTL_LSB_BANK) {
628                                 continue;
629                         }
630
631                         if (chn_cnt > 1) {
632
633                                 /* multiple channels - create a submenu, with 1 item per channel */
634
635                                 Menu* chn_menu = manage (new Menu);
636                                 MenuList& chn_items (chn_menu->items());
637
638                                 /* add a couple of items to hide/show this controller on all channels */
639
640                                 Evoral::Parameter param_without_channel (MidiCCAutomation, 0, ctl);
641                                 chn_items.push_back (MenuElem (_("Hide all channels"),
642                                                                     sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
643                                                                                 false, param_without_channel)));
644                                 chn_items.push_back (MenuElem (_("Show all channels"),
645                                                                     sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
646                                                                                 true, param_without_channel)));
647
648                                 for (uint8_t chn = 0; chn < 16; chn++) {
649                                         if (selected_channels & (0x0001 << chn)) {
650
651                                                 /* for each selected channel, add a menu item for this controller */
652
653                                                 Evoral::Parameter fully_qualified_param (MidiCCAutomation, chn, ctl);
654                                                 chn_items.push_back (CheckMenuElem (string_compose (_("Channel %1"), chn+1),
655                                                                                     sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
656                                                                                                 fully_qualified_param)));
657
658                                                 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
659                                                 bool visible = false;
660
661                                                 if (track) {
662                                                         if (track->marked_for_display()) {
663                                                                 visible = true;
664                                                         }
665                                                 }
666
667                                                 CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&chn_items.back());
668                                                 _controller_menu_map[fully_qualified_param] = cmi;
669                                                 cmi->set_active (visible);
670                                         }
671                                 }
672
673                                 /* add the per-channel menu to the list of controllers, with the name of the controller */
674                                 ctl_items.push_back (MenuElem (string_compose ("<b>%1</b>: %2", ctl, midi_name (ctl)), *chn_menu));
675                                 dynamic_cast<Label*> (ctl_items.back().get_child())->set_use_markup (true);
676
677                         } else {
678
679                                 /* just one channel - create a single menu item for this ctl+channel combination*/
680
681                                 for (uint8_t chn = 0; chn < 16; chn++) {
682                                         if (selected_channels & (0x0001 << chn)) {
683
684                                                 Evoral::Parameter fully_qualified_param (MidiCCAutomation, chn, ctl);
685                                                 ctl_items.push_back (
686                                                         CheckMenuElem (
687                                                                 string_compose ("<b>%1</b>: %2 [%3]", ctl, midi_name (ctl), int (chn)),
688                                                                 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
689                                                                             fully_qualified_param)
690                                                                 )
691                                                         );
692                                                 dynamic_cast<Label*> (ctl_items.back().get_child())->set_use_markup (true);
693
694                                                 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
695                                                 bool visible = false;
696
697                                                 if (track) {
698                                                         if (track->marked_for_display()) {
699                                                                 visible = true;
700                                                         }
701                                                 }
702
703                                                 CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&ctl_items.back());
704                                                 _controller_menu_map[fully_qualified_param] = cmi;
705                                                 cmi->set_active (visible);
706
707                                                 /* one channel only */
708                                                 break;
709                                         }
710                                 }
711                         }
712                 }
713
714                 /* add the menu for this block of controllers to the overall controller menu */
715
716                 items.push_back (MenuElem (string_compose (_("Controllers %1-%2"), i, i+15), *ctl_menu));
717         }
718 }
719
720 Gtk::Menu*
721 MidiTimeAxisView::build_note_mode_menu()
722 {
723         using namespace Menu_Helpers;
724
725         Menu* mode_menu = manage (new Menu);
726         MenuList& items = mode_menu->items();
727         mode_menu->set_name ("ArdourContextMenu");
728
729         RadioMenuItem::Group mode_group;
730         items.push_back (RadioMenuElem (mode_group, _("Sustained"),
731                                 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_mode), Sustained)));
732         _note_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
733         _note_mode_item->set_active(_note_mode == Sustained);
734
735         items.push_back (RadioMenuElem (mode_group, _("Percussive"),
736                                 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_mode), Percussive)));
737         _percussion_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
738         _percussion_mode_item->set_active(_note_mode == Percussive);
739
740         return mode_menu;
741 }
742
743 Gtk::Menu*
744 MidiTimeAxisView::build_color_mode_menu()
745 {
746         using namespace Menu_Helpers;
747
748         Menu* mode_menu = manage (new Menu);
749         MenuList& items = mode_menu->items();
750         mode_menu->set_name ("ArdourContextMenu");
751
752         RadioMenuItem::Group mode_group;
753         items.push_back (RadioMenuElem (mode_group, _("Meter Colors"),
754                                         sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode),
755                                                     MeterColors, false, true)));
756         _meter_color_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
757         _meter_color_mode_item->set_active(_color_mode == MeterColors);
758
759         items.push_back (RadioMenuElem (mode_group, _("Channel Colors"),
760                                         sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode),
761                                                     ChannelColors, false, true)));
762         _channel_color_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
763         _channel_color_mode_item->set_active(_color_mode == ChannelColors);
764
765         items.push_back (RadioMenuElem (mode_group, _("Track Color"),
766                                         sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode),
767                                                     TrackColor, false, true)));
768         _channel_color_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
769         _channel_color_mode_item->set_active(_color_mode == TrackColor);
770
771         return mode_menu;
772 }
773
774 void
775 MidiTimeAxisView::set_note_mode(NoteMode mode)
776 {
777         if (_note_mode != mode || midi_track()->note_mode() != mode) {
778                 _note_mode = mode;
779                 midi_track()->set_note_mode(mode);
780                 set_gui_property ("note-mode", enum_2_string(_note_mode));
781                 _view->redisplay_track();
782         }
783 }
784
785 void
786 MidiTimeAxisView::set_color_mode (ColorMode mode, bool force, bool redisplay)
787 {
788         if (_color_mode == mode && !force) {
789                 return;
790         }
791
792         if (mode == ChannelColors) {
793                 _channel_selector.set_channel_colors(CanvasNoteEvent::midi_channel_colors);
794         } else {
795                 _channel_selector.set_default_channel_color();
796         }
797
798         _color_mode = mode;
799         set_gui_property ("color-mode", enum_2_string(_color_mode));
800         if (redisplay) {
801                 _view->redisplay_track();
802         }
803 }
804
805 void
806 MidiTimeAxisView::set_note_range(MidiStreamView::VisibleNoteRange range)
807 {
808         if (!_ignore_signals)
809                 midi_view()->set_note_range(range);
810 }
811
812
813 void
814 MidiTimeAxisView::update_range()
815 {
816         MidiGhostRegion* mgr;
817
818         for(list<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
819                 if ((mgr = dynamic_cast<MidiGhostRegion*>(*i)) != 0) {
820                         mgr->update_range();
821                 }
822         }
823 }
824
825 void
826 MidiTimeAxisView::show_all_automation (bool apply_to_selection)
827 {
828         if (apply_to_selection) {
829                 _editor.get_selection().tracks.foreach_midi_time_axis (boost::bind (&MidiTimeAxisView::show_all_automation, _1, false));
830         } else {
831                 if (midi_track()) {
832                         const set<Evoral::Parameter> params = midi_track()->midi_playlist()->contained_automation();
833
834                         for (set<Evoral::Parameter>::const_iterator i = params.begin(); i != params.end(); ++i) {
835                                 create_automation_child(*i, true);
836                         }
837                 }
838
839                 RouteTimeAxisView::show_all_automation ();
840         }
841 }
842
843 void
844 MidiTimeAxisView::show_existing_automation (bool apply_to_selection)
845 {
846         if (apply_to_selection) {
847                 _editor.get_selection().tracks.foreach_midi_time_axis (boost::bind (&MidiTimeAxisView::show_existing_automation, _1, false));
848         } else {
849                 if (midi_track()) {
850                         const set<Evoral::Parameter> params = midi_track()->midi_playlist()->contained_automation();
851
852                         for (set<Evoral::Parameter>::const_iterator i = params.begin(); i != params.end(); ++i) {
853                                 create_automation_child (*i, true);
854                         }
855                 }
856
857                 RouteTimeAxisView::show_existing_automation ();
858         }
859 }
860
861 /** Create an automation track for the given parameter (pitch bend, channel pressure).
862  */
863 void
864 MidiTimeAxisView::create_automation_child (const Evoral::Parameter& param, bool show)
865 {
866         if (param.type() == NullAutomation) {
867                 return;
868         }
869
870         AutomationTracks::iterator existing = _automation_tracks.find (param);
871
872         if (existing != _automation_tracks.end()) {
873
874                 /* automation track created because we had existing data for
875                  * the processor, but visibility may need to be controlled
876                  * since it will have been set visible by default.
877                  */
878
879                 if (existing->second->set_marked_for_display (show) && !no_redraw) {
880                         request_redraw ();
881                 }
882
883                 return;
884         }
885
886         boost::shared_ptr<AutomationTimeAxisView> track;
887
888         switch (param.type()) {
889
890         case GainAutomation:
891                 create_gain_automation_child (param, show);
892                 break;
893
894         case PluginAutomation:
895                 /* handled elsewhere */
896                 break;
897
898         case MidiCCAutomation:
899         case MidiPgmChangeAutomation:
900         case MidiPitchBenderAutomation:
901         case MidiChannelPressureAutomation:
902         case MidiSystemExclusiveAutomation:
903                 /* These controllers are region "automation" - they are owned
904                  * by regions (and their MidiModels), not by the track. As a
905                  * result we do not create an AutomationList/Line for the track
906                  * ... except here we are doing something!! XXX 
907                  */
908
909                 track.reset (new AutomationTimeAxisView (
910                                      _session,
911                                      _route,
912                                      boost::shared_ptr<Automatable> (),
913                                      boost::shared_ptr<AutomationControl> (),
914                                      param,
915                                      _editor,
916                                      *this,
917                                      true,
918                                      parent_canvas,
919                                      _route->describe_parameter(param)
920                                      ));
921
922                 if (_view) {
923                         _view->foreach_regionview (sigc::mem_fun (*track.get(), &TimeAxisView::add_ghost));
924                 }
925
926                 add_automation_child (param, track, show);
927                 break;
928
929         default:
930                 error << "MidiTimeAxisView: unknown automation child " << EventTypeMap::instance().to_symbol(param) << endmsg;
931         }
932 }
933
934 void
935 MidiTimeAxisView::route_active_changed ()
936 {
937         RouteUI::route_active_changed ();
938
939         if (is_track()) {
940                 if (_route->active()) {
941                         controls_ebox.set_name ("MidiTrackControlsBaseUnselected");
942                         controls_base_selected_name = "MidiTrackControlsBaseSelected";
943                         controls_base_unselected_name = "MidiTrackControlsBaseUnselected";
944                 } else {
945                         controls_ebox.set_name ("MidiTrackControlsBaseInactiveUnselected");
946                         controls_base_selected_name = "MidiTrackControlsBaseInactiveSelected";
947                         controls_base_unselected_name = "MidiTrackControlsBaseInactiveUnselected";
948                 }
949         } else {
950
951                 throw; // wha?
952
953                 if (_route->active()) {
954                         controls_ebox.set_name ("BusControlsBaseUnselected");
955                         controls_base_selected_name = "BusControlsBaseSelected";
956                         controls_base_unselected_name = "BusControlsBaseUnselected";
957                 } else {
958                         controls_ebox.set_name ("BusControlsBaseInactiveUnselected");
959                         controls_base_selected_name = "BusControlsBaseInactiveSelected";
960                         controls_base_unselected_name = "BusControlsBaseInactiveUnselected";
961                 }
962         }
963 }
964
965 void
966 MidiTimeAxisView::set_note_selection (uint8_t note)
967 {
968         if (!_editor.internal_editing()) {
969                 return;
970         }
971
972         uint16_t chn_mask = _channel_selector.get_selected_channels();
973
974         if (_view->num_selected_regionviews() == 0) {
975                 _view->foreach_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_selection_region_view), note, chn_mask));
976         } else {
977                 _view->foreach_selected_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_selection_region_view), note, chn_mask));
978         }
979 }
980
981 void
982 MidiTimeAxisView::add_note_selection (uint8_t note)
983 {
984         if (!_editor.internal_editing()) {
985                 return;
986         }
987
988         uint16_t chn_mask = _channel_selector.get_selected_channels();
989
990         if (_view->num_selected_regionviews() == 0) {
991                 _view->foreach_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection_region_view), note, chn_mask));
992         } else {
993                 _view->foreach_selected_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection_region_view), note, chn_mask));
994         }
995 }
996
997 void
998 MidiTimeAxisView::extend_note_selection (uint8_t note)
999 {
1000         if (!_editor.internal_editing()) {
1001                 return;
1002         }
1003
1004         uint16_t chn_mask = _channel_selector.get_selected_channels();
1005
1006         if (_view->num_selected_regionviews() == 0) {
1007                 _view->foreach_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::extend_note_selection_region_view), note, chn_mask));
1008         } else {
1009                 _view->foreach_selected_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::extend_note_selection_region_view), note, chn_mask));
1010         }
1011 }
1012
1013 void
1014 MidiTimeAxisView::toggle_note_selection (uint8_t note)
1015 {
1016         if (!_editor.internal_editing()) {
1017                 return;
1018         }
1019
1020         uint16_t chn_mask = _channel_selector.get_selected_channels();
1021
1022         if (_view->num_selected_regionviews() == 0) {
1023                 _view->foreach_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection_region_view), note, chn_mask));
1024         } else {
1025                 _view->foreach_selected_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection_region_view), note, chn_mask));
1026         }
1027 }
1028
1029 void
1030 MidiTimeAxisView::set_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1031 {
1032         dynamic_cast<MidiRegionView*>(rv)->select_matching_notes (note, chn_mask, false, false);
1033 }
1034
1035 void
1036 MidiTimeAxisView::add_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1037 {
1038         dynamic_cast<MidiRegionView*>(rv)->select_matching_notes (note, chn_mask, true, false);
1039 }
1040
1041 void
1042 MidiTimeAxisView::extend_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1043 {
1044         dynamic_cast<MidiRegionView*>(rv)->select_matching_notes (note, chn_mask, true, true);
1045 }
1046
1047 void
1048 MidiTimeAxisView::toggle_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1049 {
1050         dynamic_cast<MidiRegionView*>(rv)->toggle_matching_notes (note, chn_mask);
1051 }
1052
1053 void
1054 MidiTimeAxisView::set_channel_mode (ChannelMode, uint16_t)
1055 {
1056         /* hide all automation tracks that use the wrong channel(s) and show all those that use
1057            the right ones.
1058         */
1059
1060         uint16_t selected_channels = _channel_selector.get_selected_channels();
1061         bool changed = false;
1062
1063         no_redraw = true;
1064
1065         for (uint32_t ctl = 0; ctl < 127; ++ctl) {
1066
1067                 for (uint32_t chn = 0; chn < 16; ++chn) {
1068                         Evoral::Parameter fully_qualified_param (MidiCCAutomation, chn, ctl);
1069                         boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
1070
1071                         if (!track) {
1072                                 continue;
1073                         }
1074
1075                         if ((selected_channels & (0x0001 << chn)) == 0) {
1076                                 /* channel not in use. hiding it will trigger RouteTimeAxisView::automation_track_hidden()
1077                                    which will cause a redraw. We don't want one per channel, so block that with no_redraw.
1078                                  */
1079                                 changed = track->set_marked_for_display (false) || changed;
1080                         } else {
1081                                 changed = track->set_marked_for_display (true) || changed;
1082                         }
1083                 }
1084         }
1085
1086         no_redraw = false;
1087
1088         /* TODO: Bender, Pressure */
1089
1090         /* invalidate the controller menu, so that we rebuild it next time */
1091         _controller_menu_map.clear ();
1092         delete controller_menu;
1093         controller_menu = 0;
1094
1095         if (changed) {
1096                 request_redraw ();
1097         }
1098 }
1099
1100 Gtk::CheckMenuItem*
1101 MidiTimeAxisView::automation_child_menu_item (Evoral::Parameter param)
1102 {
1103         Gtk::CheckMenuItem* m = RouteTimeAxisView::automation_child_menu_item (param);
1104         if (m) {
1105                 return m;
1106         }
1107
1108         ParameterMenuMap::iterator i = _controller_menu_map.find (param);
1109         if (i != _controller_menu_map.end()) {
1110                 return i->second;
1111         }
1112
1113         i = _channel_command_menu_map.find (param);
1114         if (i != _channel_command_menu_map.end()) {
1115                 return i->second;
1116         }
1117
1118         return 0;
1119 }
1120
1121 boost::shared_ptr<MidiRegion>
1122 MidiTimeAxisView::add_region (framepos_t pos, framecnt_t length, bool commit)
1123 {
1124         Editor* real_editor = dynamic_cast<Editor*> (&_editor);
1125
1126         real_editor->begin_reversible_command (Operations::create_region);
1127         playlist()->clear_changes ();
1128
1129         real_editor->snap_to (pos, 0);
1130
1131         boost::shared_ptr<Source> src = _session->create_midi_source_for_session (view()->trackview().track().get(),
1132                                                                                   view()->trackview().track()->name());
1133         PropertyList plist;
1134
1135         plist.add (ARDOUR::Properties::start, 0);
1136         plist.add (ARDOUR::Properties::length, length);
1137         plist.add (ARDOUR::Properties::name, PBD::basename_nosuffix(src->name()));
1138
1139         boost::shared_ptr<Region> region = (RegionFactory::create (src, plist));
1140
1141         playlist()->add_region (region, pos);
1142         _session->add_command (new StatefulDiffCommand (playlist()));
1143
1144         if (commit) {
1145                 real_editor->commit_reversible_command ();
1146         }
1147
1148         return boost::dynamic_pointer_cast<MidiRegion>(region);
1149 }
1150
1151 void
1152 MidiTimeAxisView::ensure_step_editor ()
1153 {
1154         if (!_step_editor) {
1155                 _step_editor = new StepEditor (_editor, midi_track(), *this);
1156         }
1157 }
1158
1159 void
1160 MidiTimeAxisView::start_step_editing ()
1161 {
1162         ensure_step_editor ();
1163         _step_editor->start_step_editing ();
1164
1165 }
1166 void
1167 MidiTimeAxisView::stop_step_editing ()
1168 {
1169         if (_step_editor) {
1170                 _step_editor->stop_step_editing ();
1171         }
1172 }
1173
1174
1175 /** @return channel (counted from 0) to add an event to, based on the current setting
1176  *  of the channel selector.
1177  */
1178 uint8_t
1179 MidiTimeAxisView::get_channel_for_add () const
1180 {
1181         uint16_t const chn_mask = _channel_selector.get_selected_channels ();
1182         int chn_cnt = 0;
1183         uint8_t channel = 0;
1184
1185         /* pick the highest selected channel, unless all channels are selected,
1186            which is interpreted to mean channel 1 (zero)
1187         */
1188
1189         for (uint16_t i = 0; i < 16; ++i) {
1190                 if (chn_mask & (1<<i)) {
1191                         channel = i;
1192                         chn_cnt++;
1193                 }
1194         }
1195
1196         if (chn_cnt == 16) {
1197                 channel = 0;
1198         }
1199
1200         return channel;
1201 }
1202
1203 void
1204 MidiTimeAxisView::note_range_changed ()
1205 {
1206         set_gui_property ("note-range-min", (int) midi_view()->lowest_note ());
1207         set_gui_property ("note-range-max", (int) midi_view()->highest_note ());
1208 }
1209
1210 string
1211 MidiTimeAxisView::midi_patch_model () const
1212 {
1213         return _model_selector.get_active_text ();
1214 }
1215
1216 string
1217 MidiTimeAxisView::midi_patch_custom_device_mode () const
1218 {
1219         return _custom_device_mode_selector.get_active_text ();
1220 }
1221
1222 string
1223 MidiTimeAxisView::get_patch_name (uint16_t bank, uint8_t program, uint8_t channel) const
1224 {
1225         MIDI::Name::PatchPrimaryKey patch_key (bank, program);
1226         
1227         boost::shared_ptr<MIDI::Name::Patch> patch =
1228                 MIDI::Name::MidiPatchManager::instance().find_patch (midi_patch_model(), midi_patch_custom_device_mode(), channel, patch_key);
1229
1230         if (patch) {
1231                 return patch->name();
1232         } else {
1233                 /* program and bank numbers are zero-based: convert to one-based: MIDI_BP_ZERO */
1234
1235 #define MIDI_BP_ZERO ((Config->get_first_midi_bank_is_zero())?0:1)
1236
1237                 return string_compose ("%1 %2",program + MIDI_BP_ZERO , bank + MIDI_BP_ZERO);
1238         }
1239 }