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