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