RouteDialog: Move built-in types into template list experiment
[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 <gtkmm/separator.h>
29 #include <gtkmm/stock.h>
30
31 #include "pbd/error.h"
32 #include "pbd/ffs.h"
33 #include "pbd/stl_delete.h"
34 #include "pbd/whitespace.h"
35 #include "pbd/basename.h"
36 #include "pbd/enumwriter.h"
37 #include "pbd/memento_command.h"
38 #include "pbd/stateful_diff_command.h"
39
40 #include "gtkmm2ext/gtk_ui.h"
41 #include "gtkmm2ext/utils.h"
42
43 #include "widgets/tooltips.h"
44
45 #include "ardour/event_type_map.h"
46 #include "ardour/midi_patch_manager.h"
47 #include "ardour/midi_playlist.h"
48 #include "ardour/midi_region.h"
49 #include "ardour/midi_source.h"
50 #include "ardour/midi_track.h"
51 #include "ardour/operations.h"
52 #include "ardour/pannable.h"
53 #include "ardour/panner.h"
54 #include "ardour/panner_shell.h"
55 #include "ardour/playlist.h"
56 #include "ardour/plugin_insert.h"
57 #include "ardour/profile.h"
58 #include "ardour/region.h"
59 #include "ardour/region_factory.h"
60 #include "ardour/route.h"
61 #include "ardour/session.h"
62 #include "ardour/session_object.h"
63 #include "ardour/source.h"
64 #include "ardour/track.h"
65 #include "ardour/types.h"
66
67 #include "automation_line.h"
68 #include "automation_time_axis.h"
69 #include "editor.h"
70 #include "enums.h"
71 #include "ghostregion.h"
72 #include "gui_thread.h"
73 #include "keyboard.h"
74 #include "midi_channel_selector.h"
75 #include "midi_scroomer.h"
76 #include "midi_streamview.h"
77 #include "midi_region_view.h"
78 #include "midi_time_axis.h"
79 #include "patch_change_dialog.h"
80 #include "piano_roll_header.h"
81 #include "playlist_selector.h"
82 #include "plugin_selector.h"
83 #include "plugin_ui.h"
84 #include "point_selection.h"
85 #include "region_view.h"
86 #include "rgb_macros.h"
87 #include "selection.h"
88 #include "step_editor.h"
89 #include "utils.h"
90 #include "note_base.h"
91
92 #include "ardour/midi_track.h"
93
94 #include "pbd/i18n.h"
95
96 using namespace ARDOUR;
97 using namespace PBD;
98 using namespace Gtk;
99 using namespace Gtkmm2ext;
100 using namespace Editing;
101 using namespace std;
102
103 // Minimum height at which a control is displayed
104 static const uint32_t MIDI_CONTROLS_BOX_MIN_HEIGHT = 160;
105 static const uint32_t KEYBOARD_MIN_HEIGHT = 130;
106
107 MidiTimeAxisView::MidiTimeAxisView (PublicEditor& ed, Session* sess, ArdourCanvas::Canvas& canvas)
108         : SessionHandlePtr (sess)
109         , RouteTimeAxisView (ed, sess, canvas)
110         , _ignore_signals(false)
111         , _range_scroomer(0)
112         , _piano_roll_header(0)
113         , _note_mode(Sustained)
114         , _note_mode_item(0)
115         , _percussion_mode_item(0)
116         , _color_mode(MeterColors)
117         , _meter_color_mode_item(0)
118         , _channel_color_mode_item(0)
119         , _track_color_mode_item(0)
120         , _channel_selector (0)
121         , _step_edit_item (0)
122         , controller_menu (0)
123         , poly_pressure_menu (0)
124         , _step_editor (0)
125 {
126         _midnam_model_selector.disable_scrolling();
127         _midnam_custom_device_mode_selector.disable_scrolling();
128 }
129
130 void
131 MidiTimeAxisView::set_note_highlight (uint8_t note) {
132         _piano_roll_header->set_note_highlight (note);
133 }
134
135 void
136 MidiTimeAxisView::set_route (boost::shared_ptr<Route> rt)
137 {
138         _route = rt;
139
140         _view = new MidiStreamView (*this);
141
142         if (is_track ()) {
143                 _piano_roll_header = new PianoRollHeader(*midi_view());
144                 _range_scroomer = new MidiScroomer(midi_view()->note_range_adjustment);
145                 _range_scroomer->DoubleClicked.connect (
146                         sigc::bind (sigc::mem_fun(*this, &MidiTimeAxisView::set_note_range),
147                                     MidiStreamView::ContentsRange, false));
148         }
149
150         /* This next call will result in our height being set up, so it must come after
151            the creation of the piano roll / range scroomer as their visibility is set up
152            when our height is.
153         */
154         RouteTimeAxisView::set_route (rt);
155
156         _view->apply_color (ARDOUR_UI_UTILS::gdk_color_to_rgba (color()), StreamView::RegionColor);
157
158         subplugin_menu.set_name ("ArdourContextMenu");
159
160         if (!gui_property ("note-range-min").empty ()) {
161                 midi_view()->apply_note_range (atoi (gui_property ("note-range-min").c_str()),
162                                                atoi (gui_property ("note-range-max").c_str()),
163                                                true);
164         }
165
166         _view->ContentsHeightChanged.connect (
167                 sigc::mem_fun (*this, &MidiTimeAxisView::contents_height_changed));
168
169         ignore_toggle = false;
170
171         if (is_midi_track()) {
172                 _note_mode = midi_track()->note_mode();
173         }
174
175         /* if set_state above didn't create a gain automation child, we need to make one */
176         if (automation_child (GainAutomation) == 0) {
177                 create_automation_child (GainAutomation, false);
178         }
179
180         /* if set_state above didn't create a mute automation child, we need to make one */
181         if (automation_child (MuteAutomation) == 0) {
182                 create_automation_child (MuteAutomation, false);
183         }
184
185         if (_route->panner_shell()) {
186                 _route->panner_shell()->Changed.connect (*this, invalidator (*this), boost::bind (&MidiTimeAxisView::ensure_pan_views, this, false), gui_context());
187         }
188
189         /* map current state of the route */
190         ensure_pan_views (false);
191         update_control_names();
192         processors_changed (RouteProcessorChange ());
193
194         _route->processors_changed.connect (*this, invalidator (*this),
195                                             boost::bind (&MidiTimeAxisView::processors_changed, this, _1),
196                                             gui_context());
197
198         if (is_track()) {
199                 _piano_roll_header->SetNoteSelection.connect (
200                         sigc::mem_fun (*this, &MidiTimeAxisView::set_note_selection));
201                 _piano_roll_header->AddNoteSelection.connect (
202                         sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection));
203                 _piano_roll_header->ExtendNoteSelection.connect (
204                         sigc::mem_fun (*this, &MidiTimeAxisView::extend_note_selection));
205                 _piano_roll_header->ToggleNoteSelection.connect (
206                         sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection));
207
208                 /* Update StreamView during scroomer drags.*/
209
210                 _range_scroomer->DragStarting.connect (
211                         sigc::mem_fun (*this, &MidiTimeAxisView::start_scroomer_update));
212                 _range_scroomer->DragFinishing.connect (
213                         sigc::mem_fun (*this, &MidiTimeAxisView::stop_scroomer_update));
214
215                 /* Put the scroomer and the keyboard in a VBox with a padding
216                    label so that they can be reduced in height for stacked-view
217                    tracks.
218                 */
219
220                 HSeparator* separator = manage (new HSeparator());
221                 separator->set_name("TrackSeparator");
222                 separator->set_size_request(-1, 1);
223                 separator->show();
224
225                 VBox* v = manage (new VBox);
226                 HBox* h = manage (new HBox);
227                 h->pack_end (*_piano_roll_header);
228                 h->pack_end (*_range_scroomer);
229                 v->pack_start (*separator, false, false);
230                 v->pack_start (*h, true, true);
231                 v->show ();
232                 h->show ();
233                 top_hbox.remove(scroomer_placeholder);
234                 time_axis_hbox.pack_end(*v, false, false, 0);
235                 midi_scroomer_size_group->add_widget (*v);
236
237                 midi_view()->NoteRangeChanged.connect (
238                         sigc::mem_fun(*this, &MidiTimeAxisView::update_range));
239
240                 /* ask for notifications of any new RegionViews */
241                 _view->RegionViewAdded.connect (
242                         sigc::mem_fun(*this, &MidiTimeAxisView::region_view_added));
243
244                 if (!_editor.have_idled()) {
245                         /* first idle will do what we need */
246                 } else {
247                         first_idle ();
248                 }
249         }
250
251         if (gui_property (X_("midnam-model-name")).empty()) {
252                 set_gui_property (X_("midnam-model-name"), "Generic");
253         }
254
255         if (gui_property (X_("midnam-custom-device-mode")).empty()) {
256                 boost::shared_ptr<MIDI::Name::MasterDeviceNames> device_names = get_device_names();
257                 if (device_names) {
258                         set_gui_property (X_("midnam-custom-device-mode"),
259                                           *device_names->custom_device_mode_names().begin());
260                 }
261         }
262
263         ArdourWidgets::set_tooltip (_midnam_model_selector, _("External MIDI Device"));
264         ArdourWidgets::set_tooltip (_midnam_custom_device_mode_selector, _("External Device Mode"));
265
266         _midi_controls_box.pack_start (_midnam_model_selector, false, false, 2);
267         _midi_controls_box.pack_start (_midnam_custom_device_mode_selector, false, false, 2);
268
269         _midi_controls_box.set_homogeneous(false);
270         _midi_controls_box.set_border_width (2);
271
272         MIDI::Name::MidiPatchManager::instance().PatchesChanged.connect (*this, invalidator (*this),
273                         boost::bind (&MidiTimeAxisView::setup_midnam_patches, this),
274                         gui_context());
275
276         setup_midnam_patches ();
277         update_patch_selector ();
278
279         model_changed (gui_property(X_("midnam-model-name")));
280         custom_device_mode_changed (gui_property(X_("midnam-custom-device-mode")));
281
282         controls_vbox.pack_start(_midi_controls_box, false, false);
283
284         const string color_mode = gui_property ("color-mode");
285         if (!color_mode.empty()) {
286                 _color_mode = ColorMode (string_2_enum(color_mode, _color_mode));
287                 if (_channel_selector && _color_mode == ChannelColors) {
288                         _channel_selector->set_channel_colors(NoteBase::midi_channel_colors);
289                 }
290         }
291
292         set_color_mode (_color_mode, true, false);
293
294         const string note_mode = gui_property ("note-mode");
295         if (!note_mode.empty()) {
296                 _note_mode = NoteMode (string_2_enum (note_mode, _note_mode));
297                 if (_percussion_mode_item) {
298                         _percussion_mode_item->set_active (_note_mode == Percussive);
299                 }
300         }
301
302         /* Look for any GUI object state nodes that represent automation children
303          * that should exist, and create the children.
304          */
305
306         const list<string> gui_ids = gui_object_state().all_ids ();
307         for (list<string>::const_iterator i = gui_ids.begin(); i != gui_ids.end(); ++i) {
308                 PBD::ID route_id;
309                 bool has_parameter;
310                 Evoral::Parameter parameter (0, 0, 0);
311
312                 bool const p = AutomationTimeAxisView::parse_state_id (
313                         *i, route_id, has_parameter, parameter);
314                 if (p && route_id == _route->id () && has_parameter) {
315                         const std::string& visible = gui_object_state().get_string (*i, X_("visible"));
316                         create_automation_child (parameter, string_to<bool> (visible));
317                 }
318         }
319 }
320
321 void
322 MidiTimeAxisView::processors_changed (RouteProcessorChange c)
323 {
324         RouteTimeAxisView::processors_changed (c);
325         update_patch_selector ();
326 }
327
328 void
329 MidiTimeAxisView::first_idle ()
330 {
331         if (is_track ()) {
332                 _view->attach ();
333         }
334 }
335
336 MidiTimeAxisView::~MidiTimeAxisView ()
337 {
338         delete _channel_selector;
339
340         delete _piano_roll_header;
341         _piano_roll_header = 0;
342
343         delete _range_scroomer;
344         _range_scroomer = 0;
345
346         delete controller_menu;
347         delete _step_editor;
348 }
349
350 void
351 MidiTimeAxisView::check_step_edit ()
352 {
353         ensure_step_editor ();
354         _step_editor->check_step_edit ();
355 }
356
357 void
358 MidiTimeAxisView::setup_midnam_patches ()
359 {
360         typedef MIDI::Name::MidiPatchManager PatchManager;
361         PatchManager& patch_manager = PatchManager::instance();
362
363         _midnam_model_selector.clear_items ();
364         for (PatchManager::DeviceNamesByMaker::const_iterator m = patch_manager.devices_by_manufacturer().begin();
365                         m != patch_manager.devices_by_manufacturer().end(); ++m) {
366                 Menu*                   menu  = Gtk::manage(new Menu);
367                 Menu_Helpers::MenuList& items = menu->items();
368
369                 // Build manufacturer submenu
370                 for (MIDI::Name::MIDINameDocument::MasterDeviceNamesList::const_iterator n = m->second.begin();
371                                 n != m->second.end(); ++n) {
372                         Menu_Helpers::MenuElem elem = Gtk::Menu_Helpers::MenuElem(
373                                         n->first.c_str(),
374                                         sigc::bind(sigc::mem_fun(*this, &MidiTimeAxisView::model_changed),
375                                                 n->first.c_str()));
376
377                         items.push_back(elem);
378                 }
379
380                 // Add manufacturer submenu to selector
381                 _midnam_model_selector.AddMenuElem(Menu_Helpers::MenuElem(m->first, *menu));
382         }
383
384         if (!get_device_names()) {
385                 model_changed ("Generic");
386         }
387 }
388
389 void
390 MidiTimeAxisView::drop_instrument_ref ()
391 {
392         midnam_connection.drop_connections ();
393 }
394 void
395 MidiTimeAxisView::start_scroomer_update ()
396 {
397         _note_range_changed_connection.disconnect();
398         _note_range_changed_connection = midi_view()->NoteRangeChanged.connect (
399                 sigc::mem_fun (*this, &MidiTimeAxisView::note_range_changed));
400 }
401 void
402 MidiTimeAxisView::stop_scroomer_update ()
403 {
404         _note_range_changed_connection.disconnect();
405 }
406
407 void
408 MidiTimeAxisView::update_patch_selector ()
409 {
410         typedef MIDI::Name::MidiPatchManager PatchManager;
411         PatchManager& patch_manager = PatchManager::instance();
412
413         bool pluginprovided = false;
414         if (_route) {
415                 boost::shared_ptr<Processor> the_instrument (_route->the_instrument());
416                 boost::shared_ptr<PluginInsert> pi = boost::dynamic_pointer_cast<PluginInsert>(the_instrument);
417                 if (pi && pi->plugin ()->has_midnam ()) {
418                         midnam_connection.drop_connections ();
419                         the_instrument->DropReferences.connect (midnam_connection, invalidator (*this),
420                                         boost::bind (&MidiTimeAxisView::drop_instrument_ref, this),
421                                         gui_context());
422                         pi->plugin()->UpdateMidnam.connect (midnam_connection, invalidator (*this),
423                                         boost::bind (&Plugin::read_midnam, pi->plugin ()),
424                                         gui_context());
425
426                         pluginprovided = true;
427                         std::string model_name = pi->plugin ()->midnam_model ();
428                         if (gui_property (X_("midnam-model-name")) != model_name) {
429                                 model_changed (model_name);
430                         }
431                 }
432         }
433
434         if (patch_manager.all_models().empty() || pluginprovided) {
435                 _midnam_model_selector.hide ();
436                 _midnam_custom_device_mode_selector.hide ();
437         } else {
438                 _midnam_model_selector.show ();
439                 _midnam_custom_device_mode_selector.show ();
440         }
441 }
442
443 void
444 MidiTimeAxisView::model_changed(const std::string& model)
445 {
446         set_gui_property (X_("midnam-model-name"), model);
447
448         const std::list<std::string> device_modes = MIDI::Name::MidiPatchManager::instance()
449                 .custom_device_mode_names_by_model(model);
450
451         _midnam_model_selector.set_text(model);
452         _midnam_custom_device_mode_selector.clear_items();
453
454         for (std::list<std::string>::const_iterator i = device_modes.begin();
455              i != device_modes.end(); ++i) {
456                 _midnam_custom_device_mode_selector.AddMenuElem(
457                         Gtk::Menu_Helpers::MenuElem(
458                                 *i, sigc::bind(sigc::mem_fun(*this, &MidiTimeAxisView::custom_device_mode_changed),
459                                                *i)));
460         }
461
462         if (!device_modes.empty()) {
463                 custom_device_mode_changed(device_modes.front());
464         }
465
466         if (device_modes.size() > 1) {
467                 _midnam_custom_device_mode_selector.show();
468         } else {
469                 _midnam_custom_device_mode_selector.hide();
470         }
471
472         // now this is a real bad hack
473         if (device_modes.size() > 0) {
474                 _route->instrument_info().set_external_instrument (model, device_modes.front());
475         } else {
476                 _route->instrument_info().set_external_instrument (model, "");
477         }
478
479         // Rebuild controller menu
480         _controller_menu_map.clear ();
481         delete controller_menu;
482         controller_menu = 0;
483         build_automation_action_menu(false);
484 }
485
486 void
487 MidiTimeAxisView::custom_device_mode_changed(const std::string& mode)
488 {
489         const std::string model = gui_property (X_("midnam-model-name"));
490
491         set_gui_property (X_("midnam-custom-device-mode"), mode);
492         _midnam_custom_device_mode_selector.set_text(mode);
493         _route->instrument_info().set_external_instrument (model, mode);
494 }
495
496 MidiStreamView*
497 MidiTimeAxisView::midi_view()
498 {
499         return dynamic_cast<MidiStreamView*>(_view);
500 }
501
502 void
503 MidiTimeAxisView::set_height (uint32_t h, TrackHeightMode m)
504 {
505         if (h >= MIDI_CONTROLS_BOX_MIN_HEIGHT) {
506                 _midi_controls_box.show ();
507         } else {
508                 _midi_controls_box.hide();
509         }
510
511         if (h >= KEYBOARD_MIN_HEIGHT) {
512                 if (is_track() && _range_scroomer) {
513                         _range_scroomer->show();
514                 }
515                 if (is_track() && _piano_roll_header) {
516                         _piano_roll_header->show();
517                 }
518         } else {
519                 if (is_track() && _range_scroomer) {
520                         _range_scroomer->hide();
521                 }
522                 if (is_track() && _piano_roll_header) {
523                         _piano_roll_header->hide();
524                 }
525         }
526
527         /* We need to do this after changing visibility of our stuff, as it will
528            eventually trigger a call to Editor::reset_controls_layout_width(),
529            which needs to know if we have just shown or hidden a scroomer /
530            piano roll.
531         */
532         RouteTimeAxisView::set_height (h, m);
533 }
534
535 void
536 MidiTimeAxisView::append_extra_display_menu_items ()
537 {
538         using namespace Menu_Helpers;
539
540         MenuList& items = display_menu->items();
541
542         // Note range
543         Menu *range_menu = manage(new Menu);
544         MenuList& range_items = range_menu->items();
545         range_menu->set_name ("ArdourContextMenu");
546
547         range_items.push_back (
548                 MenuElem (_("Show Full Range"),
549                           sigc::bind (sigc::mem_fun(*this, &MidiTimeAxisView::set_note_range),
550                                       MidiStreamView::FullRange, true)));
551
552         range_items.push_back (
553                 MenuElem (_("Fit Contents"),
554                           sigc::bind (sigc::mem_fun(*this, &MidiTimeAxisView::set_note_range),
555                                       MidiStreamView::ContentsRange, true)));
556
557         items.push_back (MenuElem (_("Note Range"), *range_menu));
558         items.push_back (MenuElem (_("Note Mode"), *build_note_mode_menu()));
559         items.push_back (MenuElem (_("Channel Selector"),
560                                    sigc::mem_fun(*this, &MidiTimeAxisView::toggle_channel_selector)));
561
562         items.push_back (MenuElem (_("Select Patch..."),
563                                 sigc::mem_fun(*this, &MidiTimeAxisView::send_patch_change)));
564
565         color_mode_menu = build_color_mode_menu();
566         if (color_mode_menu) {
567                 items.push_back (MenuElem (_("Color Mode"), *color_mode_menu));
568         }
569
570         items.push_back (SeparatorElem ());
571 }
572
573 void
574 MidiTimeAxisView::toggle_channel_selector ()
575 {
576         if (!_channel_selector) {
577                 _channel_selector = new MidiChannelSelectorWindow (midi_track());
578
579                 if (_color_mode == ChannelColors) {
580                         _channel_selector->set_channel_colors(NoteBase::midi_channel_colors);
581                 } else {
582                         _channel_selector->set_default_channel_color ();
583                 }
584
585                 _channel_selector->show_all ();
586         } else {
587                 _channel_selector->cycle_visibility ();
588         }
589 }
590
591 void
592 MidiTimeAxisView::build_automation_action_menu (bool for_selection)
593 {
594         using namespace Menu_Helpers;
595
596         /* If we have a controller menu, we need to detach it before
597            RouteTimeAxis::build_automation_action_menu destroys the
598            menu it is attached to.  Otherwise GTK destroys
599            controller_menu's gobj, meaning that it can't be reattached
600            below.  See bug #3134.
601         */
602
603         if (controller_menu) {
604                 detach_menu (*controller_menu);
605         }
606
607         _channel_command_menu_map.clear ();
608         RouteTimeAxisView::build_automation_action_menu (for_selection);
609
610         MenuList& automation_items = automation_action_menu->items();
611
612         uint16_t selected_channels = midi_track()->get_playback_channel_mask();
613
614         if (selected_channels !=  0) {
615
616                 automation_items.push_back (SeparatorElem());
617
618                 /* these 2 MIDI "command" types are semantically more like automation
619                    than note data, but they are not MIDI controllers. We give them
620                    special status in this menu, since they will not show up in the
621                    controller list and anyone who actually knows something about MIDI
622                    (!) would not expect to find them there.
623                 */
624
625                 add_channel_command_menu_item (
626                         automation_items, _("Bender"), MidiPitchBenderAutomation, 0);
627                 automation_items.back().set_sensitive (
628                         !for_selection || _editor.get_selection().tracks.size() == 1);
629                 add_channel_command_menu_item (
630                         automation_items, _("Pressure"), MidiChannelPressureAutomation, 0);
631                 automation_items.back().set_sensitive (
632                         !for_selection || _editor.get_selection().tracks.size() == 1);
633
634                 /* now all MIDI controllers. Always offer the possibility that we will
635                    rebuild the controllers menu since it might need to be updated after
636                    a channel mode change or other change. Also detach it first in case
637                    it has been used anywhere else.
638                 */
639
640                 build_controller_menu ();
641
642                 automation_items.push_back (MenuElem (_("Controllers"), *controller_menu));
643
644                 if (!poly_pressure_menu) {
645                         poly_pressure_menu = new Gtk::Menu;
646                 }
647
648                 automation_items.push_back (MenuElem  (_("Polyphonic Pressure"), *poly_pressure_menu));
649
650                 automation_items.back().set_sensitive (
651                         !for_selection || _editor.get_selection().tracks.size() == 1);
652         } else {
653                 automation_items.push_back (
654                         MenuElem (string_compose ("<i>%1</i>", _("No MIDI Channels selected"))));
655                 dynamic_cast<Label*> (automation_items.back().get_child())->set_use_markup (true);
656         }
657 }
658
659 void
660 MidiTimeAxisView::change_all_channel_tracks_visibility (bool yn, Evoral::Parameter param)
661 {
662         const uint16_t selected_channels = midi_track()->get_playback_channel_mask();
663
664         for (uint8_t chn = 0; chn < 16; chn++) {
665                 if (selected_channels & (0x0001 << chn)) {
666
667                         Evoral::Parameter fully_qualified_param (param.type(), chn, param.id());
668                         Gtk::CheckMenuItem* menu = automation_child_menu_item (fully_qualified_param);
669
670                         if (menu) {
671                                 menu->set_active (yn);
672                         }
673                 }
674         }
675 }
676
677 void
678 MidiTimeAxisView::add_channel_command_menu_item (Menu_Helpers::MenuList& items,
679                                                  const string&           label,
680                                                  AutomationType          auto_type,
681                                                  uint8_t                 cmd)
682 {
683         using namespace Menu_Helpers;
684
685         /* count the number of selected channels because we will build a different menu
686            structure if there is more than 1 selected.
687          */
688
689         const uint16_t selected_channels = midi_track()->get_playback_channel_mask();
690         int chn_cnt = 0;
691
692         for (uint8_t chn = 0; chn < 16; chn++) {
693                 if (selected_channels & (0x0001 << chn)) {
694                         if (++chn_cnt > 1) {
695                                 break;
696                         }
697                 }
698         }
699
700         if (chn_cnt > 1) {
701
702                 /* multiple channels - create a submenu, with 1 item per channel */
703
704                 Menu* chn_menu = manage (new Menu);
705                 MenuList& chn_items (chn_menu->items());
706                 Evoral::Parameter param_without_channel (auto_type, 0, cmd);
707
708                 /* add a couple of items to hide/show all of them */
709
710                 chn_items.push_back (
711                         MenuElem (_("Hide all channels"),
712                                   sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
713                                               false, param_without_channel)));
714                 chn_items.push_back (
715                         MenuElem (_("Show all channels"),
716                                   sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
717                                               true, param_without_channel)));
718
719                 for (uint8_t chn = 0; chn < 16; chn++) {
720                         if (selected_channels & (0x0001 << chn)) {
721
722                                 /* for each selected channel, add a menu item for this controller */
723
724                                 Evoral::Parameter fully_qualified_param (auto_type, chn, cmd);
725                                 chn_items.push_back (
726                                         CheckMenuElem (string_compose (_("Channel %1"), chn+1),
727                                                        sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
728                                                                    fully_qualified_param)));
729
730                                 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
731                                 bool visible = false;
732
733                                 if (track) {
734                                         if (track->marked_for_display()) {
735                                                 visible = true;
736                                         }
737                                 }
738
739                                 Gtk::CheckMenuItem* cmi = static_cast<Gtk::CheckMenuItem*>(&chn_items.back());
740                                 _channel_command_menu_map[fully_qualified_param] = cmi;
741                                 cmi->set_active (visible);
742                         }
743                 }
744
745                 /* now create an item in the parent menu that has the per-channel list as a submenu */
746
747                 items.push_back (MenuElem (label, *chn_menu));
748
749         } else {
750
751                 /* just one channel - create a single menu item for this command+channel combination*/
752
753                 for (uint8_t chn = 0; chn < 16; chn++) {
754                         if (selected_channels & (0x0001 << chn)) {
755
756                                 Evoral::Parameter fully_qualified_param (auto_type, chn, cmd);
757                                 items.push_back (
758                                         CheckMenuElem (label,
759                                                        sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
760                                                                    fully_qualified_param)));
761
762                                 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
763                                 bool visible = false;
764
765                                 if (track) {
766                                         if (track->marked_for_display()) {
767                                                 visible = true;
768                                         }
769                                 }
770
771                                 Gtk::CheckMenuItem* cmi = static_cast<Gtk::CheckMenuItem*>(&items.back());
772                                 _channel_command_menu_map[fully_qualified_param] = cmi;
773                                 cmi->set_active (visible);
774
775                                 /* one channel only */
776                                 break;
777                         }
778                 }
779         }
780 }
781
782 /** Add a single menu item for a controller on one channel. */
783 void
784 MidiTimeAxisView::add_single_channel_controller_item(Menu_Helpers::MenuList& ctl_items,
785                                                      int                     ctl,
786                                                      const std::string&      name)
787 {
788         using namespace Menu_Helpers;
789
790         const uint16_t selected_channels = midi_track()->get_playback_channel_mask();
791         for (uint8_t chn = 0; chn < 16; chn++) {
792                 if (selected_channels & (0x0001 << chn)) {
793
794                         Evoral::Parameter fully_qualified_param (MidiCCAutomation, chn, ctl);
795                         ctl_items.push_back (
796                                 CheckMenuElem (
797                                         string_compose ("<b>%1</b>: %2 [%3]", ctl, name, int (chn + 1)),
798                                         sigc::bind (
799                                                 sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
800                                                 fully_qualified_param)));
801                         dynamic_cast<Label*> (ctl_items.back().get_child())->set_use_markup (true);
802
803                         boost::shared_ptr<AutomationTimeAxisView> track = automation_child (
804                                 fully_qualified_param);
805
806                         bool visible = false;
807                         if (track) {
808                                 if (track->marked_for_display()) {
809                                         visible = true;
810                                 }
811                         }
812
813                         Gtk::CheckMenuItem* cmi = static_cast<Gtk::CheckMenuItem*>(&ctl_items.back());
814                         _controller_menu_map[fully_qualified_param] = cmi;
815                         cmi->set_active (visible);
816
817                         /* one channel only */
818                         break;
819                 }
820         }
821 }
822
823 /** Add a submenu with 1 item per channel for a controller on many channels. */
824 void
825 MidiTimeAxisView::add_multi_channel_controller_item(Menu_Helpers::MenuList& ctl_items,
826                                                     int                     ctl,
827                                                     const std::string&      name)
828 {
829         using namespace Menu_Helpers;
830
831         const uint16_t selected_channels = midi_track()->get_playback_channel_mask();
832
833         Menu* chn_menu = manage (new Menu);
834         MenuList& chn_items (chn_menu->items());
835
836         /* add a couple of items to hide/show this controller on all channels */
837
838         Evoral::Parameter param_without_channel (MidiCCAutomation, 0, ctl);
839         chn_items.push_back (
840                 MenuElem (_("Hide all channels"),
841                           sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
842                                       false, param_without_channel)));
843         chn_items.push_back (
844                 MenuElem (_("Show all channels"),
845                           sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
846                                       true, param_without_channel)));
847
848         for (uint8_t chn = 0; chn < 16; chn++) {
849                 if (selected_channels & (0x0001 << chn)) {
850
851                         /* for each selected channel, add a menu item for this controller */
852
853                         Evoral::Parameter fully_qualified_param (MidiCCAutomation, chn, ctl);
854                         chn_items.push_back (
855                                 CheckMenuElem (string_compose (_("Channel %1"), chn+1),
856                                                sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
857                                                            fully_qualified_param)));
858
859                         boost::shared_ptr<AutomationTimeAxisView> track = automation_child (
860                                 fully_qualified_param);
861                         bool visible = false;
862
863                         if (track) {
864                                 if (track->marked_for_display()) {
865                                         visible = true;
866                                 }
867                         }
868
869                         Gtk::CheckMenuItem* cmi = static_cast<Gtk::CheckMenuItem*>(&chn_items.back());
870                         _controller_menu_map[fully_qualified_param] = cmi;
871                         cmi->set_active (visible);
872                 }
873         }
874
875         /* add the per-channel menu to the list of controllers, with the name of the controller */
876         ctl_items.push_back (MenuElem (string_compose ("<b>%1</b>: %2", ctl, name),
877                                        *chn_menu));
878         dynamic_cast<Label*> (ctl_items.back().get_child())->set_use_markup (true);
879 }
880
881 boost::shared_ptr<MIDI::Name::CustomDeviceMode>
882 MidiTimeAxisView::get_device_mode()
883 {
884         using namespace MIDI::Name;
885
886         boost::shared_ptr<MasterDeviceNames> device_names = get_device_names();
887         if (!device_names) {
888                 return boost::shared_ptr<MIDI::Name::CustomDeviceMode>();
889         }
890
891         return device_names->custom_device_mode_by_name(
892                 gui_property (X_("midnam-custom-device-mode")));
893 }
894
895 boost::shared_ptr<MIDI::Name::MasterDeviceNames>
896 MidiTimeAxisView::get_device_names()
897 {
898         using namespace MIDI::Name;
899
900         const std::string model = gui_property (X_("midnam-model-name"));
901
902         boost::shared_ptr<MIDINameDocument> midnam = MidiPatchManager::instance()
903                 .document_by_model(model);
904         if (midnam) {
905                 return midnam->master_device_names(model);
906         } else {
907                 return boost::shared_ptr<MasterDeviceNames>();
908         }
909 }
910
911 void
912 MidiTimeAxisView::build_controller_menu ()
913 {
914         using namespace Menu_Helpers;
915
916         if (controller_menu) {
917                 /* it exists and has not been invalidated by a channel mode change */
918                 return;
919         }
920
921         controller_menu = new Menu; // explicitly managed by us
922         MenuList& items (controller_menu->items());
923
924         /* create several "top level" menu items for sets of controllers (16 at a
925            time), and populate each one with a submenu for each controller+channel
926            combination covering the currently selected channels for this track
927         */
928
929         const uint16_t selected_channels = midi_track()->get_playback_channel_mask();
930
931         /* count the number of selected channels because we will build a different menu
932            structure if there is more than 1 selected.
933         */
934
935         int chn_cnt = 0;
936         for (uint8_t chn = 0; chn < 16; chn++) {
937                 if (selected_channels & (0x0001 << chn)) {
938                         if (++chn_cnt > 1) {
939                                 break;
940                         }
941                 }
942         }
943
944         using namespace MIDI::Name;
945         boost::shared_ptr<MasterDeviceNames> device_names = get_device_names();
946
947         if (device_names && !device_names->controls().empty()) {
948                 /* Controllers names available in midnam file, generate fancy menu */
949                 unsigned n_items  = 0;
950                 unsigned n_groups = 0;
951
952                 /* TODO: This is not correct, should look up the currently applicable ControlNameList
953                    and only build a menu for that one. */
954                 for (MasterDeviceNames::ControlNameLists::const_iterator l = device_names->controls().begin();
955                      l != device_names->controls().end(); ++l) {
956                         boost::shared_ptr<ControlNameList> name_list = l->second;
957                         Menu*                              ctl_menu  = NULL;
958
959                         for (ControlNameList::Controls::const_iterator c = name_list->controls().begin();
960                              c != name_list->controls().end();) {
961                                 const uint16_t ctl = c->second->number();
962                                 if (ctl != MIDI_CTL_MSB_BANK && ctl != MIDI_CTL_LSB_BANK) {
963                                         /* Skip bank select controllers since they're handled specially */
964                                         if (n_items == 0) {
965                                                 /* Create a new submenu */
966                                                 ctl_menu = manage (new Menu);
967                                         }
968
969                                         MenuList& ctl_items (ctl_menu->items());
970                                         if (chn_cnt > 1) {
971                                                 add_multi_channel_controller_item(ctl_items, ctl, c->second->name());
972                                         } else {
973                                                 add_single_channel_controller_item(ctl_items, ctl, c->second->name());
974                                         }
975                                 }
976
977                                 ++c;
978                                 if (ctl_menu && (++n_items == 16 || c == name_list->controls().end())) {
979                                         /* Submenu has 16 items or we're done, add it to controller menu and reset */
980                                         items.push_back(
981                                                 MenuElem(string_compose(_("Controllers %1-%2"),
982                                                                         (16 * n_groups), (16 * n_groups) + n_items - 1),
983                                                          *ctl_menu));
984                                         ctl_menu = NULL;
985                                         n_items  = 0;
986                                         ++n_groups;
987                                 }
988                         }
989                 }
990         } else {
991                 /* No controllers names, generate generic numeric menu */
992                 for (int i = 0; i < 127; i += 16) {
993                         Menu*     ctl_menu = manage (new Menu);
994                         MenuList& ctl_items (ctl_menu->items());
995
996                         for (int ctl = i; ctl < i+16; ++ctl) {
997                                 if (ctl == MIDI_CTL_MSB_BANK || ctl == MIDI_CTL_LSB_BANK) {
998                                         /* Skip bank select controllers since they're handled specially */
999                                         continue;
1000                                 }
1001
1002                                 if (chn_cnt > 1) {
1003                                         add_multi_channel_controller_item(
1004                                                 ctl_items, ctl, string_compose(_("Controller %1"), ctl));
1005                                 } else {
1006                                         add_single_channel_controller_item(
1007                                                 ctl_items, ctl, string_compose(_("Controller %1"), ctl));
1008                                 }
1009                         }
1010
1011                         /* Add submenu for this block of controllers to controller menu */
1012                         items.push_back (
1013                                 MenuElem (string_compose (_("Controllers %1-%2"), i, i + 15),
1014                                           *ctl_menu));
1015                 }
1016         }
1017 }
1018
1019 Gtk::Menu*
1020 MidiTimeAxisView::build_note_mode_menu()
1021 {
1022         using namespace Menu_Helpers;
1023
1024         Menu* mode_menu = manage (new Menu);
1025         MenuList& items = mode_menu->items();
1026         mode_menu->set_name ("ArdourContextMenu");
1027
1028         RadioMenuItem::Group mode_group;
1029         items.push_back (
1030                 RadioMenuElem (mode_group,_("Sustained"),
1031                                sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_mode),
1032                                            Sustained, true)));
1033         _note_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
1034         _note_mode_item->set_active(_note_mode == Sustained);
1035
1036         items.push_back (
1037                 RadioMenuElem (mode_group, _("Percussive"),
1038                                sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_mode),
1039                                            Percussive, true)));
1040         _percussion_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
1041         _percussion_mode_item->set_active(_note_mode == Percussive);
1042
1043         return mode_menu;
1044 }
1045
1046 Gtk::Menu*
1047 MidiTimeAxisView::build_color_mode_menu()
1048 {
1049         using namespace Menu_Helpers;
1050
1051         Menu* mode_menu = manage (new Menu);
1052         MenuList& items = mode_menu->items();
1053         mode_menu->set_name ("ArdourContextMenu");
1054
1055         RadioMenuItem::Group mode_group;
1056         items.push_back (
1057                 RadioMenuElem (mode_group, _("Meter Colors"),
1058                                sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode),
1059                                            MeterColors, false, true, true)));
1060         _meter_color_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
1061         _meter_color_mode_item->set_active(_color_mode == MeterColors);
1062
1063         items.push_back (
1064                 RadioMenuElem (mode_group, _("Channel Colors"),
1065                                sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode),
1066                                            ChannelColors, false, true, true)));
1067         _channel_color_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
1068         _channel_color_mode_item->set_active(_color_mode == ChannelColors);
1069
1070         items.push_back (
1071                 RadioMenuElem (mode_group, _("Track Color"),
1072                                sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode),
1073                                            TrackColor, false, true, true)));
1074         _channel_color_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
1075         _channel_color_mode_item->set_active(_color_mode == TrackColor);
1076
1077         return mode_menu;
1078 }
1079
1080 void
1081 MidiTimeAxisView::send_patch_change ()
1082 {
1083         if (!_route) {
1084                 return;
1085         }
1086
1087         Evoral::PatchChange<Evoral::Beats> empty (Evoral::Beats(), 0, 0, 0);
1088         PatchChangeDialog d (0, 0, empty, _route->instrument_info(), Gtk::Stock::OK);
1089
1090         if (d.run() == RESPONSE_CANCEL) {
1091                 return;
1092         }
1093         Evoral::PatchChange<Evoral::Beats> p (d.patch ());
1094
1095         uint8_t chn = p.channel();
1096
1097         boost::shared_ptr<AutomationControl> bank_msb = _route->automation_control(Evoral::Parameter (MidiCCAutomation, chn, MIDI_CTL_MSB_BANK), true);
1098         boost::shared_ptr<AutomationControl> bank_lsb = _route->automation_control(Evoral::Parameter (MidiCCAutomation, chn, MIDI_CTL_LSB_BANK), true);
1099         boost::shared_ptr<AutomationControl> program = _route->automation_control(Evoral::Parameter (MidiPgmChangeAutomation, chn), true);
1100
1101         if (!bank_msb || ! bank_lsb || !program) {
1102                 return;
1103         }
1104
1105         bank_msb->set_value (p.bank_msb (), Controllable::NoGroup);
1106         bank_lsb->set_value (p.bank_lsb (), Controllable::NoGroup);
1107         program->set_value  (p.program () , Controllable::NoGroup);
1108 }
1109
1110 void
1111 MidiTimeAxisView::set_note_mode(NoteMode mode, bool apply_to_selection)
1112 {
1113         if (apply_to_selection) {
1114                 _editor.get_selection().tracks.foreach_midi_time_axis (
1115                         boost::bind (&MidiTimeAxisView::set_note_mode, _1, mode, false));
1116         } else {
1117                 if (_note_mode != mode || midi_track()->note_mode() != mode) {
1118                         _note_mode = mode;
1119                         midi_track()->set_note_mode(mode);
1120                         set_gui_property ("note-mode", enum_2_string(_note_mode));
1121                         _view->redisplay_track();
1122                 }
1123         }
1124 }
1125
1126 void
1127 MidiTimeAxisView::set_color_mode (ColorMode mode, bool force, bool redisplay, bool apply_to_selection)
1128 {
1129         if (apply_to_selection) {
1130                 _editor.get_selection().tracks.foreach_midi_time_axis (
1131                         boost::bind (&MidiTimeAxisView::set_color_mode, _1, mode, force, redisplay, false));
1132         } else {
1133                 if (_color_mode == mode && !force) {
1134                         return;
1135                 }
1136
1137                 if (_channel_selector) {
1138                         if (mode == ChannelColors) {
1139                                 _channel_selector->set_channel_colors(NoteBase::midi_channel_colors);
1140                         } else {
1141                                 _channel_selector->set_default_channel_color();
1142                         }
1143                 }
1144
1145                 _color_mode = mode;
1146                 set_gui_property ("color-mode", enum_2_string(_color_mode));
1147                 if (redisplay) {
1148                         _view->redisplay_track();
1149                 }
1150         }
1151 }
1152
1153 void
1154 MidiTimeAxisView::set_note_range (MidiStreamView::VisibleNoteRange range, bool apply_to_selection)
1155 {
1156         if (apply_to_selection) {
1157                 _editor.get_selection().tracks.foreach_midi_time_axis (
1158                         boost::bind (&MidiTimeAxisView::set_note_range, _1, range, false));
1159         } else {
1160                 if (!_ignore_signals) {
1161                         midi_view()->set_note_range(range);
1162                 }
1163         }
1164 }
1165
1166 void
1167 MidiTimeAxisView::update_range()
1168 {
1169 }
1170
1171 void
1172 MidiTimeAxisView::show_all_automation (bool apply_to_selection)
1173 {
1174         using namespace MIDI::Name;
1175
1176         if (apply_to_selection) {
1177                 _editor.get_selection().tracks.foreach_midi_time_axis (
1178                         boost::bind (&MidiTimeAxisView::show_all_automation, _1, false));
1179         } else {
1180                 if (midi_track()) {
1181                         // Show existing automation
1182                         const set<Evoral::Parameter> params = midi_track()->midi_playlist()->contained_automation();
1183
1184                         for (set<Evoral::Parameter>::const_iterator i = params.begin(); i != params.end(); ++i) {
1185                                 create_automation_child(*i, true);
1186                         }
1187
1188                         // Show automation for all controllers named in midnam file
1189                         boost::shared_ptr<MasterDeviceNames> device_names = get_device_names();
1190                         if (gui_property (X_("midnam-model-name")) != "Generic" &&
1191                              device_names && !device_names->controls().empty()) {
1192                                 const std::string device_mode       = gui_property (X_("midnam-custom-device-mode"));
1193                                 const uint16_t    selected_channels = midi_track()->get_playback_channel_mask();
1194                                 for (uint32_t chn = 0; chn < 16; ++chn) {
1195                                         if ((selected_channels & (0x0001 << chn)) == 0) {
1196                                                 // Channel not in use
1197                                                 continue;
1198                                         }
1199
1200                                         boost::shared_ptr<ChannelNameSet> chan_names = device_names->channel_name_set_by_channel(
1201                                                 device_mode, chn);
1202                                         if (!chan_names) {
1203                                                 continue;
1204                                         }
1205
1206                                         boost::shared_ptr<ControlNameList> control_names = device_names->control_name_list(
1207                                                 chan_names->control_list_name());
1208                                         if (!control_names) {
1209                                                 continue;
1210                                         }
1211
1212                                         for (ControlNameList::Controls::const_iterator c = control_names->controls().begin();
1213                                              c != control_names->controls().end();
1214                                              ++c) {
1215                                                 const uint16_t ctl = c->second->number();
1216                                                 if (ctl != MIDI_CTL_MSB_BANK && ctl != MIDI_CTL_LSB_BANK) {
1217                                                         /* Skip bank select controllers since they're handled specially */
1218                                                         const Evoral::Parameter param(MidiCCAutomation, chn, ctl);
1219                                                         create_automation_child(param, true);
1220                                                 }
1221                                         }
1222                                 }
1223                         }
1224                 }
1225
1226                 RouteTimeAxisView::show_all_automation ();
1227         }
1228 }
1229
1230 void
1231 MidiTimeAxisView::show_existing_automation (bool apply_to_selection)
1232 {
1233         if (apply_to_selection) {
1234                 _editor.get_selection().tracks.foreach_midi_time_axis (
1235                         boost::bind (&MidiTimeAxisView::show_existing_automation, _1, false));
1236         } else {
1237                 if (midi_track()) {
1238                         const set<Evoral::Parameter> params = midi_track()->midi_playlist()->contained_automation();
1239
1240                         for (set<Evoral::Parameter>::const_iterator i = params.begin(); i != params.end(); ++i) {
1241                                 create_automation_child (*i, true);
1242                         }
1243                 }
1244
1245                 RouteTimeAxisView::show_existing_automation ();
1246         }
1247 }
1248
1249 /** Create an automation track for the given parameter (pitch bend, channel pressure).
1250  */
1251 void
1252 MidiTimeAxisView::create_automation_child (const Evoral::Parameter& param, bool show)
1253 {
1254         if (param.type() == NullAutomation) {
1255                 return;
1256         }
1257
1258         AutomationTracks::iterator existing = _automation_tracks.find (param);
1259
1260         if (existing != _automation_tracks.end()) {
1261
1262                 /* automation track created because we had existing data for
1263                  * the processor, but visibility may need to be controlled
1264                  * since it will have been set visible by default.
1265                  */
1266
1267                 existing->second->set_marked_for_display (show);
1268
1269                 if (!no_redraw) {
1270                         request_redraw ();
1271                 }
1272
1273                 return;
1274         }
1275
1276         boost::shared_ptr<AutomationTimeAxisView> track;
1277         boost::shared_ptr<AutomationControl> control;
1278
1279
1280         switch (param.type()) {
1281
1282         case GainAutomation:
1283                 create_gain_automation_child (param, show);
1284                 break;
1285
1286         case MuteAutomation:
1287                 create_mute_automation_child (param, show);
1288                 break;
1289
1290         case PluginAutomation:
1291                 /* handled elsewhere */
1292                 break;
1293
1294         case MidiCCAutomation:
1295         case MidiPgmChangeAutomation:
1296         case MidiPitchBenderAutomation:
1297         case MidiChannelPressureAutomation:
1298         case MidiSystemExclusiveAutomation:
1299                 /* These controllers are region "automation" - they are owned
1300                  * by regions (and their MidiModels), not by the track. As a
1301                  * result there is no AutomationList/Line for the track, but we create
1302                  * a controller for the user to write immediate events, so the editor
1303                  * can act as a control surface for the present MIDI controllers.
1304                  *
1305                  * TODO: Record manipulation of the controller to regions?
1306                  */
1307
1308                 control = _route->automation_control(param, true);
1309                 track.reset (new AutomationTimeAxisView (
1310                                      _session,
1311                                      _route,
1312                                      control ? _route : boost::shared_ptr<Automatable> (),
1313                                      control,
1314                                      param,
1315                                      _editor,
1316                                      *this,
1317                                      true,
1318                                      parent_canvas,
1319                                      _route->describe_parameter(param)));
1320
1321                 if (_view) {
1322                         _view->foreach_regionview (
1323                                 sigc::mem_fun (*track.get(), &TimeAxisView::add_ghost));
1324                 }
1325
1326                 add_automation_child (param, track, show);
1327                 if (selected ()) {
1328                         reshow_selection (_editor.get_selection().time);
1329                 }
1330
1331                 break;
1332
1333         case PanWidthAutomation:
1334         case PanElevationAutomation:
1335         case PanAzimuthAutomation:
1336                 ensure_pan_views (show);
1337                 break;
1338
1339         default:
1340                 error << "MidiTimeAxisView: unknown automation child "
1341                       << EventTypeMap::instance().to_symbol(param) << endmsg;
1342         }
1343 }
1344
1345 void
1346 MidiTimeAxisView::route_active_changed ()
1347 {
1348         RouteUI::route_active_changed ();
1349         update_control_names();
1350 }
1351
1352 void
1353 MidiTimeAxisView::update_control_names ()
1354 {
1355         if (is_track()) {
1356                 if (_route->active()) {
1357                         controls_base_selected_name = "MidiTrackControlsBaseSelected";
1358                         controls_base_unselected_name = "MidiTrackControlsBaseUnselected";
1359                 } else {
1360                         controls_base_selected_name = "MidiTrackControlsBaseInactiveSelected";
1361                         controls_base_unselected_name = "MidiTrackControlsBaseInactiveUnselected";
1362                 }
1363         } else { // MIDI bus (which doesn't exist yet..)
1364                 if (_route->active()) {
1365                         controls_base_selected_name = "BusControlsBaseSelected";
1366                         controls_base_unselected_name = "BusControlsBaseUnselected";
1367                 } else {
1368                         controls_base_selected_name = "BusControlsBaseInactiveSelected";
1369                         controls_base_unselected_name = "BusControlsBaseInactiveUnselected";
1370                 }
1371         }
1372
1373         if (selected()) {
1374                 controls_ebox.set_name (controls_base_selected_name);
1375                 time_axis_frame.set_name (controls_base_selected_name);
1376         } else {
1377                 controls_ebox.set_name (controls_base_unselected_name);
1378                 time_axis_frame.set_name (controls_base_unselected_name);
1379         }
1380 }
1381
1382 void
1383 MidiTimeAxisView::set_note_selection (uint8_t note)
1384 {
1385         uint16_t chn_mask = midi_track()->get_playback_channel_mask();
1386
1387         _editor.begin_reversible_selection_op (X_("Set Note Selection"));
1388
1389         if (_view->num_selected_regionviews() == 0) {
1390                 _view->foreach_regionview (
1391                         sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_selection_region_view),
1392                                     note, chn_mask));
1393         } else {
1394                 _view->foreach_selected_regionview (
1395                         sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_selection_region_view),
1396                                     note, chn_mask));
1397         }
1398
1399         _editor.commit_reversible_selection_op();
1400 }
1401
1402 void
1403 MidiTimeAxisView::add_note_selection (uint8_t note)
1404 {
1405         const uint16_t chn_mask = midi_track()->get_playback_channel_mask();
1406
1407         _editor.begin_reversible_selection_op (X_("Add Note Selection"));
1408
1409         if (_view->num_selected_regionviews() == 0) {
1410                 _view->foreach_regionview (
1411                         sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection_region_view),
1412                                     note, chn_mask));
1413         } else {
1414                 _view->foreach_selected_regionview (
1415                         sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection_region_view),
1416                                     note, chn_mask));
1417         }
1418
1419         _editor.commit_reversible_selection_op();
1420 }
1421
1422 void
1423 MidiTimeAxisView::extend_note_selection (uint8_t note)
1424 {
1425         const uint16_t chn_mask = midi_track()->get_playback_channel_mask();
1426
1427         _editor.begin_reversible_selection_op (X_("Extend Note Selection"));
1428
1429         if (_view->num_selected_regionviews() == 0) {
1430                 _view->foreach_regionview (
1431                         sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::extend_note_selection_region_view),
1432                                     note, chn_mask));
1433         } else {
1434                 _view->foreach_selected_regionview (
1435                         sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::extend_note_selection_region_view),
1436                                     note, chn_mask));
1437         }
1438
1439         _editor.commit_reversible_selection_op();
1440 }
1441
1442 void
1443 MidiTimeAxisView::toggle_note_selection (uint8_t note)
1444 {
1445         const uint16_t chn_mask = midi_track()->get_playback_channel_mask();
1446
1447         _editor.begin_reversible_selection_op (X_("Toggle Note Selection"));
1448
1449         if (_view->num_selected_regionviews() == 0) {
1450                 _view->foreach_regionview (
1451                         sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection_region_view),
1452                                     note, chn_mask));
1453         } else {
1454                 _view->foreach_selected_regionview (
1455                         sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection_region_view),
1456                                     note, chn_mask));
1457         }
1458
1459         _editor.commit_reversible_selection_op();
1460 }
1461
1462 void
1463 MidiTimeAxisView::get_per_region_note_selection (list<pair<PBD::ID, set<boost::shared_ptr<Evoral::Note<Evoral::Beats> > > > >& selection)
1464 {
1465         _view->foreach_regionview (
1466                 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::get_per_region_note_selection_region_view), sigc::ref(selection)));
1467 }
1468
1469 void
1470 MidiTimeAxisView::set_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1471 {
1472         dynamic_cast<MidiRegionView*>(rv)->select_matching_notes (note, chn_mask, false, false);
1473 }
1474
1475 void
1476 MidiTimeAxisView::add_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1477 {
1478         dynamic_cast<MidiRegionView*>(rv)->select_matching_notes (note, chn_mask, true, false);
1479 }
1480
1481 void
1482 MidiTimeAxisView::extend_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1483 {
1484         dynamic_cast<MidiRegionView*>(rv)->select_matching_notes (note, chn_mask, true, true);
1485 }
1486
1487 void
1488 MidiTimeAxisView::toggle_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1489 {
1490         dynamic_cast<MidiRegionView*>(rv)->toggle_matching_notes (note, chn_mask);
1491 }
1492
1493 void
1494 MidiTimeAxisView::get_per_region_note_selection_region_view (RegionView* rv, list<pair<PBD::ID, std::set<boost::shared_ptr<Evoral::Note<Evoral::Beats> > > > > &selection)
1495 {
1496         Evoral::Sequence<Evoral::Beats>::Notes selected;
1497         dynamic_cast<MidiRegionView*>(rv)->selection_as_notelist (selected, false);
1498
1499         std::set<boost::shared_ptr<Evoral::Note<Evoral::Beats> > > notes;
1500
1501         Evoral::Sequence<Evoral::Beats>::Notes::iterator sel_it;
1502         for (sel_it = selected.begin(); sel_it != selected.end(); ++sel_it) {
1503                 notes.insert (*sel_it);
1504         }
1505
1506         if (!notes.empty()) {
1507                 selection.push_back (make_pair ((rv)->region()->id(), notes));
1508         }
1509 }
1510
1511 void
1512 MidiTimeAxisView::set_channel_mode (ChannelMode, uint16_t)
1513 {
1514         /* hide all automation tracks that use the wrong channel(s) and show all those that use
1515            the right ones.
1516         */
1517
1518         const uint16_t selected_channels = midi_track()->get_playback_channel_mask();
1519         bool changed = false;
1520
1521         no_redraw = true;
1522
1523         for (uint32_t ctl = 0; ctl < 127; ++ctl) {
1524
1525                 for (uint32_t chn = 0; chn < 16; ++chn) {
1526                         Evoral::Parameter fully_qualified_param (MidiCCAutomation, chn, ctl);
1527                         boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
1528
1529                         if (!track) {
1530                                 continue;
1531                         }
1532
1533                         if ((selected_channels & (0x0001 << chn)) == 0) {
1534                                 /* channel not in use. hiding it will trigger RouteTimeAxisView::automation_track_hidden()
1535                                    which will cause a redraw. We don't want one per channel, so block that with no_redraw.
1536                                 */
1537                                 changed = track->set_marked_for_display (false) || changed;
1538                         } else {
1539                                 changed = track->set_marked_for_display (true) || changed;
1540                         }
1541                 }
1542         }
1543
1544         no_redraw = false;
1545
1546         /* TODO: Bender, Pressure */
1547
1548         /* invalidate the controller menu, so that we rebuild it next time */
1549         _controller_menu_map.clear ();
1550         delete controller_menu;
1551         controller_menu = 0;
1552
1553         if (changed) {
1554                 request_redraw ();
1555         }
1556 }
1557
1558 Gtk::CheckMenuItem*
1559 MidiTimeAxisView::automation_child_menu_item (Evoral::Parameter param)
1560 {
1561         Gtk::CheckMenuItem* m = RouteTimeAxisView::automation_child_menu_item (param);
1562         if (m) {
1563                 return m;
1564         }
1565
1566         ParameterMenuMap::iterator i = _controller_menu_map.find (param);
1567         if (i != _controller_menu_map.end()) {
1568                 return i->second;
1569         }
1570
1571         i = _channel_command_menu_map.find (param);
1572         if (i != _channel_command_menu_map.end()) {
1573                 return i->second;
1574         }
1575
1576         return 0;
1577 }
1578
1579 boost::shared_ptr<MidiRegion>
1580 MidiTimeAxisView::add_region (framepos_t f, framecnt_t length, bool commit)
1581 {
1582         Editor* real_editor = dynamic_cast<Editor*> (&_editor);
1583         MusicFrame pos (f, 0);
1584
1585         if (commit) {
1586                 real_editor->begin_reversible_command (Operations::create_region);
1587         }
1588         playlist()->clear_changes ();
1589
1590         real_editor->snap_to (pos, RoundNearest);
1591
1592         boost::shared_ptr<Source> src = _session->create_midi_source_by_stealing_name (view()->trackview().track());
1593         PropertyList plist;
1594
1595         plist.add (ARDOUR::Properties::start, 0);
1596         plist.add (ARDOUR::Properties::length, length);
1597         plist.add (ARDOUR::Properties::name, PBD::basename_nosuffix(src->name()));
1598
1599         boost::shared_ptr<Region> region = (RegionFactory::create (src, plist));
1600         /* sets beat position */
1601         region->set_position (pos.frame, pos.division);
1602         playlist()->add_region (region, pos.frame, 1.0, false, pos.division);
1603         _session->add_command (new StatefulDiffCommand (playlist()));
1604
1605         if (commit) {
1606                 real_editor->commit_reversible_command ();
1607         }
1608
1609         return boost::dynamic_pointer_cast<MidiRegion>(region);
1610 }
1611
1612 void
1613 MidiTimeAxisView::ensure_step_editor ()
1614 {
1615         if (!_step_editor) {
1616                 _step_editor = new StepEditor (_editor, midi_track(), *this);
1617         }
1618 }
1619
1620 void
1621 MidiTimeAxisView::start_step_editing ()
1622 {
1623         ensure_step_editor ();
1624         _step_editor->start_step_editing ();
1625
1626 }
1627 void
1628 MidiTimeAxisView::stop_step_editing ()
1629 {
1630         if (_step_editor) {
1631                 _step_editor->stop_step_editing ();
1632         }
1633 }
1634
1635 /** @return channel (counted from 0) to add an event to, based on the current setting
1636  *  of the channel selector.
1637  */
1638 uint8_t
1639 MidiTimeAxisView::get_channel_for_add () const
1640 {
1641         uint16_t const chn_mask = midi_track()->get_playback_channel_mask();
1642         int chn_cnt = 0;
1643         uint8_t channel = 0;
1644
1645         /* pick the highest selected channel, unless all channels are selected,
1646            which is interpreted to mean channel 1 (zero)
1647         */
1648
1649         for (uint16_t i = 0; i < 16; ++i) {
1650                 if (chn_mask & (1<<i)) {
1651                         channel = i;
1652                         chn_cnt++;
1653                 }
1654         }
1655
1656         if (chn_cnt == 16) {
1657                 channel = 0;
1658         }
1659
1660         return channel;
1661 }
1662
1663 void
1664 MidiTimeAxisView::note_range_changed ()
1665 {
1666         set_gui_property ("note-range-min", (int) midi_view()->lowest_note ());
1667         set_gui_property ("note-range-max", (int) midi_view()->highest_note ());
1668 }
1669
1670 void
1671 MidiTimeAxisView::contents_height_changed ()
1672 {
1673         _range_scroomer->queue_resize ();
1674 }
1675
1676 bool
1677 MidiTimeAxisView::paste (framepos_t pos, const Selection& selection, PasteContext& ctx, const int32_t sub_num)
1678 {
1679         if (!_editor.internal_editing()) {
1680                 // Non-internal paste, paste regions like any other route
1681                 return RouteTimeAxisView::paste(pos, selection, ctx, sub_num);
1682         }
1683
1684         return midi_view()->paste(pos, selection, ctx, sub_num);
1685 }