Make controller menu items look the same whether for one channel or many (#3779).
[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/file_source.h"
42 #include "ardour/midi_playlist.h"
43 #include "ardour/midi_diskstream.h"
44 #include "ardour/midi_patch_manager.h"
45 #include "ardour/midi_source.h"
46 #include "ardour/processor.h"
47 #include "ardour/ladspa_plugin.h"
48 #include "ardour/location.h"
49 #include "ardour/playlist.h"
50 #include "ardour/region_factory.h"
51 #include "ardour/session.h"
52 #include "ardour/session_playlist.h"
53 #include "ardour/tempo.h"
54 #include "ardour/utils.h"
55 #include "ardour/operations.h"
56
57 #include "midi++/names.h"
58
59 #include "add_midi_cc_track_dialog.h"
60 #include "ardour_ui.h"
61 #include "automation_line.h"
62 #include "automation_time_axis.h"
63 #include "canvas-note-event.h"
64 #include "canvas_impl.h"
65 #include "crossfade_view.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,
103                 boost::shared_ptr<Route> rt, Canvas& canvas)
104         : AxisView(sess) // virtually inherited
105         , RouteTimeAxisView(ed, sess, rt, 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         , _step_edit_item (0)
117         , _midi_thru_item (0)
118         , default_channel_menu (0)
119         , controller_menu (0)
120         , _step_editor (0)
121 {
122         subplugin_menu.set_name ("ArdourContextMenu");
123
124         _view = new MidiStreamView (*this);
125
126         ignore_toggle = false;
127         
128         mute_button->set_active (false);
129         solo_button->set_active (false);
130
131         if (is_midi_track()) {
132                 controls_ebox.set_name ("MidiTimeAxisViewControlsBaseUnselected");
133                 _note_mode = midi_track()->note_mode();
134         } else { // MIDI bus (which doesn't exist yet..)
135                 controls_ebox.set_name ("MidiBusControlsBaseUnselected");
136         }
137
138         /* map current state of the route */
139
140         processors_changed (RouteProcessorChange ());
141
142         ensure_xml_node ();
143
144         set_state (*xml_node, Stateful::loading_state_version);
145
146         _route->processors_changed.connect (*this, invalidator (*this), ui_bind (&MidiTimeAxisView::processors_changed, this, _1), gui_context());
147
148         if (is_track()) {
149                 _piano_roll_header = new PianoRollHeader(*midi_view());
150
151                 _piano_roll_header->AddNoteSelection.connect (sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection));
152                 _piano_roll_header->ExtendNoteSelection.connect (sigc::mem_fun (*this, &MidiTimeAxisView::extend_note_selection));
153                 _piano_roll_header->ToggleNoteSelection.connect (sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection));
154
155                 _range_scroomer = new MidiScroomer(midi_view()->note_range_adjustment);
156
157                 controls_hbox.pack_start(*_range_scroomer);
158                 controls_hbox.pack_start(*_piano_roll_header);
159
160                 controls_ebox.set_name ("MidiTrackControlsBaseUnselected");
161                 controls_base_selected_name = "MidiTrackControlsBaseSelected";
162                 controls_base_unselected_name = "MidiTrackControlsBaseUnselected";
163
164                 midi_view()->NoteRangeChanged.connect (sigc::mem_fun(*this, &MidiTimeAxisView::update_range));
165
166                 /* ask for notifications of any new RegionViews */
167                 _view->RegionViewAdded.connect (sigc::mem_fun(*this, &MidiTimeAxisView::region_view_added));
168                 
169                 if (!_editor.have_idled()) {
170                         /* first idle will do what we need */
171                 } else {
172                         first_idle ();
173                 }
174         }
175
176         HBox* midi_controls_hbox = manage(new HBox());
177
178         MIDI::Name::MidiPatchManager& patch_manager = MIDI::Name::MidiPatchManager::instance();
179
180         MIDI::Name::MasterDeviceNames::Models::const_iterator m = patch_manager.all_models().begin();
181         for (; m != patch_manager.all_models().end(); ++m) {
182                 _model_selector.append_text(m->c_str());
183         }
184
185         _model_selector.signal_changed().connect(sigc::mem_fun(*this, &MidiTimeAxisView::model_changed));
186
187         _custom_device_mode_selector.signal_changed().connect(
188                         sigc::mem_fun(*this, &MidiTimeAxisView::custom_device_mode_changed));
189
190         // TODO: persist the choice
191         // this initializes the comboboxes and sends out the signal
192         _model_selector.set_active(0);
193
194         midi_controls_hbox->pack_start(_channel_selector, true, false);
195         if (!patch_manager.all_models().empty()) {
196                 _midi_controls_box.pack_start(_model_selector, true, false);
197                 _midi_controls_box.pack_start(_custom_device_mode_selector, true, false);
198         }
199
200         _midi_controls_box.pack_start(*midi_controls_hbox, true, true);
201
202         controls_vbox.pack_start(_midi_controls_box, false, false);
203
204         // restore channel selector settings
205         _channel_selector.set_channel_mode(midi_track()->get_channel_mode(), midi_track()->get_channel_mask());
206         _channel_selector.mode_changed.connect(
207                 sigc::mem_fun(*midi_track(), &MidiTrack::set_channel_mode));
208         _channel_selector.mode_changed.connect(
209                 sigc::mem_fun(*this, &MidiTimeAxisView::set_channel_mode));
210
211         XMLProperty *prop;
212         if ((prop = xml_node->property ("color-mode")) != 0) {
213                 _color_mode = ColorMode (string_2_enum(prop->value(), _color_mode));
214                 if (_color_mode == ChannelColors) {
215                         _channel_selector.set_channel_colors(CanvasNoteEvent::midi_channel_colors);
216                 }
217         }
218
219         if ((prop = xml_node->property ("note-mode")) != 0) {
220                 _note_mode = NoteMode (string_2_enum(prop->value(), _note_mode));
221                 if (_percussion_mode_item) {
222                         _percussion_mode_item->set_active (_note_mode == Percussive);
223                 }
224         }
225 }
226
227 void
228 MidiTimeAxisView::first_idle ()
229 {
230         if (is_track ()) {
231                 _view->attach ();
232         }
233 }
234
235 MidiTimeAxisView::~MidiTimeAxisView ()
236 {
237         delete _piano_roll_header;
238         _piano_roll_header = 0;
239
240         delete _range_scroomer;
241         _range_scroomer = 0;
242
243         delete controller_menu;
244         delete _step_editor;
245 }
246
247 void
248 MidiTimeAxisView::check_step_edit ()
249 {
250         ensure_step_editor ();
251         _step_editor->check_step_edit ();
252 }
253
254 void 
255 MidiTimeAxisView::model_changed()
256 {
257         std::list<std::string> device_modes = MIDI::Name::MidiPatchManager::instance()
258                 .custom_device_mode_names_by_model(_model_selector.get_active_text());
259
260         _custom_device_mode_selector.clear_items();
261
262         for (std::list<std::string>::const_iterator i = device_modes.begin();
263                         i != device_modes.end(); ++i) {
264                 cerr << "found custom device mode " << *i << " thread_id: " << pthread_self() << endl;
265                 _custom_device_mode_selector.append_text(*i);
266         }
267
268         _custom_device_mode_selector.set_active(0);
269 }
270
271 void MidiTimeAxisView::custom_device_mode_changed()
272 {
273         _midi_patch_settings_changed.emit(_model_selector.get_active_text(),
274                         _custom_device_mode_selector.get_active_text());
275 }
276
277 MidiStreamView*
278 MidiTimeAxisView::midi_view()
279 {
280         return dynamic_cast<MidiStreamView*>(_view);
281 }
282
283 guint32
284 MidiTimeAxisView::show_at (double y, int& nth, Gtk::VBox *parent)
285 {
286         ensure_xml_node ();
287         xml_node->add_property ("shown-editor", "yes");
288
289         guint32 ret = TimeAxisView::show_at (y, nth, parent);
290         return ret;
291 }
292
293 void
294 MidiTimeAxisView::hide ()
295 {
296         ensure_xml_node ();
297         xml_node->add_property ("shown-editor", "no");
298
299         TimeAxisView::hide ();
300 }
301
302 void
303 MidiTimeAxisView::set_height (uint32_t h)
304 {
305         RouteTimeAxisView::set_height (h);
306
307         if (height >= MIDI_CONTROLS_BOX_MIN_HEIGHT) {
308                 _midi_controls_box.show_all ();
309         } else {
310                 _midi_controls_box.hide();
311         }
312
313         if (height >= KEYBOARD_MIN_HEIGHT) {
314                 if (is_track() && _range_scroomer)
315                         _range_scroomer->show();
316                 if (is_track() && _piano_roll_header)
317                         _piano_roll_header->show();
318         } else {
319                 if (is_track() && _range_scroomer)
320                         _range_scroomer->hide();
321                 if (is_track() && _piano_roll_header)
322                         _piano_roll_header->hide();
323         }
324 }
325
326 void
327 MidiTimeAxisView::append_extra_display_menu_items ()
328 {
329         using namespace Menu_Helpers;
330
331         MenuList& items = display_menu->items();
332
333         // Note range
334         Menu *range_menu = manage(new Menu);
335         MenuList& range_items = range_menu->items();
336         range_menu->set_name ("ArdourContextMenu");
337
338         range_items.push_back (MenuElem (_("Show Full Range"), sigc::bind (
339                         sigc::mem_fun(*this, &MidiTimeAxisView::set_note_range),
340                         MidiStreamView::FullRange)));
341
342         range_items.push_back (MenuElem (_("Fit Contents"), sigc::bind (
343                         sigc::mem_fun(*this, &MidiTimeAxisView::set_note_range),
344                         MidiStreamView::ContentsRange)));
345
346         items.push_back (MenuElem (_("Note range"), *range_menu));
347         items.push_back (MenuElem (_("Note mode"), *build_note_mode_menu()));
348         items.push_back (MenuElem (_("Default Channel"), *build_def_channel_menu()));
349
350         items.push_back (CheckMenuElem (_("MIDI Thru"), sigc::mem_fun(*this, &MidiTimeAxisView::toggle_midi_thru)));
351         _midi_thru_item = dynamic_cast<CheckMenuItem*>(&items.back());
352 }
353
354 Gtk::Menu*
355 MidiTimeAxisView::build_def_channel_menu ()
356 {
357         using namespace Menu_Helpers;
358
359         default_channel_menu = manage (new Menu ());
360
361         uint8_t defchn = midi_track()->default_channel();
362         MenuList& def_channel_items = default_channel_menu->items();
363         RadioMenuItem* item;
364         RadioMenuItem::Group dc_group;
365
366         for (int i = 0; i < 16; ++i) {
367                 char buf[4];
368                 snprintf (buf, sizeof (buf), "%d", i+1);
369
370                 def_channel_items.push_back (RadioMenuElem (dc_group, buf,
371                                                             sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_default_channel), i)));
372                 item = dynamic_cast<RadioMenuItem*>(&def_channel_items.back());
373                 item->set_active ((i == defchn));
374         }
375
376         return default_channel_menu;
377 }
378
379 void
380 MidiTimeAxisView::set_default_channel (int chn)
381 {
382         midi_track()->set_default_channel (chn);
383 }
384
385 void
386 MidiTimeAxisView::toggle_midi_thru ()
387 {
388         if (!_midi_thru_item) {
389                 return;
390         }
391
392         bool view_yn = _midi_thru_item->get_active();
393         if (view_yn != midi_track()->midi_thru()) {
394                 midi_track()->set_midi_thru (view_yn);
395         }
396 }
397
398 void
399 MidiTimeAxisView::build_automation_action_menu ()
400 {
401         using namespace Menu_Helpers;
402
403         /* If we have a controller menu, we need to detach it before
404            RouteTimeAxis::build_automation_action_menu destroys the
405            menu it is attached to.  Otherwise GTK destroys
406            controller_menu's gobj, meaning that it can't be reattached
407            below.  See bug #3134.
408         */
409            
410         if (controller_menu) {
411                 detach_menu (*controller_menu);
412         }
413
414         _channel_command_menu_map.clear ();
415         RouteTimeAxisView::build_automation_action_menu ();
416
417         MenuList& automation_items = automation_action_menu->items();
418         
419         uint16_t selected_channels = _channel_selector.get_selected_channels();
420
421         if (selected_channels !=  0) {
422
423                 automation_items.push_back (SeparatorElem());
424
425                 /* these 2 MIDI "command" types are semantically more like automation than note data,
426                    but they are not MIDI controllers. We give them special status in this menu, since
427                    they will not show up in the controller list and anyone who actually knows
428                    something about MIDI (!) would not expect to find them there.
429                 */
430
431                 add_channel_command_menu_item (automation_items, _("Bender"), MidiPitchBenderAutomation, 0);
432                 add_channel_command_menu_item (automation_items, _("Pressure"), MidiChannelPressureAutomation, 0);
433                 
434                 /* now all MIDI controllers. Always offer the possibility that we will rebuild the controllers menu
435                    since it might need to be updated after a channel mode change or other change. Also detach it
436                    first in case it has been used anywhere else.
437                 */
438                 
439                 build_controller_menu ();
440                 
441                 automation_items.push_back (SeparatorElem());
442                 automation_items.push_back (MenuElem (_("Controllers"), *controller_menu));
443         } else {
444                 automation_items.push_back (MenuElem (string_compose ("<i>%1</i>", _("No MIDI Channels selected"))));
445                 dynamic_cast<Label*> (automation_items.back().get_child())->set_use_markup (true);
446         }
447                 
448 }
449
450 void
451 MidiTimeAxisView::change_all_channel_tracks_visibility (bool yn, Evoral::Parameter param)
452 {
453         uint16_t selected_channels = _channel_selector.get_selected_channels();
454         
455         for (uint8_t chn = 0; chn < 16; chn++) {
456                 if (selected_channels & (0x0001 << chn)) {
457                         
458                         Evoral::Parameter fully_qualified_param (param.type(), chn, param.id());
459                         Gtk::CheckMenuItem* menu = automation_child_menu_item (fully_qualified_param);
460
461                         if (menu) {
462                                 menu->set_active (yn);
463                         }
464                 }
465         }
466 }
467
468 void
469 MidiTimeAxisView::add_channel_command_menu_item (Menu_Helpers::MenuList& items, const string& label, AutomationType auto_type, uint8_t cmd)
470 {
471         using namespace Menu_Helpers;
472
473         /* count the number of selected channels because we will build a different menu structure if there is more than 1 selected.
474          */
475
476         uint16_t selected_channels = _channel_selector.get_selected_channels();
477         int chn_cnt = 0;
478         
479         for (uint8_t chn = 0; chn < 16; chn++) {
480                 if (selected_channels & (0x0001 << chn)) {
481                         if (++chn_cnt > 1) {
482                                 break;
483                         }
484                 }
485         }
486         
487         if (chn_cnt > 1) {
488                 
489                 /* multiple channels - create a submenu, with 1 item per channel */
490                 
491                 Menu* chn_menu = manage (new Menu);
492                 MenuList& chn_items (chn_menu->items());
493                 Evoral::Parameter param_without_channel (auto_type, 0, cmd);
494
495                 /* add a couple of items to hide/show all of them */
496
497                 chn_items.push_back (MenuElem (_("Hide all channels"),
498                                                     sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility), 
499                                                                 false, param_without_channel)));
500                 chn_items.push_back (MenuElem (_("Show all channels"),
501                                                     sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility), 
502                                                                 true, param_without_channel)));
503                 
504                 for (uint8_t chn = 0; chn < 16; chn++) {
505                         if (selected_channels & (0x0001 << chn)) {
506                                 
507                                 /* for each selected channel, add a menu item for this controller */
508                                 
509                                 Evoral::Parameter fully_qualified_param (auto_type, chn, cmd);
510                                 chn_items.push_back (CheckMenuElem (string_compose (_("Channel %1"), chn+1),
511                                                                     sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
512                                                                                 fully_qualified_param)));
513                                 
514                                 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
515                                 bool visible = false;
516                                 
517                                 if (track) {
518                                         if (track->marked_for_display()) {
519                                                 visible = true;
520                                         }
521                                 }
522
523                                 CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&chn_items.back());
524                                 _channel_command_menu_map[fully_qualified_param] = cmi;
525                                 cmi->set_active (visible);
526                         }
527                 }
528                 
529                 /* now create an item in the parent menu that has the per-channel list as a submenu */
530                         
531                 items.push_back (MenuElem (label, *chn_menu));
532                 
533         } else {
534                 
535                 /* just one channel - create a single menu item for this command+channel combination*/
536                 
537                 for (uint8_t chn = 0; chn < 16; chn++) {
538                         if (selected_channels & (0x0001 << chn)) {
539                                 
540                                 Evoral::Parameter fully_qualified_param (auto_type, chn, cmd);
541                                 items.push_back (CheckMenuElem (label,
542                                                                 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
543                                                                             fully_qualified_param)));
544                                 
545                                 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
546                                 bool visible = false;
547                                 
548                                 if (track) {
549                                         if (track->marked_for_display()) {
550                                                 visible = true;
551                                         }
552                                 }
553                                 
554                                 CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&items.back());
555                                 _channel_command_menu_map[fully_qualified_param] = cmi;
556                                 cmi->set_active (visible);
557                                 
558                                 /* one channel only */
559                                 break;
560                         }
561                 }
562         }
563 }
564
565 void
566 MidiTimeAxisView::build_controller_menu ()
567 {
568         using namespace Menu_Helpers;
569
570         if (controller_menu) {
571                 /* it exists and has not been invalidated by a channel mode change, so just return it */
572                 return;
573         }
574
575         controller_menu = new Menu; // explicitly managed by us
576         MenuList& items (controller_menu->items());
577
578         /* create several "top level" menu items for sets of controllers (16 at a time), and populate each one with a submenu 
579            for each controller+channel combination covering the currently selected channels for this track
580         */
581
582         uint16_t selected_channels = _channel_selector.get_selected_channels();
583
584         /* count the number of selected channels because we will build a different menu structure if there is more than 1 selected.
585          */
586
587         int chn_cnt = 0;
588         
589         for (uint8_t chn = 0; chn < 16; chn++) {
590                 if (selected_channels & (0x0001 << chn)) {
591                         if (++chn_cnt > 1) {
592                                 break;
593                         }
594                 }
595         }
596         
597         /* loop over all 127 MIDI controllers, in groups of 16; except don't offer
598            bank select controllers, as they are handled by the `patch' code */
599
600         for (int i = 0; i < 127; i += 16) {
601
602                 Menu* ctl_menu = manage (new Menu);
603                 MenuList& ctl_items (ctl_menu->items());
604
605
606                 /* for each controller, consider whether to create a submenu or a single item */
607
608                 for (int ctl = i; ctl < i+16; ++ctl) {
609
610                         if (ctl == MIDI_CTL_MSB_BANK || ctl == MIDI_CTL_LSB_BANK) {
611                                 continue;
612                         }
613
614                         if (chn_cnt > 1) {
615
616                                 /* multiple channels - create a submenu, with 1 item per channel */
617
618                                 Menu* chn_menu = manage (new Menu);
619                                 MenuList& chn_items (chn_menu->items());
620
621                                 /* add a couple of items to hide/show this controller on all channels */
622                                 
623                                 Evoral::Parameter param_without_channel (MidiCCAutomation, 0, ctl);
624                                 chn_items.push_back (MenuElem (_("Hide all channels"),
625                                                                     sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility), 
626                                                                                 false, param_without_channel)));
627                                 chn_items.push_back (MenuElem (_("Show all channels"),
628                                                                     sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility), 
629                                                                                 true, param_without_channel)));
630                 
631                                 for (uint8_t chn = 0; chn < 16; chn++) {
632                                         if (selected_channels & (0x0001 << chn)) {
633                                                 
634                                                 /* for each selected channel, add a menu item for this controller */
635                                                 
636                                                 Evoral::Parameter fully_qualified_param (MidiCCAutomation, chn, ctl);
637                                                 chn_items.push_back (CheckMenuElem (string_compose (_("Channel %1"), chn+1),
638                                                                                     sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
639                                                                                                 fully_qualified_param)));
640
641                                                 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
642                                                 bool visible = false;
643                                                 
644                                                 if (track) {
645                                                         if (track->marked_for_display()) {
646                                                                 visible = true;
647                                                         }
648                                                 }
649                                                 
650                                                 CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&chn_items.back());
651                                                 _controller_menu_map[fully_qualified_param] = cmi;
652                                                 cmi->set_active (visible);
653                                         }
654                                 }
655                                 
656                                 /* add the per-channel menu to the list of controllers, with the name of the controller */
657                                 ctl_items.push_back (MenuElem (string_compose ("<b>%1</b>: %2", ctl, midi_name (ctl)), *chn_menu));
658                                 dynamic_cast<Label*> (ctl_items.back().get_child())->set_use_markup (true);
659                                       
660                         } else {
661
662                                 /* just one channel - create a single menu item for this ctl+channel combination*/
663
664                                 for (uint8_t chn = 0; chn < 16; chn++) {
665                                         if (selected_channels & (0x0001 << chn)) {
666                                                 
667                                                 Evoral::Parameter fully_qualified_param (MidiCCAutomation, chn, ctl);
668                                                 ctl_items.push_back (
669                                                         CheckMenuElem (
670                                                                 string_compose ("<b>%1</b>: %2 [%3]", ctl, midi_name (ctl), int (chn)),
671                                                                 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
672                                                                             fully_qualified_param)
673                                                                 )
674                                                         );
675                                                 dynamic_cast<Label*> (ctl_items.back().get_child())->set_use_markup (true);
676                                                 
677                                                 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
678                                                 bool visible = false;
679                                                 
680                                                 if (track) {
681                                                         if (track->marked_for_display()) {
682                                                                 visible = true;
683                                                         }
684                                                 }
685                                                 
686                                                 CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&ctl_items.back());
687                                                 _controller_menu_map[fully_qualified_param] = cmi;
688                                                 cmi->set_active (visible);
689                                                 
690                                                 /* one channel only */
691                                                 break;
692                                         }
693                                 }
694                         }
695                 }
696                         
697                 /* add the menu for this block of controllers to the overall controller menu */
698
699                 items.push_back (MenuElem (string_compose (_("Controllers %1-%2"), i, i+15), *ctl_menu));
700         }
701 }
702
703 Gtk::Menu*
704 MidiTimeAxisView::build_note_mode_menu()
705 {
706         using namespace Menu_Helpers;
707
708         Menu* mode_menu = manage (new Menu);
709         MenuList& items = mode_menu->items();
710         mode_menu->set_name ("ArdourContextMenu");
711
712         RadioMenuItem::Group mode_group;
713         items.push_back (RadioMenuElem (mode_group, _("Sustained"),
714                                 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_mode), Sustained)));
715         _note_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
716         _note_mode_item->set_active(_note_mode == Sustained);
717
718         items.push_back (RadioMenuElem (mode_group, _("Percussive"),
719                                 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_mode), Percussive)));
720         _percussion_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
721         _percussion_mode_item->set_active(_note_mode == Percussive);
722
723         return mode_menu;
724 }
725
726 Gtk::Menu*
727 MidiTimeAxisView::build_color_mode_menu()
728 {
729         using namespace Menu_Helpers;
730
731         Menu* mode_menu = manage (new Menu);
732         MenuList& items = mode_menu->items();
733         mode_menu->set_name ("ArdourContextMenu");
734
735         RadioMenuItem::Group mode_group;
736         items.push_back (RadioMenuElem (mode_group, _("Meter Colors"),
737                                 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode), MeterColors)));
738         _meter_color_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
739         _meter_color_mode_item->set_active(_color_mode == MeterColors);
740
741         items.push_back (RadioMenuElem (mode_group, _("Channel Colors"),
742                                 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode), ChannelColors)));
743         _channel_color_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
744         _channel_color_mode_item->set_active(_color_mode == ChannelColors);
745
746         items.push_back (RadioMenuElem (mode_group, _("Track Color"),
747                                 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode), TrackColor)));
748         _channel_color_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
749         _channel_color_mode_item->set_active(_color_mode == TrackColor);
750
751         return mode_menu;
752 }
753
754 void
755 MidiTimeAxisView::set_note_mode(NoteMode mode)
756 {
757         if (_note_mode != mode || midi_track()->note_mode() != mode) {
758                 _note_mode = mode;
759                 midi_track()->set_note_mode(mode);
760                 xml_node->add_property ("note-mode", enum_2_string(_note_mode));
761                 _view->redisplay_track();
762         }
763 }
764
765 void
766 MidiTimeAxisView::set_color_mode(ColorMode mode)
767 {
768         if (_color_mode != mode) {
769                 if (mode == ChannelColors) {
770                         _channel_selector.set_channel_colors(CanvasNoteEvent::midi_channel_colors);
771                 } else {
772                         _channel_selector.set_default_channel_color();
773                 }
774
775                 _color_mode = mode;
776                 xml_node->add_property ("color-mode", enum_2_string(_color_mode));
777                 _view->redisplay_track();
778         }
779 }
780
781 void
782 MidiTimeAxisView::set_note_range(MidiStreamView::VisibleNoteRange range)
783 {
784         if (!_ignore_signals)
785                 midi_view()->set_note_range(range);
786 }
787
788
789 void
790 MidiTimeAxisView::update_range()
791 {
792         MidiGhostRegion* mgr;
793
794         for(list<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
795                 if ((mgr = dynamic_cast<MidiGhostRegion*>(*i)) != 0) {
796                         mgr->update_range();
797                 }
798         }
799 }
800
801 void
802 MidiTimeAxisView::show_all_automation ()
803 {
804         if (midi_track()) {
805                 const set<Evoral::Parameter> params = midi_track()->midi_playlist()->contained_automation();
806
807                 for (set<Evoral::Parameter>::const_iterator i = params.begin(); i != params.end(); ++i) {
808                         create_automation_child(*i, true);
809                 }
810         }
811
812         RouteTimeAxisView::show_all_automation ();
813 }
814
815 void
816 MidiTimeAxisView::show_existing_automation ()
817 {
818         if (midi_track()) {
819                 const set<Evoral::Parameter> params = midi_track()->midi_playlist()->contained_automation();
820
821                 for (set<Evoral::Parameter>::const_iterator i = params.begin(); i != params.end(); ++i) {
822                         create_automation_child(*i, true);
823                 }
824         }
825
826         RouteTimeAxisView::show_existing_automation ();
827 }
828
829 /** Create an automation track for the given parameter (pitch bend, channel pressure).
830  */
831 void
832 MidiTimeAxisView::create_automation_child (const Evoral::Parameter& param, bool show)
833 {
834         if (param.type() == NullAutomation) {
835                 cerr << "WARNING: Attempt to create NullAutomation child, ignoring" << endl;
836                 return;
837         }
838         
839         AutomationTracks::iterator existing = _automation_tracks.find (param);
840         if (existing != _automation_tracks.end()) {
841                 return;
842         }
843                 
844         if (param.type() == GainAutomation) {
845                 create_gain_automation_child (param, show);
846         } else {
847                 
848                 /* These controllers are region "automation", so we do not create
849                  * an AutomationList/Line for the track */
850                 
851                 boost::shared_ptr<AutomationTimeAxisView> track (
852                         new AutomationTimeAxisView (
853                                 _session,
854                                 _route, 
855                                 boost::shared_ptr<Automatable> (),
856                                 boost::shared_ptr<AutomationControl> (),
857                                 param,
858                                 _editor,
859                                 *this,
860                                 true,
861                                 parent_canvas,
862                                 _route->describe_parameter(param)
863                                 )
864                         );
865                 
866                 add_automation_child (param, track, show);
867         }
868 }
869
870
871 void
872 MidiTimeAxisView::route_active_changed ()
873 {
874         RouteUI::route_active_changed ();
875
876         if (is_track()) {
877                 if (_route->active()) {
878                         controls_ebox.set_name ("MidiTrackControlsBaseUnselected");
879                         controls_base_selected_name = "MidiTrackControlsBaseSelected";
880                         controls_base_unselected_name = "MidiTrackControlsBaseUnselected";
881                 } else {
882                         controls_ebox.set_name ("MidiTrackControlsBaseInactiveUnselected");
883                         controls_base_selected_name = "MidiTrackControlsBaseInactiveSelected";
884                         controls_base_unselected_name = "MidiTrackControlsBaseInactiveUnselected";
885                 }
886         } else {
887
888                 throw; // wha?
889
890                 if (_route->active()) {
891                         controls_ebox.set_name ("BusControlsBaseUnselected");
892                         controls_base_selected_name = "BusControlsBaseSelected";
893                         controls_base_unselected_name = "BusControlsBaseUnselected";
894                 } else {
895                         controls_ebox.set_name ("BusControlsBaseInactiveUnselected");
896                         controls_base_selected_name = "BusControlsBaseInactiveSelected";
897                         controls_base_unselected_name = "BusControlsBaseInactiveUnselected";
898                 }
899         }
900 }
901
902
903
904 void
905 MidiTimeAxisView::add_note_selection (uint8_t note)
906 {
907         if (!_editor.internal_editing()) {
908                 return;
909         }
910
911         uint16_t chn_mask = _channel_selector.get_selected_channels();
912
913         if (_view->num_selected_regionviews() == 0) {
914                 _view->foreach_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection_region_view), note, chn_mask));
915         } else {
916                 _view->foreach_selected_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection_region_view), note, chn_mask));
917         }
918 }
919
920 void
921 MidiTimeAxisView::extend_note_selection (uint8_t note)
922 {
923         if (!_editor.internal_editing()) {
924                 return;
925         }
926
927         uint16_t chn_mask = _channel_selector.get_selected_channels();
928
929         if (_view->num_selected_regionviews() == 0) {
930                 _view->foreach_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::extend_note_selection_region_view), note, chn_mask));
931         } else {
932                 _view->foreach_selected_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::extend_note_selection_region_view), note, chn_mask));
933         }
934 }
935
936 void
937 MidiTimeAxisView::toggle_note_selection (uint8_t note)
938 {
939         if (!_editor.internal_editing()) {
940                 return;
941         }
942
943         uint16_t chn_mask = _channel_selector.get_selected_channels();
944
945         if (_view->num_selected_regionviews() == 0) {
946                 _view->foreach_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection_region_view), note, chn_mask));
947         } else {
948                 _view->foreach_selected_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection_region_view), note, chn_mask));
949         }
950 }
951
952 void
953 MidiTimeAxisView::add_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
954 {
955         dynamic_cast<MidiRegionView*>(rv)->select_matching_notes (note, chn_mask, false, false);
956 }
957
958 void
959 MidiTimeAxisView::extend_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
960 {
961         dynamic_cast<MidiRegionView*>(rv)->select_matching_notes (note, chn_mask, true, true);
962 }
963
964 void
965 MidiTimeAxisView::toggle_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
966 {
967         dynamic_cast<MidiRegionView*>(rv)->toggle_matching_notes (note, chn_mask);
968 }
969
970 void
971 MidiTimeAxisView::set_channel_mode (ChannelMode, uint16_t)
972 {
973         /* hide all automation tracks that use the wrong channel(s) and show all those that use
974            the right ones.
975         */
976
977         uint16_t selected_channels = _channel_selector.get_selected_channels();
978         bool changed = false;
979
980         no_redraw = true;
981
982         for (uint32_t ctl = 0; ctl < 127; ++ctl) {
983
984                 for (uint32_t chn = 0; chn < 16; ++chn) {
985                         Evoral::Parameter fully_qualified_param (MidiCCAutomation, chn, ctl);
986                         boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
987
988                         if (!track) {
989                                 continue;
990                         }
991                         
992                         if ((selected_channels & (0x0001 << chn)) == 0) {
993                                 /* channel not in use. hiding it will trigger RouteTimeAxisView::automation_track_hidden() 
994                                    which will cause a redraw. We don't want one per channel, so block that with no_redraw.
995                                  */
996                                 changed = track->set_visibility (false) || changed;
997                         } else {
998                                 changed = track->set_visibility (true) || changed;
999                         }
1000                 }
1001         }
1002
1003         no_redraw = false;
1004
1005         /* TODO: Bender, Pressure */
1006
1007         /* invalidate the controller menu, so that we rebuild it next time */
1008         _controller_menu_map.clear ();
1009         delete controller_menu;
1010         controller_menu = 0;
1011
1012         if (changed) {
1013                 _route->gui_changed ("track_height", this);
1014         }
1015 }
1016
1017 Gtk::CheckMenuItem*
1018 MidiTimeAxisView::automation_child_menu_item (Evoral::Parameter param)
1019 {
1020         Gtk::CheckMenuItem* m = RouteTimeAxisView::automation_child_menu_item (param);
1021         if (m) {
1022                 return m;
1023         }
1024
1025         ParameterMenuMap::iterator i = _controller_menu_map.find (param);
1026         if (i != _controller_menu_map.end()) {
1027                 return i->second;
1028         }
1029
1030         i = _channel_command_menu_map.find (param);
1031         if (i != _channel_command_menu_map.end()) {
1032                 return i->second;
1033         }
1034
1035         return 0;
1036 }
1037
1038 boost::shared_ptr<MidiRegion>
1039 MidiTimeAxisView::add_region (framepos_t pos, framecnt_t length, bool commit)
1040 {
1041         Editor* real_editor = dynamic_cast<Editor*> (&_editor);
1042
1043         real_editor->begin_reversible_command (Operations::create_region);
1044         playlist()->clear_changes ();
1045
1046         real_editor->snap_to (pos, 0);
1047         
1048         boost::shared_ptr<Source> src = _session->create_midi_source_for_session (view()->trackview().track().get(),
1049                                                                                   view()->trackview().track()->name());
1050         PropertyList plist; 
1051         
1052         plist.add (ARDOUR::Properties::start, 0);
1053         plist.add (ARDOUR::Properties::length, length);
1054         plist.add (ARDOUR::Properties::name, PBD::basename_nosuffix(src->name()));
1055         
1056         boost::shared_ptr<Region> region = (RegionFactory::create (src, plist));
1057         
1058         playlist()->add_region (region, pos);
1059         _session->add_command (new StatefulDiffCommand (playlist()));
1060
1061         if (commit) {
1062                 real_editor->commit_reversible_command ();
1063         }
1064
1065         return boost::dynamic_pointer_cast<MidiRegion>(region);
1066 }
1067
1068 void
1069 MidiTimeAxisView::ensure_step_editor ()
1070 {
1071         if (!_step_editor) {
1072                 _step_editor = new StepEditor (_editor, midi_track(), *this);
1073         }
1074 }
1075
1076 void 
1077 MidiTimeAxisView::start_step_editing ()
1078 {
1079         ensure_step_editor ();
1080         _step_editor->start_step_editing ();
1081
1082 }
1083 void 
1084 MidiTimeAxisView::stop_step_editing ()
1085 {
1086         if (_step_editor) {
1087                 _step_editor->stop_step_editing ();
1088         }
1089 }