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