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