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