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