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