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