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