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