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