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