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