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