4a13004a966362efe7516d3cb330c2a0ef1f8f46
[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::start_step_editing ()
896 {
897         step_edit_insert_position = _editor.get_preferred_edit_position ();
898         step_edit_beat_pos = -1.0;
899         _step_edit_triplet_countdown = 0;
900         _step_edit_within_chord = 0;
901         _step_edit_chord_duration = 0.0;
902
903         step_edit_region = playlist()->top_region_at (step_edit_insert_position);
904
905         if (step_edit_region) {
906                 RegionView* rv = view()->find_view (step_edit_region);
907                 step_edit_region_view = dynamic_cast<MidiRegionView*> (rv);
908         } else {
909                 step_edit_region_view = 0;
910         }
911
912
913         if (step_editor == 0) {
914                 step_editor = new StepEntry (*this);
915                 step_editor->signal_delete_event().connect (sigc::mem_fun (*this, &MidiTimeAxisView::step_editor_hidden));
916         }
917
918         step_editor->set_position (WIN_POS_MOUSE);
919         step_editor->present ();
920 }
921
922 bool
923 MidiTimeAxisView::step_editor_hidden (GdkEventAny*)
924 {
925         /* everything else will follow the change in the model */
926         midi_track()->set_step_editing (false);
927         return true;
928 }
929
930 void
931 MidiTimeAxisView::stop_step_editing ()
932 {
933         if (step_editor) {
934                 step_editor->hide ();
935         }
936 }
937
938 void
939 MidiTimeAxisView::check_step_edit ()
940 {
941         MidiRingBuffer<nframes_t>& incoming (midi_track()->step_edit_ring_buffer());
942         uint8_t* buf;
943         uint32_t bufsize = 32;
944
945         buf = new uint8_t[bufsize];
946
947         while (incoming.read_space()) {
948                 nframes_t time;
949                 Evoral::EventType type;
950                 uint32_t size;
951
952                 incoming.read_prefix (&time, &type, &size);
953
954                 if (size > bufsize) {
955                         delete [] buf;
956                         bufsize = size;
957                         buf = new uint8_t[bufsize];
958                 }
959
960                 incoming.read_contents (size, buf);
961
962                 if ((buf[0] & 0xf0) == MIDI_CMD_NOTE_ON) {
963                         step_add_note (buf[0] & 0xf, buf[1], buf[2], 0.0);
964                 }
965         }
966 }
967
968 int
969 MidiTimeAxisView::step_add_note (uint8_t channel, uint8_t pitch, uint8_t velocity, Evoral::MusicalTime beat_duration)
970 {
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                 MidiStreamView* msv = midi_view();
998                 
999                 /* make sure its visible on the vertical axis */
1000                 
1001                 if (pitch < msv->lowest_note() || pitch > msv->highest_note()) {
1002                         msv->update_note_range (pitch);
1003                         msv->set_note_range (MidiStreamView::ContentsRange);
1004                 }
1005
1006                 /* make sure its visible on the horizontal axis */
1007
1008                 nframes64_t fpos = step_edit_region->position() + 
1009                         step_edit_region_view->beats_to_frames (step_edit_beat_pos + beat_duration);
1010
1011                 if (fpos >= (_editor.leftmost_position() + _editor.current_page_frames())) {
1012                         _editor.reset_x_origin (fpos - (_editor.current_page_frames()/4));
1013                 }
1014
1015                 step_edit_region_view->step_add_note (channel, pitch, velocity, step_edit_beat_pos, beat_duration);
1016
1017                 if (_step_edit_triplet_countdown > 0) {
1018                         _step_edit_triplet_countdown--;
1019
1020                         if (_step_edit_triplet_countdown == 0) {
1021                                 _step_edit_triplet_countdown = 3;
1022                         }
1023                 }
1024
1025                 if (!_step_edit_within_chord) {
1026                         step_edit_beat_pos += beat_duration;
1027                 } else {
1028                         step_edit_beat_pos += 1.0/Meter::ticks_per_beat; // tiny, but no longer overlapping
1029                         _step_edit_chord_duration = beat_duration;
1030                 }
1031         }
1032
1033         return 0;
1034 }
1035
1036 bool
1037 MidiTimeAxisView::step_edit_within_triplet() const
1038 {
1039         return _step_edit_triplet_countdown > 0;
1040 }
1041
1042 bool
1043 MidiTimeAxisView::step_edit_within_chord() const
1044 {
1045         return _step_edit_within_chord;
1046 }
1047
1048 void
1049 MidiTimeAxisView::step_edit_toggle_triplet ()
1050 {
1051         if (_step_edit_triplet_countdown == 0) {
1052                 _step_edit_within_chord = false;
1053                 _step_edit_triplet_countdown = 3;
1054         } else {
1055                 _step_edit_triplet_countdown = 0;
1056         }
1057 }
1058
1059 void
1060 MidiTimeAxisView::step_edit_toggle_chord ()
1061 {
1062         if (_step_edit_within_chord) {
1063                 _step_edit_within_chord = false;
1064                 step_edit_beat_pos += _step_edit_chord_duration;
1065         } else {
1066                 _step_edit_triplet_countdown = 0;
1067                 _step_edit_within_chord = true;
1068         }
1069 }
1070
1071 void
1072 MidiTimeAxisView::step_edit_rest (Evoral::MusicalTime beats)
1073 {
1074         bool success;
1075
1076         if (beats == 0.0) {
1077                 beats = _editor.get_grid_type_as_beats (success, step_edit_insert_position);
1078         } else {
1079                 success = true;
1080         }
1081
1082         if (success) {
1083                 step_edit_beat_pos += beats;
1084         }
1085 }
1086
1087 boost::shared_ptr<Region>
1088 MidiTimeAxisView::add_region (framepos_t pos)
1089 {
1090         Editor* real_editor = dynamic_cast<Editor*> (&_editor);
1091
1092         real_editor->begin_reversible_command (_("create region"));
1093         playlist()->clear_history ();
1094
1095         framepos_t start = pos;
1096         real_editor->snap_to (start, -1);
1097         const Meter& m = _session->tempo_map().meter_at(start);
1098         const Tempo& t = _session->tempo_map().tempo_at(start);
1099         double length = floor (m.frames_per_bar(t, _session->frame_rate()));
1100
1101         boost::shared_ptr<Source> src = _session->create_midi_source_for_session (view()->trackview().track().get(),
1102                                                                                   view()->trackview().track()->name());
1103         PropertyList plist; 
1104         
1105         plist.add (ARDOUR::Properties::start, 0);
1106         plist.add (ARDOUR::Properties::length, length);
1107         plist.add (ARDOUR::Properties::name, PBD::basename_nosuffix(src->name()));
1108         
1109         boost::shared_ptr<Region> region = (RegionFactory::create (src, plist));
1110         
1111         playlist()->add_region (region, start);
1112         _session->add_command (new StatefulDiffCommand (playlist()));
1113
1114         real_editor->commit_reversible_command();
1115
1116         return region;
1117 }
1118
1119 void
1120 MidiTimeAxisView::add_note_selection (uint8_t note)
1121 {
1122         if (!_editor.internal_editing()) {
1123                 return;
1124         }
1125
1126         uint16_t chn_mask = _channel_selector.get_selected_channels();
1127
1128         if (_view->num_selected_regionviews() == 0) {
1129                 _view->foreach_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection_region_view), note, chn_mask));
1130         } else {
1131                 _view->foreach_selected_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection_region_view), note, chn_mask));
1132         }
1133 }
1134
1135 void
1136 MidiTimeAxisView::extend_note_selection (uint8_t note)
1137 {
1138         if (!_editor.internal_editing()) {
1139                 return;
1140         }
1141
1142         uint16_t chn_mask = _channel_selector.get_selected_channels();
1143
1144         if (_view->num_selected_regionviews() == 0) {
1145                 _view->foreach_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::extend_note_selection_region_view), note, chn_mask));
1146         } else {
1147                 _view->foreach_selected_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::extend_note_selection_region_view), note, chn_mask));
1148         }
1149 }
1150
1151 void
1152 MidiTimeAxisView::toggle_note_selection (uint8_t note)
1153 {
1154         if (!_editor.internal_editing()) {
1155                 return;
1156         }
1157
1158         uint16_t chn_mask = _channel_selector.get_selected_channels();
1159
1160         if (_view->num_selected_regionviews() == 0) {
1161                 _view->foreach_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection_region_view), note, chn_mask));
1162         } else {
1163                 _view->foreach_selected_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection_region_view), note, chn_mask));
1164         }
1165 }
1166
1167 void
1168 MidiTimeAxisView::add_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1169 {
1170         dynamic_cast<MidiRegionView*>(rv)->select_matching_notes (note, chn_mask, false, false);
1171 }
1172
1173 void
1174 MidiTimeAxisView::extend_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1175 {
1176         dynamic_cast<MidiRegionView*>(rv)->select_matching_notes (note, chn_mask, true, true);
1177 }
1178
1179 void
1180 MidiTimeAxisView::toggle_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1181 {
1182         dynamic_cast<MidiRegionView*>(rv)->toggle_matching_notes (note, chn_mask);
1183 }
1184
1185 void
1186 MidiTimeAxisView::set_channel_mode (ChannelMode, uint16_t)
1187 {
1188         /* hide all automation tracks that use the wrong channel(s) and show all those that use
1189            the right ones.
1190         */
1191
1192         uint16_t selected_channels = _channel_selector.get_selected_channels();
1193         bool changed = false;
1194
1195         no_redraw = true;
1196
1197         for (uint32_t ctl = 0; ctl < 127; ++ctl) {
1198
1199                 for (uint32_t chn = 0; chn < 16; ++chn) {
1200                         Evoral::Parameter fully_qualified_param (MidiCCAutomation, chn, ctl);
1201                         boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
1202
1203                         if (!track) {
1204                                 continue;
1205                         }
1206                         
1207                         if ((selected_channels & (0x0001 << chn)) == 0) {
1208                                 /* channel not in use. hiding it will trigger RouteTimeAxisView::automation_track_hidden() 
1209                                    which will cause a redraw. We don't want one per channel, so block that with no_redraw.
1210                                  */
1211                                 changed = track->set_visibility (false) || changed;
1212                         } else {
1213                                 changed = track->set_visibility (true) || changed;
1214                         }
1215                 }
1216         }
1217
1218         no_redraw = false;
1219
1220         /* TODO: Bender, PgmChange, Pressure */
1221
1222         /* invalidate the controller menu, so that we rebuilt it next time */
1223         _controller_menu_map.clear ();
1224         delete controller_menu;
1225         controller_menu = 0;
1226
1227         if (changed) {
1228                 _route->gui_changed ("track_height", this);
1229         }
1230 }
1231
1232 Gtk::CheckMenuItem*
1233 MidiTimeAxisView::automation_child_menu_item (Evoral::Parameter param)
1234 {
1235         Gtk::CheckMenuItem* m = RouteTimeAxisView::automation_child_menu_item (param);
1236         if (m) {
1237                 return m;
1238         }
1239
1240         ParameterMenuMap::iterator i = _controller_menu_map.find (param);
1241         if (i != _controller_menu_map.end()) {
1242                 return i->second;
1243         }
1244
1245         i = _channel_command_menu_map.find (param);
1246         if (i != _channel_command_menu_map.end()) {
1247                 return i->second;
1248         }
1249
1250         return 0;
1251 }