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