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