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