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