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