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