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