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