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