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