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