1657e11bfb95bbb4c35296190a74a1153f6a91a4
[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/stl_delete.h"
30 #include "pbd/whitespace.h"
31 #include "pbd/basename.h"
32 #include "pbd/enumwriter.h"
33 #include "pbd/memento_command.h"
34 #include "pbd/stateful_diff_command.h"
35
36 #include "gtkmm2ext/gtk_ui.h"
37 #include "gtkmm2ext/selector.h"
38 #include "gtkmm2ext/bindable_button.h"
39 #include "gtkmm2ext/utils.h"
40
41 #include "ardour/event_type_map.h"
42 #include "ardour/midi_patch_manager.h"
43 #include "ardour/midi_playlist.h"
44 #include "ardour/midi_region.h"
45 #include "ardour/midi_source.h"
46 #include "ardour/midi_track.h"
47 #include "ardour/operations.h"
48 #include "ardour/playlist.h"
49 #include "ardour/region.h"
50 #include "ardour/region_factory.h"
51 #include "ardour/route.h"
52 #include "ardour/session.h"
53 #include "ardour/session_object.h"
54 #include "ardour/source.h"
55 #include "ardour/track.h"
56 #include "ardour/types.h"
57
58 #include "midi++/names.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_scroomer.h"
72 #include "midi_streamview.h"
73 #include "midi_region_view.h"
74 #include "midi_time_axis.h"
75 #include "piano_roll_header.h"
76 #include "playlist_selector.h"
77 #include "plugin_selector.h"
78 #include "plugin_ui.h"
79 #include "point_selection.h"
80 #include "prompter.h"
81 #include "region_view.h"
82 #include "rgb_macros.h"
83 #include "selection.h"
84 #include "step_editor.h"
85 #include "simplerect.h"
86 #include "utils.h"
87
88 #include "ardour/midi_track.h"
89
90 #include "i18n.h"
91
92 using namespace ARDOUR;
93 using namespace PBD;
94 using namespace Gtk;
95 using namespace Gtkmm2ext;
96 using namespace Editing;
97
98 // Minimum height at which a control is displayed
99 static const uint32_t MIDI_CONTROLS_BOX_MIN_HEIGHT = 162;
100 static const uint32_t KEYBOARD_MIN_HEIGHT = 140;
101
102 MidiTimeAxisView::MidiTimeAxisView (PublicEditor& ed, Session* sess, Canvas& canvas)
103         : AxisView(sess) // virtually inherited
104         , RouteTimeAxisView(ed, sess, canvas)
105         , _ignore_signals(false)
106         , _range_scroomer(0)
107         , _piano_roll_header(0)
108         , _note_mode(Sustained)
109         , _note_mode_item(0)
110         , _percussion_mode_item(0)
111         , _color_mode(MeterColors)
112         , _meter_color_mode_item(0)
113         , _channel_color_mode_item(0)
114         , _track_color_mode_item(0)
115         , _step_edit_item (0)
116         , _midi_thru_item (0)
117         , controller_menu (0)
118         , _step_editor (0)
119 {
120 }
121
122 void
123 MidiTimeAxisView::set_route (boost::shared_ptr<Route> rt)
124 {
125         _route = rt;
126         
127         _view = new MidiStreamView (*this);
128         
129         if (is_track ()) {
130                 _piano_roll_header = new PianoRollHeader(*midi_view());
131                 _range_scroomer = new MidiScroomer(midi_view()->note_range_adjustment);
132         }
133
134         /* This next call will result in our height being set up, so it must come after
135            the creation of the piano roll / range scroomer as their visibility is set up
136            when our height is.
137         */
138         RouteTimeAxisView::set_route (rt);
139
140         _view->apply_color (_color, StreamView::RegionColor);
141
142         subplugin_menu.set_name ("ArdourContextMenu");
143
144         if (!gui_property ("note-range-min").empty ()) {
145                 midi_view()->apply_note_range (atoi (gui_property ("note-range-min").c_str()), atoi (gui_property ("note-range-max").c_str()), true);
146         }
147         midi_view()->NoteRangeChanged.connect (sigc::mem_fun (*this, &MidiTimeAxisView::note_range_changed));
148
149         ignore_toggle = false;
150
151         if (is_midi_track()) {
152                 controls_ebox.set_name ("MidiTimeAxisViewControlsBaseUnselected");
153                 _note_mode = midi_track()->note_mode();
154         } else { // MIDI bus (which doesn't exist yet..)
155                 controls_ebox.set_name ("MidiBusControlsBaseUnselected");
156         }
157
158         /* map current state of the route */
159
160         processors_changed (RouteProcessorChange ());
161
162         _route->processors_changed.connect (*this, invalidator (*this), boost::bind (&MidiTimeAxisView::processors_changed, this, _1), gui_context());
163
164         if (is_track()) {
165                 _piano_roll_header->SetNoteSelection.connect (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_selection));
166                 _piano_roll_header->AddNoteSelection.connect (sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection));
167                 _piano_roll_header->ExtendNoteSelection.connect (sigc::mem_fun (*this, &MidiTimeAxisView::extend_note_selection));
168                 _piano_roll_header->ToggleNoteSelection.connect (sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection));
169
170                 /* Suspend updates of the StreamView during scroomer drags to speed things up */
171                 _range_scroomer->DragStarting.connect (sigc::mem_fun (*midi_view(), &MidiStreamView::suspend_updates));
172                 _range_scroomer->DragFinishing.connect (sigc::mem_fun (*midi_view(), &MidiStreamView::resume_updates));
173
174                 controls_hbox.pack_start(*_range_scroomer);
175                 controls_hbox.pack_start(*_piano_roll_header);
176
177                 controls_ebox.set_name ("MidiTrackControlsBaseUnselected");
178                 controls_base_selected_name = "MidiTrackControlsBaseSelected";
179                 controls_base_unselected_name = "MidiTrackControlsBaseUnselected";
180
181                 midi_view()->NoteRangeChanged.connect (sigc::mem_fun(*this, &MidiTimeAxisView::update_range));
182
183                 /* ask for notifications of any new RegionViews */
184                 _view->RegionViewAdded.connect (sigc::mem_fun(*this, &MidiTimeAxisView::region_view_added));
185
186                 if (!_editor.have_idled()) {
187                         /* first idle will do what we need */
188                 } else {
189                         first_idle ();
190                 }
191         }
192
193         HBox* midi_controls_hbox = manage(new HBox());
194
195         MIDI::Name::MidiPatchManager& patch_manager = MIDI::Name::MidiPatchManager::instance();
196
197         MIDI::Name::MasterDeviceNames::Models::const_iterator m = patch_manager.all_models().begin();
198         for (; m != patch_manager.all_models().end(); ++m) {
199                 _model_selector.append_text(m->c_str());
200         }
201
202
203         _model_selector.signal_changed().connect(sigc::mem_fun(*this, &MidiTimeAxisView::model_changed));
204
205         _custom_device_mode_selector.signal_changed().connect(
206                         sigc::mem_fun(*this, &MidiTimeAxisView::custom_device_mode_changed));
207
208         _model_selector.set_active_text (gui_property (X_("midnam-model-name")));
209         _custom_device_mode_selector.set_active_text (gui_property (X_("midnam-custom-device-mode")));
210
211         ARDOUR_UI::instance()->set_tip (_model_selector, _("External MIDI Device"));
212         ARDOUR_UI::instance()->set_tip (_custom_device_mode_selector, _("External Device Mode"));
213
214         midi_controls_hbox->pack_start(_channel_selector, true, false);
215         if (!patch_manager.all_models().empty()) {
216                 _midi_controls_box.set_border_width (5);
217                 _midi_controls_box.pack_start(_model_selector, true, false);
218                 _midi_controls_box.pack_start(_custom_device_mode_selector, true, false);
219         }
220
221         _midi_controls_box.pack_start(*midi_controls_hbox, true, true);
222
223         controls_vbox.pack_start(_midi_controls_box, false, false);
224
225         // restore channel selector settings
226         _channel_selector.set_channel_mode(midi_track()->get_channel_mode(), midi_track()->get_channel_mask());
227         _channel_selector.mode_changed.connect(
228                 sigc::mem_fun(*midi_track(), &MidiTrack::set_channel_mode));
229         _channel_selector.mode_changed.connect(
230                 sigc::mem_fun(*this, &MidiTimeAxisView::set_channel_mode));
231
232         string prop = gui_property ("color-mode");
233         if (!prop.empty()) {
234                 _color_mode = ColorMode (string_2_enum(prop, _color_mode));
235                 if (_color_mode == ChannelColors) {
236                         _channel_selector.set_channel_colors(CanvasNoteEvent::midi_channel_colors);
237                 }
238         }
239
240         set_color_mode (_color_mode, true, false);
241
242         prop = gui_property ("note-mode");
243         if (!prop.empty()) {
244                 _note_mode = NoteMode (string_2_enum (prop, _note_mode));
245                 if (_percussion_mode_item) {
246                         _percussion_mode_item->set_active (_note_mode == Percussive);
247                 }
248         }
249
250         /* Look for any GUI object state nodes that represent automation children that should exist, and create
251          * the children.
252          */
253
254         list<string> gui_ids = gui_object_state().all_ids ();
255         for (list<string>::const_iterator i = gui_ids.begin(); i != gui_ids.end(); ++i) {
256                 PBD::ID route_id;
257                 bool has_parameter;
258                 Evoral::Parameter parameter (0, 0, 0);
259
260                 bool const p = AutomationTimeAxisView::parse_state_id (*i, route_id, has_parameter, parameter);
261                 if (p && route_id == _route->id () && has_parameter) {
262                         create_automation_child (parameter, string_is_affirmative (gui_object_state().get_string (*i, X_("visible"))));
263                 }
264         }
265 }
266
267 void
268 MidiTimeAxisView::first_idle ()
269 {
270         if (is_track ()) {
271                 _view->attach ();
272         }
273 }
274
275 MidiTimeAxisView::~MidiTimeAxisView ()
276 {
277         delete _piano_roll_header;
278         _piano_roll_header = 0;
279
280         delete _range_scroomer;
281         _range_scroomer = 0;
282
283         delete controller_menu;
284         delete _step_editor;
285 }
286
287 void
288 MidiTimeAxisView::enter_internal_edit_mode ()
289 {
290         if (midi_view()) {
291                 midi_view()->enter_internal_edit_mode ();
292         }
293 }
294
295 void
296 MidiTimeAxisView::leave_internal_edit_mode ()
297 {
298         if (midi_view()) {
299                 midi_view()->leave_internal_edit_mode ();
300         }
301 }
302
303 void
304 MidiTimeAxisView::check_step_edit ()
305 {
306         ensure_step_editor ();
307         _step_editor->check_step_edit ();
308 }
309
310 void
311 MidiTimeAxisView::model_changed()
312 {
313         std::list<std::string> device_modes = MIDI::Name::MidiPatchManager::instance()
314                 .custom_device_mode_names_by_model(_model_selector.get_active_text());
315
316         _custom_device_mode_selector.clear_items();
317
318         for (std::list<std::string>::const_iterator i = device_modes.begin();
319                         i != device_modes.end(); ++i) {
320                 _custom_device_mode_selector.append_text(*i);
321         }
322
323         _custom_device_mode_selector.set_active(0);
324         
325         _route->instrument_info().set_external_instrument (_model_selector.get_active_text(), _custom_device_mode_selector.get_active_text());
326 }
327
328 void
329 MidiTimeAxisView::custom_device_mode_changed()
330 {
331         _route->instrument_info().set_external_instrument (_model_selector.get_active_text(), _custom_device_mode_selector.get_active_text());
332 }
333
334 MidiStreamView*
335 MidiTimeAxisView::midi_view()
336 {
337         return dynamic_cast<MidiStreamView*>(_view);
338 }
339
340 void
341 MidiTimeAxisView::set_height (uint32_t h)
342 {
343         if (h >= MIDI_CONTROLS_BOX_MIN_HEIGHT) {
344                 _midi_controls_box.show_all ();
345         } else {
346                 _midi_controls_box.hide();
347         }
348         
349         if (h >= KEYBOARD_MIN_HEIGHT) {
350                 if (is_track() && _range_scroomer) {
351                         _range_scroomer->show();
352                 }
353                 if (is_track() && _piano_roll_header) {
354                         _piano_roll_header->show();
355                 }
356         } else {
357                 if (is_track() && _range_scroomer) {
358                         _range_scroomer->hide();
359                 }
360                 if (is_track() && _piano_roll_header) {
361                         _piano_roll_header->hide();
362                 }
363         }
364
365         /* We need to do this after changing visibility of our stuff, as it will
366            eventually trigger a call to Editor::reset_controls_layout_width(),
367            which needs to know if we have just shown or hidden a scroomer /
368            piano roll.
369         */
370         RouteTimeAxisView::set_height (h);
371 }
372
373 void
374 MidiTimeAxisView::append_extra_display_menu_items ()
375 {
376         using namespace Menu_Helpers;
377
378         MenuList& items = display_menu->items();
379
380         // Note range
381         Menu *range_menu = manage(new Menu);
382         MenuList& range_items = range_menu->items();
383         range_menu->set_name ("ArdourContextMenu");
384
385         range_items.push_back (MenuElem (_("Show Full Range"), sigc::bind (
386                         sigc::mem_fun(*this, &MidiTimeAxisView::set_note_range),
387                         MidiStreamView::FullRange)));
388
389         range_items.push_back (MenuElem (_("Fit Contents"), sigc::bind (
390                         sigc::mem_fun(*this, &MidiTimeAxisView::set_note_range),
391                         MidiStreamView::ContentsRange)));
392
393         items.push_back (MenuElem (_("Note Range"), *range_menu));
394         items.push_back (MenuElem (_("Note Mode"), *build_note_mode_menu()));
395
396         items.push_back (CheckMenuElem (_("MIDI Thru"), sigc::mem_fun(*this, &MidiTimeAxisView::toggle_midi_thru)));
397         _midi_thru_item = dynamic_cast<CheckMenuItem*>(&items.back());
398
399         items.push_back (SeparatorElem ());
400 }
401
402 void
403 MidiTimeAxisView::toggle_midi_thru ()
404 {
405         if (!_midi_thru_item) {
406                 return;
407         }
408
409         bool view_yn = _midi_thru_item->get_active();
410         if (view_yn != midi_track()->midi_thru()) {
411                 midi_track()->set_midi_thru (view_yn);
412         }
413 }
414
415 void
416 MidiTimeAxisView::build_automation_action_menu (bool for_selection)
417 {
418         using namespace Menu_Helpers;
419
420         /* If we have a controller menu, we need to detach it before
421            RouteTimeAxis::build_automation_action_menu destroys the
422            menu it is attached to.  Otherwise GTK destroys
423            controller_menu's gobj, meaning that it can't be reattached
424            below.  See bug #3134.
425         */
426
427         if (controller_menu) {
428                 detach_menu (*controller_menu);
429         }
430
431         _channel_command_menu_map.clear ();
432         RouteTimeAxisView::build_automation_action_menu (for_selection);
433
434         MenuList& automation_items = automation_action_menu->items();
435
436         uint16_t selected_channels = _channel_selector.get_selected_channels();
437
438         if (selected_channels !=  0) {
439
440                 automation_items.push_back (SeparatorElem());
441
442                 /* these 2 MIDI "command" types are semantically more like automation than note data,
443                    but they are not MIDI controllers. We give them special status in this menu, since
444                    they will not show up in the controller list and anyone who actually knows
445                    something about MIDI (!) would not expect to find them there.
446                 */
447
448                 add_channel_command_menu_item (automation_items, _("Bender"), MidiPitchBenderAutomation, 0);
449                 automation_items.back().set_sensitive (!for_selection || _editor.get_selection().tracks.size() == 1);
450                 add_channel_command_menu_item (automation_items, _("Pressure"), MidiChannelPressureAutomation, 0);
451                 automation_items.back().set_sensitive (!for_selection || _editor.get_selection().tracks.size() == 1);
452
453                 /* now all MIDI controllers. Always offer the possibility that we will rebuild the controllers menu
454                    since it might need to be updated after a channel mode change or other change. Also detach it
455                    first in case it has been used anywhere else.
456                 */
457
458                 build_controller_menu ();
459
460                 automation_items.push_back (SeparatorElem());
461                 automation_items.push_back (MenuElem (_("Controllers"), *controller_menu));
462                 automation_items.back().set_sensitive (!for_selection || _editor.get_selection().tracks.size() == 1);
463         } else {
464                 automation_items.push_back (MenuElem (string_compose ("<i>%1</i>", _("No MIDI Channels selected"))));
465                 dynamic_cast<Label*> (automation_items.back().get_child())->set_use_markup (true);
466         }
467
468 }
469
470 void
471 MidiTimeAxisView::change_all_channel_tracks_visibility (bool yn, Evoral::Parameter param)
472 {
473         uint16_t selected_channels = _channel_selector.get_selected_channels();
474
475         for (uint8_t chn = 0; chn < 16; chn++) {
476                 if (selected_channels & (0x0001 << chn)) {
477
478                         Evoral::Parameter fully_qualified_param (param.type(), chn, param.id());
479                         Gtk::CheckMenuItem* menu = automation_child_menu_item (fully_qualified_param);
480
481                         if (menu) {
482                                 menu->set_active (yn);
483                         }
484                 }
485         }
486 }
487
488 void
489 MidiTimeAxisView::add_channel_command_menu_item (Menu_Helpers::MenuList& items, const string& label, AutomationType auto_type, uint8_t cmd)
490 {
491         using namespace Menu_Helpers;
492
493         /* count the number of selected channels because we will build a different menu structure if there is more than 1 selected.
494          */
495
496         uint16_t selected_channels = _channel_selector.get_selected_channels();
497         int chn_cnt = 0;
498
499         for (uint8_t chn = 0; chn < 16; chn++) {
500                 if (selected_channels & (0x0001 << chn)) {
501                         if (++chn_cnt > 1) {
502                                 break;
503                         }
504                 }
505         }
506
507         if (chn_cnt > 1) {
508
509                 /* multiple channels - create a submenu, with 1 item per channel */
510
511                 Menu* chn_menu = manage (new Menu);
512                 MenuList& chn_items (chn_menu->items());
513                 Evoral::Parameter param_without_channel (auto_type, 0, cmd);
514
515                 /* add a couple of items to hide/show all of them */
516
517                 chn_items.push_back (MenuElem (_("Hide all channels"),
518                                                     sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
519                                                                 false, param_without_channel)));
520                 chn_items.push_back (MenuElem (_("Show all channels"),
521                                                     sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
522                                                                 true, param_without_channel)));
523
524                 for (uint8_t chn = 0; chn < 16; chn++) {
525                         if (selected_channels & (0x0001 << chn)) {
526
527                                 /* for each selected channel, add a menu item for this controller */
528
529                                 Evoral::Parameter fully_qualified_param (auto_type, chn, cmd);
530                                 chn_items.push_back (CheckMenuElem (string_compose (_("Channel %1"), chn+1),
531                                                                     sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
532                                                                                 fully_qualified_param)));
533
534                                 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
535                                 bool visible = false;
536
537                                 if (track) {
538                                         if (track->marked_for_display()) {
539                                                 visible = true;
540                                         }
541                                 }
542
543                                 CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&chn_items.back());
544                                 _channel_command_menu_map[fully_qualified_param] = cmi;
545                                 cmi->set_active (visible);
546                         }
547                 }
548
549                 /* now create an item in the parent menu that has the per-channel list as a submenu */
550
551                 items.push_back (MenuElem (label, *chn_menu));
552
553         } else {
554
555                 /* just one channel - create a single menu item for this command+channel combination*/
556
557                 for (uint8_t chn = 0; chn < 16; chn++) {
558                         if (selected_channels & (0x0001 << chn)) {
559
560                                 Evoral::Parameter fully_qualified_param (auto_type, chn, cmd);
561                                 items.push_back (CheckMenuElem (label,
562                                                                 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
563                                                                             fully_qualified_param)));
564
565                                 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
566                                 bool visible = false;
567
568                                 if (track) {
569                                         if (track->marked_for_display()) {
570                                                 visible = true;
571                                         }
572                                 }
573
574                                 CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&items.back());
575                                 _channel_command_menu_map[fully_qualified_param] = cmi;
576                                 cmi->set_active (visible);
577
578                                 /* one channel only */
579                                 break;
580                         }
581                 }
582         }
583 }
584
585 void
586 MidiTimeAxisView::build_controller_menu ()
587 {
588         using namespace Menu_Helpers;
589
590         if (controller_menu) {
591                 /* it exists and has not been invalidated by a channel mode change, so just return it */
592                 return;
593         }
594
595         controller_menu = new Menu; // explicitly managed by us
596         MenuList& items (controller_menu->items());
597
598         /* create several "top level" menu items for sets of controllers (16 at a time), and populate each one with a submenu
599            for each controller+channel combination covering the currently selected channels for this track
600         */
601
602         uint16_t selected_channels = _channel_selector.get_selected_channels();
603
604         /* count the number of selected channels because we will build a different menu structure if there is more than 1 selected.
605          */
606
607         int chn_cnt = 0;
608
609         for (uint8_t chn = 0; chn < 16; chn++) {
610                 if (selected_channels & (0x0001 << chn)) {
611                         if (++chn_cnt > 1) {
612                                 break;
613                         }
614                 }
615         }
616
617         /* loop over all 127 MIDI controllers, in groups of 16; except don't offer
618            bank select controllers, as they are handled by the `patch' code */
619
620         for (int i = 0; i < 127; i += 16) {
621
622                 Menu* ctl_menu = manage (new Menu);
623                 MenuList& ctl_items (ctl_menu->items());
624
625
626                 /* for each controller, consider whether to create a submenu or a single item */
627
628                 for (int ctl = i; ctl < i+16; ++ctl) {
629
630                         if (ctl == MIDI_CTL_MSB_BANK || ctl == MIDI_CTL_LSB_BANK) {
631                                 continue;
632                         }
633
634                         if (chn_cnt > 1) {
635
636                                 /* multiple channels - create a submenu, with 1 item per channel */
637
638                                 Menu* chn_menu = manage (new Menu);
639                                 MenuList& chn_items (chn_menu->items());
640
641                                 /* add a couple of items to hide/show this controller on all channels */
642
643                                 Evoral::Parameter param_without_channel (MidiCCAutomation, 0, ctl);
644                                 chn_items.push_back (MenuElem (_("Hide all channels"),
645                                                                     sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
646                                                                                 false, param_without_channel)));
647                                 chn_items.push_back (MenuElem (_("Show all channels"),
648                                                                     sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
649                                                                                 true, param_without_channel)));
650
651                                 for (uint8_t chn = 0; chn < 16; chn++) {
652                                         if (selected_channels & (0x0001 << chn)) {
653
654                                                 /* for each selected channel, add a menu item for this controller */
655
656                                                 Evoral::Parameter fully_qualified_param (MidiCCAutomation, chn, ctl);
657                                                 chn_items.push_back (CheckMenuElem (string_compose (_("Channel %1"), chn+1),
658                                                                                     sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
659                                                                                                 fully_qualified_param)));
660
661                                                 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
662                                                 bool visible = false;
663
664                                                 if (track) {
665                                                         if (track->marked_for_display()) {
666                                                                 visible = true;
667                                                         }
668                                                 }
669
670                                                 CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&chn_items.back());
671                                                 _controller_menu_map[fully_qualified_param] = cmi;
672                                                 cmi->set_active (visible);
673                                         }
674                                 }
675
676                                 /* add the per-channel menu to the list of controllers, with the name of the controller */
677                                 ctl_items.push_back (MenuElem (string_compose ("<b>%1</b>: %2", ctl, midi_name (ctl)), *chn_menu));
678                                 dynamic_cast<Label*> (ctl_items.back().get_child())->set_use_markup (true);
679
680                         } else {
681
682                                 /* just one channel - create a single menu item for this ctl+channel combination*/
683
684                                 for (uint8_t chn = 0; chn < 16; chn++) {
685                                         if (selected_channels & (0x0001 << chn)) {
686
687                                                 Evoral::Parameter fully_qualified_param (MidiCCAutomation, chn, ctl);
688                                                 ctl_items.push_back (
689                                                         CheckMenuElem (
690                                                                 string_compose ("<b>%1</b>: %2 [%3]", ctl, midi_name (ctl), int (chn)),
691                                                                 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
692                                                                             fully_qualified_param)
693                                                                 )
694                                                         );
695                                                 dynamic_cast<Label*> (ctl_items.back().get_child())->set_use_markup (true);
696
697                                                 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
698                                                 bool visible = false;
699
700                                                 if (track) {
701                                                         if (track->marked_for_display()) {
702                                                                 visible = true;
703                                                         }
704                                                 }
705
706                                                 CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&ctl_items.back());
707                                                 _controller_menu_map[fully_qualified_param] = cmi;
708                                                 cmi->set_active (visible);
709
710                                                 /* one channel only */
711                                                 break;
712                                         }
713                                 }
714                         }
715                 }
716
717                 /* add the menu for this block of controllers to the overall controller menu */
718
719                 items.push_back (MenuElem (string_compose (_("Controllers %1-%2"), i, i+15), *ctl_menu));
720         }
721 }
722
723 Gtk::Menu*
724 MidiTimeAxisView::build_note_mode_menu()
725 {
726         using namespace Menu_Helpers;
727
728         Menu* mode_menu = manage (new Menu);
729         MenuList& items = mode_menu->items();
730         mode_menu->set_name ("ArdourContextMenu");
731
732         RadioMenuItem::Group mode_group;
733         items.push_back (RadioMenuElem (mode_group, _("Sustained"),
734                                 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_mode), Sustained)));
735         _note_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
736         _note_mode_item->set_active(_note_mode == Sustained);
737
738         items.push_back (RadioMenuElem (mode_group, _("Percussive"),
739                                 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_mode), Percussive)));
740         _percussion_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
741         _percussion_mode_item->set_active(_note_mode == Percussive);
742
743         return mode_menu;
744 }
745
746 Gtk::Menu*
747 MidiTimeAxisView::build_color_mode_menu()
748 {
749         using namespace Menu_Helpers;
750
751         Menu* mode_menu = manage (new Menu);
752         MenuList& items = mode_menu->items();
753         mode_menu->set_name ("ArdourContextMenu");
754
755         RadioMenuItem::Group mode_group;
756         items.push_back (RadioMenuElem (mode_group, _("Meter Colors"),
757                                         sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode),
758                                                     MeterColors, false, true)));
759         _meter_color_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
760         _meter_color_mode_item->set_active(_color_mode == MeterColors);
761
762         items.push_back (RadioMenuElem (mode_group, _("Channel Colors"),
763                                         sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode),
764                                                     ChannelColors, false, true)));
765         _channel_color_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
766         _channel_color_mode_item->set_active(_color_mode == ChannelColors);
767
768         items.push_back (RadioMenuElem (mode_group, _("Track Color"),
769                                         sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode),
770                                                     TrackColor, false, true)));
771         _channel_color_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
772         _channel_color_mode_item->set_active(_color_mode == TrackColor);
773
774         return mode_menu;
775 }
776
777 void
778 MidiTimeAxisView::set_note_mode(NoteMode mode)
779 {
780         if (_note_mode != mode || midi_track()->note_mode() != mode) {
781                 _note_mode = mode;
782                 midi_track()->set_note_mode(mode);
783                 set_gui_property ("note-mode", enum_2_string(_note_mode));
784                 _view->redisplay_track();
785         }
786 }
787
788 void
789 MidiTimeAxisView::set_color_mode (ColorMode mode, bool force, bool redisplay)
790 {
791         if (_color_mode == mode && !force) {
792                 return;
793         }
794
795         if (mode == ChannelColors) {
796                 _channel_selector.set_channel_colors(CanvasNoteEvent::midi_channel_colors);
797         } else {
798                 _channel_selector.set_default_channel_color();
799         }
800
801         _color_mode = mode;
802         set_gui_property ("color-mode", enum_2_string(_color_mode));
803         if (redisplay) {
804                 _view->redisplay_track();
805         }
806 }
807
808 void
809 MidiTimeAxisView::set_note_range(MidiStreamView::VisibleNoteRange range)
810 {
811         if (!_ignore_signals)
812                 midi_view()->set_note_range(range);
813 }
814
815
816 void
817 MidiTimeAxisView::update_range()
818 {
819         MidiGhostRegion* mgr;
820
821         for(list<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
822                 if ((mgr = dynamic_cast<MidiGhostRegion*>(*i)) != 0) {
823                         mgr->update_range();
824                 }
825         }
826 }
827
828 void
829 MidiTimeAxisView::show_all_automation (bool apply_to_selection)
830 {
831         if (apply_to_selection) {
832                 _editor.get_selection().tracks.foreach_midi_time_axis (boost::bind (&MidiTimeAxisView::show_all_automation, _1, false));
833         } else {
834                 if (midi_track()) {
835                         const set<Evoral::Parameter> params = midi_track()->midi_playlist()->contained_automation();
836
837                         for (set<Evoral::Parameter>::const_iterator i = params.begin(); i != params.end(); ++i) {
838                                 create_automation_child(*i, true);
839                         }
840                 }
841
842                 RouteTimeAxisView::show_all_automation ();
843         }
844 }
845
846 void
847 MidiTimeAxisView::show_existing_automation (bool apply_to_selection)
848 {
849         if (apply_to_selection) {
850                 _editor.get_selection().tracks.foreach_midi_time_axis (boost::bind (&MidiTimeAxisView::show_existing_automation, _1, false));
851         } else {
852                 if (midi_track()) {
853                         const set<Evoral::Parameter> params = midi_track()->midi_playlist()->contained_automation();
854
855                         for (set<Evoral::Parameter>::const_iterator i = params.begin(); i != params.end(); ++i) {
856                                 create_automation_child (*i, true);
857                         }
858                 }
859
860                 RouteTimeAxisView::show_existing_automation ();
861         }
862 }
863
864 /** Create an automation track for the given parameter (pitch bend, channel pressure).
865  */
866 void
867 MidiTimeAxisView::create_automation_child (const Evoral::Parameter& param, bool show)
868 {
869         if (param.type() == NullAutomation) {
870                 return;
871         }
872
873         AutomationTracks::iterator existing = _automation_tracks.find (param);
874
875         if (existing != _automation_tracks.end()) {
876
877                 /* automation track created because we had existing data for
878                  * the processor, but visibility may need to be controlled
879                  * since it will have been set visible by default.
880                  */
881
882                 if (existing->second->set_marked_for_display (show) && !no_redraw) {
883                         request_redraw ();
884                 }
885
886                 return;
887         }
888
889         boost::shared_ptr<AutomationTimeAxisView> track;
890
891         switch (param.type()) {
892
893         case GainAutomation:
894                 create_gain_automation_child (param, show);
895                 break;
896
897         case PluginAutomation:
898                 /* handled elsewhere */
899                 break;
900
901         case MidiCCAutomation:
902         case MidiPgmChangeAutomation:
903         case MidiPitchBenderAutomation:
904         case MidiChannelPressureAutomation:
905         case MidiSystemExclusiveAutomation:
906                 /* These controllers are region "automation" - they are owned
907                  * by regions (and their MidiModels), not by the track. As a
908                  * result we do not create an AutomationList/Line for the track
909                  * ... except here we are doing something!! XXX 
910                  */
911
912                 track.reset (new AutomationTimeAxisView (
913                                      _session,
914                                      _route,
915                                      boost::shared_ptr<Automatable> (),
916                                      boost::shared_ptr<AutomationControl> (),
917                                      param,
918                                      _editor,
919                                      *this,
920                                      true,
921                                      parent_canvas,
922                                      _route->describe_parameter(param)
923                                      ));
924
925                 if (_view) {
926                         _view->foreach_regionview (sigc::mem_fun (*track.get(), &TimeAxisView::add_ghost));
927                 }
928
929                 add_automation_child (param, track, show);
930                 break;
931
932         default:
933                 error << "MidiTimeAxisView: unknown automation child " << EventTypeMap::instance().to_symbol(param) << endmsg;
934         }
935 }
936
937 void
938 MidiTimeAxisView::route_active_changed ()
939 {
940         RouteUI::route_active_changed ();
941
942         if (is_track()) {
943                 if (_route->active()) {
944                         controls_ebox.set_name ("MidiTrackControlsBaseUnselected");
945                         controls_base_selected_name = "MidiTrackControlsBaseSelected";
946                         controls_base_unselected_name = "MidiTrackControlsBaseUnselected";
947                 } else {
948                         controls_ebox.set_name ("MidiTrackControlsBaseInactiveUnselected");
949                         controls_base_selected_name = "MidiTrackControlsBaseInactiveSelected";
950                         controls_base_unselected_name = "MidiTrackControlsBaseInactiveUnselected";
951                 }
952         } else {
953
954                 throw; // wha?
955
956                 if (_route->active()) {
957                         controls_ebox.set_name ("BusControlsBaseUnselected");
958                         controls_base_selected_name = "BusControlsBaseSelected";
959                         controls_base_unselected_name = "BusControlsBaseUnselected";
960                 } else {
961                         controls_ebox.set_name ("BusControlsBaseInactiveUnselected");
962                         controls_base_selected_name = "BusControlsBaseInactiveSelected";
963                         controls_base_unselected_name = "BusControlsBaseInactiveUnselected";
964                 }
965         }
966 }
967
968 void
969 MidiTimeAxisView::set_note_selection (uint8_t note)
970 {
971         if (!_editor.internal_editing()) {
972                 return;
973         }
974
975         uint16_t chn_mask = _channel_selector.get_selected_channels();
976
977         if (_view->num_selected_regionviews() == 0) {
978                 _view->foreach_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_selection_region_view), note, chn_mask));
979         } else {
980                 _view->foreach_selected_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_selection_region_view), note, chn_mask));
981         }
982 }
983
984 void
985 MidiTimeAxisView::add_note_selection (uint8_t note)
986 {
987         if (!_editor.internal_editing()) {
988                 return;
989         }
990
991         uint16_t chn_mask = _channel_selector.get_selected_channels();
992
993         if (_view->num_selected_regionviews() == 0) {
994                 _view->foreach_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection_region_view), note, chn_mask));
995         } else {
996                 _view->foreach_selected_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection_region_view), note, chn_mask));
997         }
998 }
999
1000 void
1001 MidiTimeAxisView::extend_note_selection (uint8_t note)
1002 {
1003         if (!_editor.internal_editing()) {
1004                 return;
1005         }
1006
1007         uint16_t chn_mask = _channel_selector.get_selected_channels();
1008
1009         if (_view->num_selected_regionviews() == 0) {
1010                 _view->foreach_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::extend_note_selection_region_view), note, chn_mask));
1011         } else {
1012                 _view->foreach_selected_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::extend_note_selection_region_view), note, chn_mask));
1013         }
1014 }
1015
1016 void
1017 MidiTimeAxisView::toggle_note_selection (uint8_t note)
1018 {
1019         if (!_editor.internal_editing()) {
1020                 return;
1021         }
1022
1023         uint16_t chn_mask = _channel_selector.get_selected_channels();
1024
1025         if (_view->num_selected_regionviews() == 0) {
1026                 _view->foreach_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection_region_view), note, chn_mask));
1027         } else {
1028                 _view->foreach_selected_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection_region_view), note, chn_mask));
1029         }
1030 }
1031
1032 void
1033 MidiTimeAxisView::set_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1034 {
1035         dynamic_cast<MidiRegionView*>(rv)->select_matching_notes (note, chn_mask, false, false);
1036 }
1037
1038 void
1039 MidiTimeAxisView::add_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1040 {
1041         dynamic_cast<MidiRegionView*>(rv)->select_matching_notes (note, chn_mask, true, false);
1042 }
1043
1044 void
1045 MidiTimeAxisView::extend_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1046 {
1047         dynamic_cast<MidiRegionView*>(rv)->select_matching_notes (note, chn_mask, true, true);
1048 }
1049
1050 void
1051 MidiTimeAxisView::toggle_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1052 {
1053         dynamic_cast<MidiRegionView*>(rv)->toggle_matching_notes (note, chn_mask);
1054 }
1055
1056 void
1057 MidiTimeAxisView::set_channel_mode (ChannelMode, uint16_t)
1058 {
1059         /* hide all automation tracks that use the wrong channel(s) and show all those that use
1060            the right ones.
1061         */
1062
1063         uint16_t selected_channels = _channel_selector.get_selected_channels();
1064         bool changed = false;
1065
1066         no_redraw = true;
1067
1068         for (uint32_t ctl = 0; ctl < 127; ++ctl) {
1069
1070                 for (uint32_t chn = 0; chn < 16; ++chn) {
1071                         Evoral::Parameter fully_qualified_param (MidiCCAutomation, chn, ctl);
1072                         boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
1073
1074                         if (!track) {
1075                                 continue;
1076                         }
1077
1078                         if ((selected_channels & (0x0001 << chn)) == 0) {
1079                                 /* channel not in use. hiding it will trigger RouteTimeAxisView::automation_track_hidden()
1080                                    which will cause a redraw. We don't want one per channel, so block that with no_redraw.
1081                                  */
1082                                 changed = track->set_marked_for_display (false) || changed;
1083                         } else {
1084                                 changed = track->set_marked_for_display (true) || changed;
1085                         }
1086                 }
1087         }
1088
1089         no_redraw = false;
1090
1091         /* TODO: Bender, Pressure */
1092
1093         /* invalidate the controller menu, so that we rebuild it next time */
1094         _controller_menu_map.clear ();
1095         delete controller_menu;
1096         controller_menu = 0;
1097
1098         if (changed) {
1099                 request_redraw ();
1100         }
1101 }
1102
1103 Gtk::CheckMenuItem*
1104 MidiTimeAxisView::automation_child_menu_item (Evoral::Parameter param)
1105 {
1106         Gtk::CheckMenuItem* m = RouteTimeAxisView::automation_child_menu_item (param);
1107         if (m) {
1108                 return m;
1109         }
1110
1111         ParameterMenuMap::iterator i = _controller_menu_map.find (param);
1112         if (i != _controller_menu_map.end()) {
1113                 return i->second;
1114         }
1115
1116         i = _channel_command_menu_map.find (param);
1117         if (i != _channel_command_menu_map.end()) {
1118                 return i->second;
1119         }
1120
1121         return 0;
1122 }
1123
1124 boost::shared_ptr<MidiRegion>
1125 MidiTimeAxisView::add_region (framepos_t pos, framecnt_t length, bool commit)
1126 {
1127         Editor* real_editor = dynamic_cast<Editor*> (&_editor);
1128
1129         real_editor->begin_reversible_command (Operations::create_region);
1130         playlist()->clear_changes ();
1131
1132         real_editor->snap_to (pos, 0);
1133
1134         boost::shared_ptr<Source> src = _session->create_midi_source_for_session (view()->trackview().track().get(),
1135                                                                                   view()->trackview().track()->name());
1136         PropertyList plist;
1137
1138         plist.add (ARDOUR::Properties::start, 0);
1139         plist.add (ARDOUR::Properties::length, length);
1140         plist.add (ARDOUR::Properties::name, PBD::basename_nosuffix(src->name()));
1141
1142         boost::shared_ptr<Region> region = (RegionFactory::create (src, plist));
1143
1144         playlist()->add_region (region, pos);
1145         _session->add_command (new StatefulDiffCommand (playlist()));
1146
1147         if (commit) {
1148                 real_editor->commit_reversible_command ();
1149         }
1150
1151         return boost::dynamic_pointer_cast<MidiRegion>(region);
1152 }
1153
1154 void
1155 MidiTimeAxisView::ensure_step_editor ()
1156 {
1157         if (!_step_editor) {
1158                 _step_editor = new StepEditor (_editor, midi_track(), *this);
1159         }
1160 }
1161
1162 void
1163 MidiTimeAxisView::start_step_editing ()
1164 {
1165         ensure_step_editor ();
1166         _step_editor->start_step_editing ();
1167
1168 }
1169 void
1170 MidiTimeAxisView::stop_step_editing ()
1171 {
1172         if (_step_editor) {
1173                 _step_editor->stop_step_editing ();
1174         }
1175 }
1176
1177
1178 /** @return channel (counted from 0) to add an event to, based on the current setting
1179  *  of the channel selector.
1180  */
1181 uint8_t
1182 MidiTimeAxisView::get_channel_for_add () const
1183 {
1184         uint16_t const chn_mask = _channel_selector.get_selected_channels ();
1185         int chn_cnt = 0;
1186         uint8_t channel = 0;
1187
1188         /* pick the highest selected channel, unless all channels are selected,
1189            which is interpreted to mean channel 1 (zero)
1190         */
1191
1192         for (uint16_t i = 0; i < 16; ++i) {
1193                 if (chn_mask & (1<<i)) {
1194                         channel = i;
1195                         chn_cnt++;
1196                 }
1197         }
1198
1199         if (chn_cnt == 16) {
1200                 channel = 0;
1201         }
1202
1203         return channel;
1204 }
1205
1206 void
1207 MidiTimeAxisView::note_range_changed ()
1208 {
1209         set_gui_property ("note-range-min", (int) midi_view()->lowest_note ());
1210         set_gui_property ("note-range-max", (int) midi_view()->highest_note ());
1211 }
1212