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