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