c46aa9a26b0741bdcde8bda2ab05e8a8dc706c31
[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/stop_signal.h>
39 #include <gtkmm2ext/bindable_button.h>
40 #include <gtkmm2ext/utils.h>
41
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         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, MIDI_CMD_PGM_CHANGE);
408                 add_channel_command_menu_item (automation_items, _("Bender"), MidiPitchBenderAutomation, MIDI_CMD_BENDER);
409                 add_channel_command_menu_item (automation_items, _("Pressure"), MidiChannelPressureAutomation, MIDI_CMD_CHANNEL_PRESSURE);
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                                 _parameter_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                                 _parameter_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                                                 _parameter_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                                                 _parameter_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 /** Hide 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         delete controller_menu;
1085         controller_menu = 0;
1086
1087         if (changed) {
1088                 _route->gui_changed ("track_height", this);
1089         }
1090 }