84288db326ab7bf48f548e9cfef8c3dbe6354842
[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         }
446                 
447 }
448
449 void
450 MidiTimeAxisView::change_all_channel_tracks_visibility (bool yn, Evoral::Parameter param)
451 {
452         uint16_t selected_channels = _channel_selector.get_selected_channels();
453         
454         for (uint8_t chn = 0; chn < 16; chn++) {
455                 if (selected_channels & (0x0001 << chn)) {
456                         
457                         Evoral::Parameter fully_qualified_param (param.type(), chn, param.id());
458                         Gtk::CheckMenuItem* menu = automation_child_menu_item (fully_qualified_param);
459
460                         if (menu) {
461                                 menu->set_active (yn);
462                         }
463                 }
464         }
465 }
466
467 void
468 MidiTimeAxisView::add_channel_command_menu_item (Menu_Helpers::MenuList& items, const string& label, AutomationType auto_type, uint8_t cmd)
469 {
470         using namespace Menu_Helpers;
471
472         /* count the number of selected channels because we will build a different menu structure if there is more than 1 selected.
473          */
474
475         uint16_t selected_channels = _channel_selector.get_selected_channels();
476         int chn_cnt = 0;
477         
478         for (uint8_t chn = 0; chn < 16; chn++) {
479                 if (selected_channels & (0x0001 << chn)) {
480                         if (++chn_cnt > 1) {
481                                 break;
482                         }
483                 }
484         }
485         
486         if (chn_cnt > 1) {
487                 
488                 /* multiple channels - create a submenu, with 1 item per channel */
489                 
490                 Menu* chn_menu = manage (new Menu);
491                 MenuList& chn_items (chn_menu->items());
492                 Evoral::Parameter param_without_channel (auto_type, 0, cmd);
493
494                 /* add a couple of items to hide/show all of them */
495
496                 chn_items.push_back (MenuElem (_("Hide all channels"),
497                                                     sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility), 
498                                                                 false, param_without_channel)));
499                 chn_items.push_back (MenuElem (_("Show all channels"),
500                                                     sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility), 
501                                                                 true, param_without_channel)));
502                 
503                 for (uint8_t chn = 0; chn < 16; chn++) {
504                         if (selected_channels & (0x0001 << chn)) {
505                                 
506                                 /* for each selected channel, add a menu item for this controller */
507                                 
508                                 Evoral::Parameter fully_qualified_param (auto_type, chn, cmd);
509                                 chn_items.push_back (CheckMenuElem (string_compose (_("Channel %1"), chn+1),
510                                                                     sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
511                                                                                 fully_qualified_param)));
512                                 
513                                 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
514                                 bool visible = false;
515                                 
516                                 if (track) {
517                                         if (track->marked_for_display()) {
518                                                 visible = true;
519                                         }
520                                 }
521
522                                 CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&chn_items.back());
523                                 _channel_command_menu_map[fully_qualified_param] = cmi;
524                                 cmi->set_active (visible);
525                         }
526                 }
527                 
528                 /* now create an item in the parent menu that has the per-channel list as a submenu */
529                         
530                 items.push_back (MenuElem (label, *chn_menu));
531                 
532         } else {
533                 
534                 /* just one channel - create a single menu item for this command+channel combination*/
535                 
536                 for (uint8_t chn = 0; chn < 16; chn++) {
537                         if (selected_channels & (0x0001 << chn)) {
538                                 
539                                 Evoral::Parameter fully_qualified_param (auto_type, chn, cmd);
540                                 items.push_back (CheckMenuElem (label,
541                                                                 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
542                                                                             fully_qualified_param)));
543                                 
544                                 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
545                                 bool visible = false;
546                                 
547                                 if (track) {
548                                         if (track->marked_for_display()) {
549                                                 visible = true;
550                                         }
551                                 }
552                                 
553                                 CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&items.back());
554                                 _channel_command_menu_map[fully_qualified_param] = cmi;
555                                 cmi->set_active (visible);
556                                 
557                                 /* one channel only */
558                                 break;
559                         }
560                 }
561         }
562 }
563
564 void
565 MidiTimeAxisView::build_controller_menu ()
566 {
567         using namespace Menu_Helpers;
568
569         if (controller_menu) {
570                 /* it exists and has not been invalidated by a channel mode change, so just return it */
571                 return;
572         }
573
574         controller_menu = new Menu; // explicitly managed by us
575         MenuList& items (controller_menu->items());
576
577         /* create several "top level" menu items for sets of controllers (16 at a time), and populate each one with a submenu 
578            for each controller+channel combination covering the currently selected channels for this track
579         */
580
581         uint16_t selected_channels = _channel_selector.get_selected_channels();
582
583         /* count the number of selected channels because we will build a different menu structure if there is more than 1 selected.
584          */
585
586         int chn_cnt = 0;
587         
588         for (uint8_t chn = 0; chn < 16; chn++) {
589                 if (selected_channels & (0x0001 << chn)) {
590                         if (++chn_cnt > 1) {
591                                 break;
592                         }
593                 }
594         }
595         
596         /* loop over all 127 MIDI controllers, in groups of 16; except don't offer
597            bank select controllers, as they are handled by the `patch' code */
598
599         for (int i = 0; i < 127; i += 16) {
600
601                 Menu* ctl_menu = manage (new Menu);
602                 MenuList& ctl_items (ctl_menu->items());
603
604
605                 /* for each controller, consider whether to create a submenu or a single item */
606
607                 for (int ctl = i; ctl < i+16; ++ctl) {
608
609                         if (ctl == MIDI_CTL_MSB_BANK || ctl == MIDI_CTL_LSB_BANK) {
610                                 continue;
611                         }
612
613                         if (chn_cnt > 1) {
614
615                                 /* multiple channels - create a submenu, with 1 item per channel */
616
617                                 Menu* chn_menu = manage (new Menu);
618                                 MenuList& chn_items (chn_menu->items());
619
620                                 /* add a couple of items to hide/show this controller on all channels */
621                                 
622                                 Evoral::Parameter param_without_channel (MidiCCAutomation, 0, ctl);
623                                 chn_items.push_back (MenuElem (_("Hide all channels"),
624                                                                     sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility), 
625                                                                                 false, param_without_channel)));
626                                 chn_items.push_back (MenuElem (_("Show all channels"),
627                                                                     sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility), 
628                                                                                 true, param_without_channel)));
629                 
630                                 for (uint8_t chn = 0; chn < 16; chn++) {
631                                         if (selected_channels & (0x0001 << chn)) {
632                                                 
633                                                 /* for each selected channel, add a menu item for this controller */
634                                                 
635                                                 Evoral::Parameter fully_qualified_param (MidiCCAutomation, chn, ctl);
636                                                 chn_items.push_back (CheckMenuElem (string_compose (_("Channel %1"), chn+1),
637                                                                                     sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
638                                                                                                 fully_qualified_param)));
639
640                                                 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
641                                                 bool visible = false;
642                                                 
643                                                 if (track) {
644                                                         if (track->marked_for_display()) {
645                                                                 visible = true;
646                                                         }
647                                                 }
648                                                 
649                                                 CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&chn_items.back());
650                                                 _controller_menu_map[fully_qualified_param] = cmi;
651                                                 cmi->set_active (visible);
652                                         }
653                                 }
654                                 
655                                 /* add the per-channel menu to the list of controllers, with the name of the controller */
656                                 ctl_items.push_back (MenuElem (string_compose ("<b>%1</b>: %2", ctl, midi_name (ctl)), *chn_menu));
657                                 dynamic_cast<Label*> (ctl_items.back().get_child())->set_use_markup (true);
658                                       
659                         } else {
660
661                                 /* just one channel - create a single menu item for this ctl+channel combination*/
662
663                                 for (uint8_t chn = 0; chn < 16; chn++) {
664                                         if (selected_channels & (0x0001 << chn)) {
665                                                 
666                                                 Evoral::Parameter fully_qualified_param (MidiCCAutomation, chn, ctl);
667                                                 ctl_items.push_back (CheckMenuElem (_route->describe_parameter (fully_qualified_param),
668                                                                                     sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
669                                                                                                 fully_qualified_param)));
670                                                 
671                                                 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
672                                                 bool visible = false;
673                                                 
674                                                 if (track) {
675                                                         if (track->marked_for_display()) {
676                                                                 visible = true;
677                                                         }
678                                                 }
679                                                 
680                                                 CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&ctl_items.back());
681                                                 _controller_menu_map[fully_qualified_param] = cmi;
682                                                 cmi->set_active (visible);
683                                                 
684                                                 /* one channel only */
685                                                 break;
686                                         }
687                                 }
688                         }
689                 }
690                         
691                 /* add the menu for this block of controllers to the overall controller menu */
692
693                 items.push_back (MenuElem (string_compose (_("Controllers %1-%2"), i, i+15), *ctl_menu));
694         }
695 }
696
697 Gtk::Menu*
698 MidiTimeAxisView::build_note_mode_menu()
699 {
700         using namespace Menu_Helpers;
701
702         Menu* mode_menu = manage (new Menu);
703         MenuList& items = mode_menu->items();
704         mode_menu->set_name ("ArdourContextMenu");
705
706         RadioMenuItem::Group mode_group;
707         items.push_back (RadioMenuElem (mode_group, _("Sustained"),
708                                 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_mode), Sustained)));
709         _note_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
710         _note_mode_item->set_active(_note_mode == Sustained);
711
712         items.push_back (RadioMenuElem (mode_group, _("Percussive"),
713                                 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_mode), Percussive)));
714         _percussion_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
715         _percussion_mode_item->set_active(_note_mode == Percussive);
716
717         return mode_menu;
718 }
719
720 Gtk::Menu*
721 MidiTimeAxisView::build_color_mode_menu()
722 {
723         using namespace Menu_Helpers;
724
725         Menu* mode_menu = manage (new Menu);
726         MenuList& items = mode_menu->items();
727         mode_menu->set_name ("ArdourContextMenu");
728
729         RadioMenuItem::Group mode_group;
730         items.push_back (RadioMenuElem (mode_group, _("Meter Colors"),
731                                 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode), MeterColors)));
732         _meter_color_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
733         _meter_color_mode_item->set_active(_color_mode == MeterColors);
734
735         items.push_back (RadioMenuElem (mode_group, _("Channel Colors"),
736                                 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode), ChannelColors)));
737         _channel_color_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
738         _channel_color_mode_item->set_active(_color_mode == ChannelColors);
739
740         items.push_back (RadioMenuElem (mode_group, _("Track Color"),
741                                 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode), TrackColor)));
742         _channel_color_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
743         _channel_color_mode_item->set_active(_color_mode == TrackColor);
744
745         return mode_menu;
746 }
747
748 void
749 MidiTimeAxisView::set_note_mode(NoteMode mode)
750 {
751         if (_note_mode != mode || midi_track()->note_mode() != mode) {
752                 _note_mode = mode;
753                 midi_track()->set_note_mode(mode);
754                 xml_node->add_property ("note-mode", enum_2_string(_note_mode));
755                 _view->redisplay_track();
756         }
757 }
758
759 void
760 MidiTimeAxisView::set_color_mode(ColorMode mode)
761 {
762         if (_color_mode != mode) {
763                 if (mode == ChannelColors) {
764                         _channel_selector.set_channel_colors(CanvasNoteEvent::midi_channel_colors);
765                 } else {
766                         _channel_selector.set_default_channel_color();
767                 }
768
769                 _color_mode = mode;
770                 xml_node->add_property ("color-mode", enum_2_string(_color_mode));
771                 _view->redisplay_track();
772         }
773 }
774
775 void
776 MidiTimeAxisView::set_note_range(MidiStreamView::VisibleNoteRange range)
777 {
778         if (!_ignore_signals)
779                 midi_view()->set_note_range(range);
780 }
781
782
783 void
784 MidiTimeAxisView::update_range()
785 {
786         MidiGhostRegion* mgr;
787
788         for(list<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
789                 if ((mgr = dynamic_cast<MidiGhostRegion*>(*i)) != 0) {
790                         mgr->update_range();
791                 }
792         }
793 }
794
795 void
796 MidiTimeAxisView::show_all_automation ()
797 {
798         if (midi_track()) {
799                 const set<Evoral::Parameter> params = midi_track()->midi_playlist()->contained_automation();
800
801                 for (set<Evoral::Parameter>::const_iterator i = params.begin(); i != params.end(); ++i) {
802                         create_automation_child(*i, true);
803                 }
804         }
805
806         RouteTimeAxisView::show_all_automation ();
807 }
808
809 void
810 MidiTimeAxisView::show_existing_automation ()
811 {
812         if (midi_track()) {
813                 const set<Evoral::Parameter> params = midi_track()->midi_playlist()->contained_automation();
814
815                 for (set<Evoral::Parameter>::const_iterator i = params.begin(); i != params.end(); ++i) {
816                         create_automation_child(*i, true);
817                 }
818         }
819
820         RouteTimeAxisView::show_existing_automation ();
821 }
822
823 /** Create an automation track for the given parameter (pitch bend, channel pressure).
824  */
825 void
826 MidiTimeAxisView::create_automation_child (const Evoral::Parameter& param, bool show)
827 {
828         if (param.type() == NullAutomation) {
829                 cerr << "WARNING: Attempt to create NullAutomation child, ignoring" << endl;
830                 return;
831         }
832         
833         AutomationTracks::iterator existing = _automation_tracks.find (param);
834         if (existing != _automation_tracks.end()) {
835                 return;
836         }
837                 
838         if (param.type() == GainAutomation) {
839                 create_gain_automation_child (param, show);
840         } else {
841                 
842                 /* These controllers are region "automation", so we do not create
843                  * an AutomationList/Line for the track */
844                 
845                 boost::shared_ptr<AutomationControl> c = _route->get_control (param);
846                 assert (c);
847                 
848                 boost::shared_ptr<AutomationTimeAxisView> track (
849                         new AutomationTimeAxisView (
850                                 _session,
851                                 _route, 
852                                 _route,
853                                 c,
854                                 _editor,
855                                 *this,
856                                 true,
857                                 parent_canvas,
858                                 _route->describe_parameter(param)
859                                 )
860                         );
861                 
862                 add_automation_child (param, track, show);
863         }
864 }
865
866
867 void
868 MidiTimeAxisView::route_active_changed ()
869 {
870         RouteUI::route_active_changed ();
871
872         if (is_track()) {
873                 if (_route->active()) {
874                         controls_ebox.set_name ("MidiTrackControlsBaseUnselected");
875                         controls_base_selected_name = "MidiTrackControlsBaseSelected";
876                         controls_base_unselected_name = "MidiTrackControlsBaseUnselected";
877                 } else {
878                         controls_ebox.set_name ("MidiTrackControlsBaseInactiveUnselected");
879                         controls_base_selected_name = "MidiTrackControlsBaseInactiveSelected";
880                         controls_base_unselected_name = "MidiTrackControlsBaseInactiveUnselected";
881                 }
882         } else {
883
884                 throw; // wha?
885
886                 if (_route->active()) {
887                         controls_ebox.set_name ("BusControlsBaseUnselected");
888                         controls_base_selected_name = "BusControlsBaseSelected";
889                         controls_base_unselected_name = "BusControlsBaseUnselected";
890                 } else {
891                         controls_ebox.set_name ("BusControlsBaseInactiveUnselected");
892                         controls_base_selected_name = "BusControlsBaseInactiveSelected";
893                         controls_base_unselected_name = "BusControlsBaseInactiveUnselected";
894                 }
895         }
896 }
897
898
899
900 void
901 MidiTimeAxisView::add_note_selection (uint8_t note)
902 {
903         if (!_editor.internal_editing()) {
904                 return;
905         }
906
907         uint16_t chn_mask = _channel_selector.get_selected_channels();
908
909         if (_view->num_selected_regionviews() == 0) {
910                 _view->foreach_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection_region_view), note, chn_mask));
911         } else {
912                 _view->foreach_selected_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection_region_view), note, chn_mask));
913         }
914 }
915
916 void
917 MidiTimeAxisView::extend_note_selection (uint8_t note)
918 {
919         if (!_editor.internal_editing()) {
920                 return;
921         }
922
923         uint16_t chn_mask = _channel_selector.get_selected_channels();
924
925         if (_view->num_selected_regionviews() == 0) {
926                 _view->foreach_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::extend_note_selection_region_view), note, chn_mask));
927         } else {
928                 _view->foreach_selected_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::extend_note_selection_region_view), note, chn_mask));
929         }
930 }
931
932 void
933 MidiTimeAxisView::toggle_note_selection (uint8_t note)
934 {
935         if (!_editor.internal_editing()) {
936                 return;
937         }
938
939         uint16_t chn_mask = _channel_selector.get_selected_channels();
940
941         if (_view->num_selected_regionviews() == 0) {
942                 _view->foreach_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection_region_view), note, chn_mask));
943         } else {
944                 _view->foreach_selected_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection_region_view), note, chn_mask));
945         }
946 }
947
948 void
949 MidiTimeAxisView::add_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
950 {
951         dynamic_cast<MidiRegionView*>(rv)->select_matching_notes (note, chn_mask, false, false);
952 }
953
954 void
955 MidiTimeAxisView::extend_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
956 {
957         dynamic_cast<MidiRegionView*>(rv)->select_matching_notes (note, chn_mask, true, true);
958 }
959
960 void
961 MidiTimeAxisView::toggle_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
962 {
963         dynamic_cast<MidiRegionView*>(rv)->toggle_matching_notes (note, chn_mask);
964 }
965
966 void
967 MidiTimeAxisView::set_channel_mode (ChannelMode, uint16_t)
968 {
969         /* hide all automation tracks that use the wrong channel(s) and show all those that use
970            the right ones.
971         */
972
973         uint16_t selected_channels = _channel_selector.get_selected_channels();
974         bool changed = false;
975
976         no_redraw = true;
977
978         for (uint32_t ctl = 0; ctl < 127; ++ctl) {
979
980                 for (uint32_t chn = 0; chn < 16; ++chn) {
981                         Evoral::Parameter fully_qualified_param (MidiCCAutomation, chn, ctl);
982                         boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
983
984                         if (!track) {
985                                 continue;
986                         }
987                         
988                         if ((selected_channels & (0x0001 << chn)) == 0) {
989                                 /* channel not in use. hiding it will trigger RouteTimeAxisView::automation_track_hidden() 
990                                    which will cause a redraw. We don't want one per channel, so block that with no_redraw.
991                                  */
992                                 changed = track->set_visibility (false) || changed;
993                         } else {
994                                 changed = track->set_visibility (true) || changed;
995                         }
996                 }
997         }
998
999         no_redraw = false;
1000
1001         /* TODO: Bender, Pressure */
1002
1003         /* invalidate the controller menu, so that we rebuilt it next time */
1004         _controller_menu_map.clear ();
1005         delete controller_menu;
1006         controller_menu = 0;
1007
1008         if (changed) {
1009                 _route->gui_changed ("track_height", this);
1010         }
1011 }
1012
1013 Gtk::CheckMenuItem*
1014 MidiTimeAxisView::automation_child_menu_item (Evoral::Parameter param)
1015 {
1016         Gtk::CheckMenuItem* m = RouteTimeAxisView::automation_child_menu_item (param);
1017         if (m) {
1018                 return m;
1019         }
1020
1021         ParameterMenuMap::iterator i = _controller_menu_map.find (param);
1022         if (i != _controller_menu_map.end()) {
1023                 return i->second;
1024         }
1025
1026         i = _channel_command_menu_map.find (param);
1027         if (i != _channel_command_menu_map.end()) {
1028                 return i->second;
1029         }
1030
1031         return 0;
1032 }
1033
1034 boost::shared_ptr<MidiRegion>
1035 MidiTimeAxisView::add_region (framepos_t pos, framecnt_t length, bool commit)
1036 {
1037         Editor* real_editor = dynamic_cast<Editor*> (&_editor);
1038
1039         real_editor->begin_reversible_command (Operations::create_region);
1040         playlist()->clear_changes ();
1041
1042         real_editor->snap_to (pos, 0);
1043         
1044         boost::shared_ptr<Source> src = _session->create_midi_source_for_session (view()->trackview().track().get(),
1045                                                                                   view()->trackview().track()->name());
1046         PropertyList plist; 
1047         
1048         plist.add (ARDOUR::Properties::start, 0);
1049         plist.add (ARDOUR::Properties::length, length);
1050         plist.add (ARDOUR::Properties::name, PBD::basename_nosuffix(src->name()));
1051         
1052         boost::shared_ptr<Region> region = (RegionFactory::create (src, plist));
1053         
1054         playlist()->add_region (region, pos);
1055         _session->add_command (new StatefulDiffCommand (playlist()));
1056
1057         if (commit) {
1058                 real_editor->commit_reversible_command ();
1059         }
1060
1061         return boost::dynamic_pointer_cast<MidiRegion>(region);
1062 }
1063
1064 void
1065 MidiTimeAxisView::ensure_step_editor ()
1066 {
1067         if (!_step_editor) {
1068                 _step_editor = new StepEditor (_editor, midi_track(), *this);
1069         }
1070 }
1071
1072 void 
1073 MidiTimeAxisView::start_step_editing ()
1074 {
1075         ensure_step_editor ();
1076         _step_editor->start_step_editing ();
1077
1078 }
1079 void 
1080 MidiTimeAxisView::stop_step_editing ()
1081 {
1082         if (_step_editor) {
1083                 _step_editor->stop_step_editing ();
1084         }
1085 }