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