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