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