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