(Source List) Fix drag-n-drop.
[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 <gtkmm/separator.h>
29 #include <gtkmm/stock.h>
30
31 #include "pbd/error.h"
32 #include "pbd/ffs.h"
33 #include "pbd/stl_delete.h"
34 #include "pbd/whitespace.h"
35 #include "pbd/basename.h"
36 #include "pbd/enumwriter.h"
37 #include "pbd/memento_command.h"
38 #include "pbd/stateful_diff_command.h"
39
40 #include "gtkmm2ext/gtk_ui.h"
41 #include "gtkmm2ext/utils.h"
42
43 #include "widgets/tooltips.h"
44
45 #include "ardour/event_type_map.h"
46 #include "ardour/midi_patch_manager.h"
47 #include "ardour/midi_playlist.h"
48 #include "ardour/midi_region.h"
49 #include "ardour/midi_source.h"
50 #include "ardour/midi_track.h"
51 #include "ardour/operations.h"
52 #include "ardour/pannable.h"
53 #include "ardour/panner.h"
54 #include "ardour/panner_shell.h"
55 #include "ardour/playlist.h"
56 #include "ardour/plugin_insert.h"
57 #include "ardour/profile.h"
58 #include "ardour/region.h"
59 #include "ardour/region_factory.h"
60 #include "ardour/route.h"
61 #include "ardour/session.h"
62 #include "ardour/session_object.h"
63 #include "ardour/source.h"
64 #include "ardour/track.h"
65 #include "ardour/types.h"
66
67 #include "automation_line.h"
68 #include "automation_time_axis.h"
69 #include "editor.h"
70 #include "enums.h"
71 #include "ghostregion.h"
72 #include "gui_thread.h"
73 #include "keyboard.h"
74 #include "midi_channel_selector.h"
75 #include "midi_scroomer.h"
76 #include "midi_streamview.h"
77 #include "midi_region_view.h"
78 #include "midi_time_axis.h"
79 #include "patch_change_dialog.h"
80 #include "patch_change_widget.h"
81 #include "piano_roll_header.h"
82 #include "playlist_selector.h"
83 #include "plugin_selector.h"
84 #include "plugin_ui.h"
85 #include "point_selection.h"
86 #include "region_view.h"
87 #include "rgb_macros.h"
88 #include "selection.h"
89 #include "step_editor.h"
90 #include "utils.h"
91 #include "note_base.h"
92
93 #include "ardour/midi_track.h"
94
95 #include "pbd/i18n.h"
96
97 using namespace ARDOUR;
98 using namespace PBD;
99 using namespace Gtk;
100 using namespace Gtkmm2ext;
101 using namespace Editing;
102 using namespace std;
103
104 // Minimum height at which a control is displayed
105 static const uint32_t MIDI_CONTROLS_BOX_MIN_HEIGHT = 160;
106 static const uint32_t KEYBOARD_MIN_HEIGHT = 130;
107
108 MidiTimeAxisView::MidiTimeAxisView (PublicEditor& ed, Session* sess, ArdourCanvas::Canvas& canvas)
109         : SessionHandlePtr (sess)
110         , RouteTimeAxisView (ed, sess, canvas)
111         , _ignore_signals(false)
112         , _range_scroomer(0)
113         , _piano_roll_header(0)
114         , _note_mode(Sustained)
115         , _note_mode_item(0)
116         , _percussion_mode_item(0)
117         , _color_mode(MeterColors)
118         , _meter_color_mode_item(0)
119         , _channel_color_mode_item(0)
120         , _track_color_mode_item(0)
121         , _channel_selector (0)
122         , _step_edit_item (0)
123         , controller_menu (0)
124         , poly_pressure_menu (0)
125         , _step_editor (0)
126 {
127         _midnam_model_selector.disable_scrolling();
128         _midnam_custom_device_mode_selector.disable_scrolling();
129 }
130
131 void
132 MidiTimeAxisView::set_note_highlight (uint8_t note) {
133         _piano_roll_header->set_note_highlight (note);
134 }
135
136 void
137 MidiTimeAxisView::set_route (boost::shared_ptr<Route> rt)
138 {
139         _route = rt;
140
141         _view = new MidiStreamView (*this);
142
143         if (is_track ()) {
144                 _piano_roll_header = new PianoRollHeader(*midi_view());
145                 _range_scroomer = new MidiScroomer(midi_view()->note_range_adjustment);
146                 _range_scroomer->DoubleClicked.connect (
147                         sigc::bind (sigc::mem_fun(*this, &MidiTimeAxisView::set_note_range),
148                                     MidiStreamView::ContentsRange, false));
149         }
150
151         /* This next call will result in our height being set up, so it must come after
152            the creation of the piano roll / range scroomer as their visibility is set up
153            when our height is.
154         */
155         RouteTimeAxisView::set_route (rt);
156
157         _view->apply_color (ARDOUR_UI_UTILS::gdk_color_to_rgba (color()), StreamView::RegionColor);
158
159         subplugin_menu.set_name ("ArdourContextMenu");
160
161         _note_range_changed_connection.disconnect();
162
163         if (!gui_property ("note-range-min").empty ()) {
164                 midi_view()->apply_note_range (atoi (gui_property ("note-range-min").c_str()),
165                                                atoi (gui_property ("note-range-max").c_str()),
166                                                true);
167         }
168
169         _view->ContentsHeightChanged.connect (
170                 sigc::mem_fun (*this, &MidiTimeAxisView::contents_height_changed));
171
172         ignore_toggle = false;
173
174         if (is_midi_track()) {
175                 _note_mode = midi_track()->note_mode();
176         }
177
178         /* if set_state above didn't create a gain automation child, we need to make one */
179         if (automation_child (GainAutomation) == 0) {
180                 create_automation_child (GainAutomation, false);
181         }
182
183         /* if set_state above didn't create a mute automation child, we need to make one */
184         if (automation_child (MuteAutomation) == 0) {
185                 create_automation_child (MuteAutomation, false);
186         }
187
188         if (_route->panner_shell()) {
189                 _route->panner_shell()->Changed.connect (*this, invalidator (*this), boost::bind (&MidiTimeAxisView::ensure_pan_views, this, false), gui_context());
190         }
191
192         /* map current state of the route */
193         ensure_pan_views (false);
194         update_control_names();
195         processors_changed (RouteProcessorChange ());
196
197         if (is_track()) {
198                 _piano_roll_header->SetNoteSelection.connect (
199                         sigc::mem_fun (*this, &MidiTimeAxisView::set_note_selection));
200                 _piano_roll_header->AddNoteSelection.connect (
201                         sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection));
202                 _piano_roll_header->ExtendNoteSelection.connect (
203                         sigc::mem_fun (*this, &MidiTimeAxisView::extend_note_selection));
204                 _piano_roll_header->ToggleNoteSelection.connect (
205                         sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection));
206
207                 /* Put the scroomer and the keyboard in a VBox with a padding
208                    label so that they can be reduced in height for stacked-view
209                    tracks.
210                 */
211
212                 HSeparator* separator = manage (new HSeparator());
213                 separator->set_name("TrackSeparator");
214                 separator->set_size_request(-1, 1);
215                 separator->show();
216
217                 VBox* v = manage (new VBox);
218                 HBox* h = manage (new HBox);
219                 h->pack_end (*_piano_roll_header);
220                 h->pack_end (*_range_scroomer);
221                 v->pack_start (*separator, false, false);
222                 v->pack_start (*h, true, true);
223                 v->show ();
224                 h->show ();
225                 top_hbox.remove(scroomer_placeholder);
226                 time_axis_hbox.pack_end(*v, false, false, 0);
227                 midi_scroomer_size_group->add_widget (*v);
228
229                 /* callback from StreamView scroomer drags, as well as
230                  * automatic changes of note-range (e.g. at rec-stop).
231                  * This callback is used to save the note-range-min/max
232                  * GUI Object property
233                  */
234                 _note_range_changed_connection = midi_view()->NoteRangeChanged.connect (
235                                 sigc::mem_fun (*this, &MidiTimeAxisView::note_range_changed));
236
237                 /* ask for notifications of any new RegionViews */
238                 _view->RegionViewAdded.connect (
239                         sigc::mem_fun(*this, &MidiTimeAxisView::region_view_added));
240
241                 if (!_editor.have_idled()) {
242                         /* first idle will do what we need */
243                 } else {
244                         first_idle ();
245                 }
246         }
247
248         if (gui_property (X_("midnam-model-name")).empty()) {
249                 set_gui_property (X_("midnam-model-name"), "Generic");
250         }
251
252         if (gui_property (X_("midnam-custom-device-mode")).empty()) {
253                 boost::shared_ptr<MIDI::Name::MasterDeviceNames> device_names = get_device_names();
254                 if (device_names) {
255                         set_gui_property (X_("midnam-custom-device-mode"),
256                                           *device_names->custom_device_mode_names().begin());
257                 }
258         }
259
260         ArdourWidgets::set_tooltip (_midnam_model_selector, _("External MIDI Device"));
261         ArdourWidgets::set_tooltip (_midnam_custom_device_mode_selector, _("External Device Mode"));
262
263         _midi_controls_box.pack_start (_midnam_model_selector, false, false, 2);
264         _midi_controls_box.pack_start (_midnam_custom_device_mode_selector, false, false, 2);
265
266         _midi_controls_box.set_homogeneous(false);
267         _midi_controls_box.set_border_width (2);
268
269         MIDI::Name::MidiPatchManager::instance().PatchesChanged.connect (*this, invalidator (*this),
270                         boost::bind (&MidiTimeAxisView::setup_midnam_patches, this),
271                         gui_context());
272
273         setup_midnam_patches ();
274         update_patch_selector ();
275
276         model_changed (gui_property(X_("midnam-model-name")));
277         custom_device_mode_changed (gui_property(X_("midnam-custom-device-mode")));
278
279         controls_vbox.pack_start(_midi_controls_box, false, false);
280
281         const string color_mode = gui_property ("color-mode");
282         if (!color_mode.empty()) {
283                 _color_mode = ColorMode (string_2_enum(color_mode, _color_mode));
284                 if (_channel_selector && _color_mode == ChannelColors) {
285                         _channel_selector->set_channel_colors(NoteBase::midi_channel_colors);
286                 }
287         }
288
289         set_color_mode (_color_mode, true, false);
290
291         const string note_mode = gui_property ("note-mode");
292         if (!note_mode.empty()) {
293                 _note_mode = NoteMode (string_2_enum (note_mode, _note_mode));
294                 if (_percussion_mode_item) {
295                         _percussion_mode_item->set_active (_note_mode == Percussive);
296                 }
297         }
298
299         /* Look for any GUI object state nodes that represent automation children
300          * that should exist, and create the children.
301          */
302
303         const list<string> gui_ids = gui_object_state().all_ids ();
304         for (list<string>::const_iterator i = gui_ids.begin(); i != gui_ids.end(); ++i) {
305                 PBD::ID route_id;
306                 bool has_parameter;
307                 Evoral::Parameter parameter (0, 0, 0);
308
309                 bool const p = AutomationTimeAxisView::parse_state_id (
310                         *i, route_id, has_parameter, parameter);
311                 if (p && route_id == _route->id () && has_parameter) {
312                         const std::string& visible = gui_object_state().get_string (*i, X_("visible"));
313                         create_automation_child (parameter, string_to<bool> (visible));
314                 }
315         }
316 }
317
318 void
319 MidiTimeAxisView::processors_changed (RouteProcessorChange c)
320 {
321         RouteTimeAxisView::processors_changed (c);
322         update_patch_selector ();
323 }
324
325 void
326 MidiTimeAxisView::first_idle ()
327 {
328         if (is_track ()) {
329                 _view->attach ();
330         }
331 }
332
333 MidiTimeAxisView::~MidiTimeAxisView ()
334 {
335         delete _channel_selector;
336
337         delete _piano_roll_header;
338         _piano_roll_header = 0;
339
340         delete _range_scroomer;
341         _range_scroomer = 0;
342
343         delete controller_menu;
344         delete _step_editor;
345 }
346
347 void
348 MidiTimeAxisView::check_step_edit ()
349 {
350         ensure_step_editor ();
351         _step_editor->check_step_edit ();
352 }
353
354 void
355 MidiTimeAxisView::setup_midnam_patches ()
356 {
357         typedef MIDI::Name::MidiPatchManager PatchManager;
358         PatchManager& patch_manager = PatchManager::instance();
359
360         _midnam_model_selector.clear_items ();
361         for (PatchManager::DeviceNamesByMaker::const_iterator m = patch_manager.devices_by_manufacturer().begin();
362                         m != patch_manager.devices_by_manufacturer().end(); ++m) {
363                 Menu*                   menu  = Gtk::manage(new Menu);
364                 Menu_Helpers::MenuList& items = menu->items();
365
366                 // Build manufacturer submenu
367                 for (MIDI::Name::MIDINameDocument::MasterDeviceNamesList::const_iterator n = m->second.begin();
368                                 n != m->second.end(); ++n) {
369                         Menu_Helpers::MenuElem elem = Gtk::Menu_Helpers::MenuElem(
370                                         n->first.c_str(),
371                                         sigc::bind(sigc::mem_fun(*this, &MidiTimeAxisView::model_changed),
372                                                 n->first.c_str()));
373
374                         items.push_back(elem);
375                 }
376
377                 // Add manufacturer submenu to selector
378                 _midnam_model_selector.AddMenuElem(Menu_Helpers::MenuElem(m->first, *menu));
379         }
380
381         if (!get_device_names()) {
382                 model_changed ("Generic");
383         }
384 }
385
386 void
387 MidiTimeAxisView::update_patch_selector ()
388 {
389         typedef MIDI::Name::MidiPatchManager PatchManager;
390         PatchManager& patch_manager = PatchManager::instance();
391
392         bool pluginprovided = false;
393         if (_route) {
394                 boost::shared_ptr<PluginInsert> pi = boost::dynamic_pointer_cast<PluginInsert> (_route->the_instrument ());
395                 if (pi && pi->plugin ()->has_midnam ()) {
396                         pluginprovided = true;
397                         std::string model_name = pi->plugin ()->midnam_model ();
398                         if (gui_property (X_("midnam-model-name")) != model_name) {
399                                 model_changed (model_name);
400                         }
401                 }
402         }
403
404         if (patch_manager.all_models().empty() || pluginprovided) {
405                 _midnam_model_selector.hide ();
406                 _midnam_custom_device_mode_selector.hide ();
407         } else {
408                 _midnam_model_selector.show ();
409                 _midnam_custom_device_mode_selector.show ();
410         }
411 }
412
413
414 void
415 MidiTimeAxisView::model_changed(const std::string& model)
416 {
417         set_gui_property (X_("midnam-model-name"), model);
418
419         const std::list<std::string> device_modes = MIDI::Name::MidiPatchManager::instance()
420                 .custom_device_mode_names_by_model(model);
421
422         _midnam_model_selector.set_text(model);
423         _midnam_custom_device_mode_selector.clear_items();
424
425         for (std::list<std::string>::const_iterator i = device_modes.begin();
426              i != device_modes.end(); ++i) {
427                 _midnam_custom_device_mode_selector.AddMenuElem(
428                         Gtk::Menu_Helpers::MenuElem(
429                                 *i, sigc::bind(sigc::mem_fun(*this, &MidiTimeAxisView::custom_device_mode_changed),
430                                                *i)));
431         }
432
433         if (!device_modes.empty()) {
434                 custom_device_mode_changed(device_modes.front());
435         }
436
437         if (device_modes.size() > 1) {
438                 _midnam_custom_device_mode_selector.show();
439         } else {
440                 _midnam_custom_device_mode_selector.hide();
441         }
442
443         // now this is a real bad hack
444         if (device_modes.size() > 0) {
445                 _route->instrument_info().set_external_instrument (model, device_modes.front());
446         } else {
447                 _route->instrument_info().set_external_instrument (model, "");
448         }
449
450         // Rebuild controller menu
451         _controller_menu_map.clear ();
452         delete controller_menu;
453         controller_menu = 0;
454         build_automation_action_menu(false);
455
456         if (patch_change_dialog ()) {
457                 patch_change_dialog ()->refresh ();
458         }
459 }
460
461 void
462 MidiTimeAxisView::custom_device_mode_changed(const std::string& mode)
463 {
464         const std::string model = gui_property (X_("midnam-model-name"));
465
466         set_gui_property (X_("midnam-custom-device-mode"), mode);
467         _midnam_custom_device_mode_selector.set_text(mode);
468         _route->instrument_info().set_external_instrument (model, mode);
469 }
470
471 MidiStreamView*
472 MidiTimeAxisView::midi_view()
473 {
474         return dynamic_cast<MidiStreamView*>(_view);
475 }
476
477 void
478 MidiTimeAxisView::set_height (uint32_t h, TrackHeightMode m)
479 {
480         if (h >= MIDI_CONTROLS_BOX_MIN_HEIGHT) {
481                 _midi_controls_box.show ();
482         } else {
483                 _midi_controls_box.hide();
484         }
485
486         if (h >= KEYBOARD_MIN_HEIGHT) {
487                 if (is_track() && _range_scroomer) {
488                         _range_scroomer->show();
489                 }
490                 if (is_track() && _piano_roll_header) {
491                         _piano_roll_header->show();
492                 }
493         } else {
494                 if (is_track() && _range_scroomer) {
495                         _range_scroomer->hide();
496                 }
497                 if (is_track() && _piano_roll_header) {
498                         _piano_roll_header->hide();
499                 }
500         }
501
502         /* We need to do this after changing visibility of our stuff, as it will
503            eventually trigger a call to Editor::reset_controls_layout_width(),
504            which needs to know if we have just shown or hidden a scroomer /
505            piano roll.
506         */
507         RouteTimeAxisView::set_height (h, m);
508 }
509
510 void
511 MidiTimeAxisView::append_extra_display_menu_items ()
512 {
513         using namespace Menu_Helpers;
514
515         MenuList& items = display_menu->items();
516
517         // Note range
518         Menu *range_menu = manage(new Menu);
519         MenuList& range_items = range_menu->items();
520         range_menu->set_name ("ArdourContextMenu");
521
522         range_items.push_back (
523                 MenuElem (_("Show Full Range"),
524                           sigc::bind (sigc::mem_fun(*this, &MidiTimeAxisView::set_note_range),
525                                       MidiStreamView::FullRange, true)));
526
527         range_items.push_back (
528                 MenuElem (_("Fit Contents"),
529                           sigc::bind (sigc::mem_fun(*this, &MidiTimeAxisView::set_note_range),
530                                       MidiStreamView::ContentsRange, true)));
531
532         items.push_back (MenuElem (_("Note Range"), *range_menu));
533         items.push_back (MenuElem (_("Note Mode"), *build_note_mode_menu()));
534         items.push_back (MenuElem (_("Channel Selector..."),
535                                    sigc::mem_fun(*this, &MidiTimeAxisView::toggle_channel_selector)));
536
537         items.push_back (MenuElem (_("Patch Selector..."),
538                                 sigc::mem_fun(*this, &RouteUI::select_midi_patch)));
539
540         items.push_back (MenuElem (_("Color Mode"), *build_color_mode_menu ()));
541
542         items.push_back (SeparatorElem ());
543 }
544
545 void
546 MidiTimeAxisView::toggle_channel_selector ()
547 {
548         if (!_channel_selector) {
549                 _channel_selector = new MidiChannelSelectorWindow (midi_track());
550
551                 if (_color_mode == ChannelColors) {
552                         _channel_selector->set_channel_colors(NoteBase::midi_channel_colors);
553                 } else {
554                         _channel_selector->set_default_channel_color ();
555                 }
556
557                 _channel_selector->show_all ();
558         } else {
559                 _channel_selector->cycle_visibility ();
560         }
561 }
562
563 void
564 MidiTimeAxisView::build_automation_action_menu (bool for_selection)
565 {
566         using namespace Menu_Helpers;
567
568         /* If we have a controller menu, we need to detach it before
569            RouteTimeAxis::build_automation_action_menu destroys the
570            menu it is attached to.  Otherwise GTK destroys
571            controller_menu's gobj, meaning that it can't be reattached
572            below.  See bug #3134.
573         */
574
575         if (controller_menu) {
576                 detach_menu (*controller_menu);
577         }
578
579         _channel_command_menu_map.clear ();
580         RouteTimeAxisView::build_automation_action_menu (for_selection);
581
582         MenuList& automation_items = automation_action_menu->items();
583
584         uint16_t selected_channels = midi_track()->get_playback_channel_mask();
585
586         if (selected_channels !=  0) {
587
588                 automation_items.push_back (SeparatorElem());
589
590                 /* these 2 MIDI "command" types are semantically more like automation
591                    than note data, but they are not MIDI controllers. We give them
592                    special status in this menu, since they will not show up in the
593                    controller list and anyone who actually knows something about MIDI
594                    (!) would not expect to find them there.
595                 */
596
597                 add_channel_command_menu_item (
598                         automation_items, _("Bender"), MidiPitchBenderAutomation, 0);
599                 automation_items.back().set_sensitive (
600                         !for_selection || _editor.get_selection().tracks.size() == 1);
601                 add_channel_command_menu_item (
602                         automation_items, _("Pressure"), MidiChannelPressureAutomation, 0);
603                 automation_items.back().set_sensitive (
604                         !for_selection || _editor.get_selection().tracks.size() == 1);
605
606                 /* now all MIDI controllers. Always offer the possibility that we will
607                    rebuild the controllers menu since it might need to be updated after
608                    a channel mode change or other change. Also detach it first in case
609                    it has been used anywhere else.
610                 */
611
612                 build_controller_menu ();
613
614                 automation_items.push_back (MenuElem (_("Controllers"), *controller_menu));
615
616                 if (!poly_pressure_menu) {
617                         poly_pressure_menu = new Gtk::Menu;
618                 }
619
620                 automation_items.push_back (MenuElem  (_("Polyphonic Pressure"), *poly_pressure_menu));
621
622                 automation_items.back().set_sensitive (
623                         !for_selection || _editor.get_selection().tracks.size() == 1);
624         } else {
625                 automation_items.push_back (
626                         MenuElem (string_compose ("<i>%1</i>", _("No MIDI Channels selected"))));
627                 dynamic_cast<Label*> (automation_items.back().get_child())->set_use_markup (true);
628         }
629 }
630
631 void
632 MidiTimeAxisView::change_all_channel_tracks_visibility (bool yn, Evoral::Parameter param)
633 {
634         const uint16_t selected_channels = midi_track()->get_playback_channel_mask();
635
636         for (uint8_t chn = 0; chn < 16; chn++) {
637                 if (selected_channels & (0x0001 << chn)) {
638
639                         Evoral::Parameter fully_qualified_param (param.type(), chn, param.id());
640                         Gtk::CheckMenuItem* menu = automation_child_menu_item (fully_qualified_param);
641
642                         if (menu) {
643                                 menu->set_active (yn);
644                         }
645                 }
646         }
647 }
648
649 void
650 MidiTimeAxisView::add_channel_command_menu_item (Menu_Helpers::MenuList& items,
651                                                  const string&           label,
652                                                  AutomationType          auto_type,
653                                                  uint8_t                 cmd)
654 {
655         using namespace Menu_Helpers;
656
657         /* count the number of selected channels because we will build a different menu
658            structure if there is more than 1 selected.
659          */
660
661         const uint16_t selected_channels = midi_track()->get_playback_channel_mask();
662         int chn_cnt = 0;
663
664         for (uint8_t chn = 0; chn < 16; chn++) {
665                 if (selected_channels & (0x0001 << chn)) {
666                         if (++chn_cnt > 1) {
667                                 break;
668                         }
669                 }
670         }
671
672         if (chn_cnt > 1) {
673
674                 /* multiple channels - create a submenu, with 1 item per channel */
675
676                 Menu* chn_menu = manage (new Menu);
677                 MenuList& chn_items (chn_menu->items());
678                 Evoral::Parameter param_without_channel (auto_type, 0, cmd);
679
680                 /* add a couple of items to hide/show all of them */
681
682                 chn_items.push_back (
683                         MenuElem (_("Hide all channels"),
684                                   sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
685                                               false, param_without_channel)));
686                 chn_items.push_back (
687                         MenuElem (_("Show all channels"),
688                                   sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
689                                               true, param_without_channel)));
690
691                 for (uint8_t chn = 0; chn < 16; chn++) {
692                         if (selected_channels & (0x0001 << chn)) {
693
694                                 /* for each selected channel, add a menu item for this controller */
695
696                                 Evoral::Parameter fully_qualified_param (auto_type, chn, cmd);
697                                 chn_items.push_back (
698                                         CheckMenuElem (string_compose (_("Channel %1"), chn+1),
699                                                        sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
700                                                                    fully_qualified_param)));
701
702                                 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
703                                 bool visible = false;
704
705                                 if (track) {
706                                         if (track->marked_for_display()) {
707                                                 visible = true;
708                                         }
709                                 }
710
711                                 Gtk::CheckMenuItem* cmi = static_cast<Gtk::CheckMenuItem*>(&chn_items.back());
712                                 _channel_command_menu_map[fully_qualified_param] = cmi;
713                                 cmi->set_active (visible);
714                         }
715                 }
716
717                 /* now create an item in the parent menu that has the per-channel list as a submenu */
718
719                 items.push_back (MenuElem (label, *chn_menu));
720
721         } else {
722
723                 /* just one channel - create a single menu item for this command+channel combination*/
724
725                 for (uint8_t chn = 0; chn < 16; chn++) {
726                         if (selected_channels & (0x0001 << chn)) {
727
728                                 Evoral::Parameter fully_qualified_param (auto_type, chn, cmd);
729                                 items.push_back (
730                                         CheckMenuElem (label,
731                                                        sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
732                                                                    fully_qualified_param)));
733
734                                 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
735                                 bool visible = false;
736
737                                 if (track) {
738                                         if (track->marked_for_display()) {
739                                                 visible = true;
740                                         }
741                                 }
742
743                                 Gtk::CheckMenuItem* cmi = static_cast<Gtk::CheckMenuItem*>(&items.back());
744                                 _channel_command_menu_map[fully_qualified_param] = cmi;
745                                 cmi->set_active (visible);
746
747                                 /* one channel only */
748                                 break;
749                         }
750                 }
751         }
752 }
753
754 /** Add a single menu item for a controller on one channel. */
755 void
756 MidiTimeAxisView::add_single_channel_controller_item(Menu_Helpers::MenuList& ctl_items,
757                                                      int                     ctl,
758                                                      const std::string&      name)
759 {
760         using namespace Menu_Helpers;
761
762         const uint16_t selected_channels = midi_track()->get_playback_channel_mask();
763         for (uint8_t chn = 0; chn < 16; chn++) {
764                 if (selected_channels & (0x0001 << chn)) {
765
766                         Evoral::Parameter fully_qualified_param (MidiCCAutomation, chn, ctl);
767                         ctl_items.push_back (
768                                 CheckMenuElem (
769                                         string_compose ("<b>%1</b>: %2 [%3]", ctl, name, int (chn + 1)),
770                                         sigc::bind (
771                                                 sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
772                                                 fully_qualified_param)));
773                         dynamic_cast<Label*> (ctl_items.back().get_child())->set_use_markup (true);
774
775                         boost::shared_ptr<AutomationTimeAxisView> track = automation_child (
776                                 fully_qualified_param);
777
778                         bool visible = false;
779                         if (track) {
780                                 if (track->marked_for_display()) {
781                                         visible = true;
782                                 }
783                         }
784
785                         Gtk::CheckMenuItem* cmi = static_cast<Gtk::CheckMenuItem*>(&ctl_items.back());
786                         _controller_menu_map[fully_qualified_param] = cmi;
787                         cmi->set_active (visible);
788
789                         /* one channel only */
790                         break;
791                 }
792         }
793 }
794
795 /** Add a submenu with 1 item per channel for a controller on many channels. */
796 void
797 MidiTimeAxisView::add_multi_channel_controller_item(Menu_Helpers::MenuList& ctl_items,
798                                                     int                     ctl,
799                                                     const std::string&      name)
800 {
801         using namespace Menu_Helpers;
802
803         const uint16_t selected_channels = midi_track()->get_playback_channel_mask();
804
805         Menu* chn_menu = manage (new Menu);
806         MenuList& chn_items (chn_menu->items());
807
808         /* add a couple of items to hide/show this controller on all channels */
809
810         Evoral::Parameter param_without_channel (MidiCCAutomation, 0, ctl);
811         chn_items.push_back (
812                 MenuElem (_("Hide all channels"),
813                           sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
814                                       false, param_without_channel)));
815         chn_items.push_back (
816                 MenuElem (_("Show all channels"),
817                           sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
818                                       true, param_without_channel)));
819
820         for (uint8_t chn = 0; chn < 16; chn++) {
821                 if (selected_channels & (0x0001 << chn)) {
822
823                         /* for each selected channel, add a menu item for this controller */
824
825                         Evoral::Parameter fully_qualified_param (MidiCCAutomation, chn, ctl);
826                         chn_items.push_back (
827                                 CheckMenuElem (string_compose (_("Channel %1"), chn+1),
828                                                sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
829                                                            fully_qualified_param)));
830
831                         boost::shared_ptr<AutomationTimeAxisView> track = automation_child (
832                                 fully_qualified_param);
833                         bool visible = false;
834
835                         if (track) {
836                                 if (track->marked_for_display()) {
837                                         visible = true;
838                                 }
839                         }
840
841                         Gtk::CheckMenuItem* cmi = static_cast<Gtk::CheckMenuItem*>(&chn_items.back());
842                         _controller_menu_map[fully_qualified_param] = cmi;
843                         cmi->set_active (visible);
844                 }
845         }
846
847         /* add the per-channel menu to the list of controllers, with the name of the controller */
848         ctl_items.push_back (MenuElem (string_compose ("<b>%1</b>: %2", ctl, name),
849                                        *chn_menu));
850         dynamic_cast<Label*> (ctl_items.back().get_child())->set_use_markup (true);
851 }
852
853 boost::shared_ptr<MIDI::Name::CustomDeviceMode>
854 MidiTimeAxisView::get_device_mode()
855 {
856         using namespace MIDI::Name;
857
858         boost::shared_ptr<MasterDeviceNames> device_names = get_device_names();
859         if (!device_names) {
860                 return boost::shared_ptr<MIDI::Name::CustomDeviceMode>();
861         }
862
863         return device_names->custom_device_mode_by_name(
864                 gui_property (X_("midnam-custom-device-mode")));
865 }
866
867 boost::shared_ptr<MIDI::Name::MasterDeviceNames>
868 MidiTimeAxisView::get_device_names()
869 {
870         using namespace MIDI::Name;
871
872         const std::string model = gui_property (X_("midnam-model-name"));
873
874         boost::shared_ptr<MIDINameDocument> midnam = MidiPatchManager::instance()
875                 .document_by_model(model);
876         if (midnam) {
877                 return midnam->master_device_names(model);
878         } else {
879                 return boost::shared_ptr<MasterDeviceNames>();
880         }
881 }
882
883 void
884 MidiTimeAxisView::build_controller_menu ()
885 {
886         using namespace Menu_Helpers;
887
888         if (controller_menu) {
889                 /* it exists and has not been invalidated by a channel mode change */
890                 return;
891         }
892
893         controller_menu = new Menu; // explicitly managed by us
894         MenuList& items (controller_menu->items());
895
896         /* create several "top level" menu items for sets of controllers (16 at a
897            time), and populate each one with a submenu for each controller+channel
898            combination covering the currently selected channels for this track
899         */
900
901         const uint16_t selected_channels = midi_track()->get_playback_channel_mask();
902
903         /* count the number of selected channels because we will build a different menu
904            structure if there is more than 1 selected.
905         */
906
907         int chn_cnt = 0;
908         for (uint8_t chn = 0; chn < 16; chn++) {
909                 if (selected_channels & (0x0001 << chn)) {
910                         if (++chn_cnt > 1) {
911                                 break;
912                         }
913                 }
914         }
915
916         using namespace MIDI::Name;
917         boost::shared_ptr<MasterDeviceNames> device_names = get_device_names();
918
919         if (device_names && !device_names->controls().empty()) {
920                 /* Controllers names available in midnam file, generate fancy menu */
921                 unsigned n_items  = 0;
922                 unsigned n_groups = 0;
923
924                 /* keep track of CC numbers that are added */
925                 uint16_t ctl_start = 1;
926                 uint16_t ctl_end   = 1;
927
928                 MasterDeviceNames::ControlNameLists const& ctllist (device_names->controls());
929
930                 size_t total_ctrls = 0;
931                 for (MasterDeviceNames::ControlNameLists::const_iterator l = ctllist.begin(); l != ctllist.end(); ++l) {
932                         boost::shared_ptr<ControlNameList> name_list = l->second;
933                         total_ctrls += name_list->controls().size();
934                 }
935
936                 bool to_top_level = total_ctrls < 32;
937
938                 /* TODO: This is not correct, should look up the currently applicable ControlNameList
939                    and only build a menu for that one. */
940                 for (MasterDeviceNames::ControlNameLists::const_iterator l = ctllist.begin(); l != ctllist.end(); ++l) {
941                         boost::shared_ptr<ControlNameList> name_list = l->second;
942                         Menu*                              ctl_menu  = NULL;
943
944                         for (ControlNameList::Controls::const_iterator c = name_list->controls().begin();
945                              c != name_list->controls().end();) {
946                                 const uint16_t ctl = c->second->number();
947
948                                 /* Skip bank select controllers since they're handled specially */
949                                 if (ctl != MIDI_CTL_MSB_BANK && ctl != MIDI_CTL_LSB_BANK) {
950
951                                         if (to_top_level) {
952                                                 ctl_menu = controller_menu;
953                                         } else if (!ctl_menu) {
954                                                 /* Create a new submenu */
955                                                 ctl_menu = manage (new Menu);
956                                                 ctl_start = ctl;
957                                         }
958
959                                         MenuList& ctl_items (ctl_menu->items());
960                                         if (chn_cnt > 1) {
961                                                 add_multi_channel_controller_item(ctl_items, ctl, c->second->name());
962                                         } else {
963                                                 add_single_channel_controller_item(ctl_items, ctl, c->second->name());
964                                         }
965                                         ctl_end = ctl;
966                                 }
967
968                                 ++c;
969
970                                 if (!ctl_menu || to_top_level) {
971                                         continue;
972                                 }
973
974                                 if (++n_items == 32 || ctl < ctl_start || c == name_list->controls().end()) {
975                                         /* Submenu has 32 items or we're done, or a new name-list started:
976                                          * add it to controller menu and reset */
977                                         items.push_back (MenuElem (string_compose (_("Controllers %1-%2"), ctl_start, ctl_end), *ctl_menu));
978                                         ctl_menu = NULL;
979                                         n_items  = 0;
980                                         ++n_groups;
981                                 }
982                         }
983                 }
984         } else {
985                 /* No controllers names, generate generic numeric menu */
986                 for (int i = 0; i < 127; i += 32) {
987                         Menu*     ctl_menu = manage (new Menu);
988                         MenuList& ctl_items (ctl_menu->items());
989
990                         for (int ctl = i; ctl < i + 32; ++ctl) {
991                                 if (ctl == MIDI_CTL_MSB_BANK || ctl == MIDI_CTL_LSB_BANK) {
992                                         /* Skip bank select controllers since they're handled specially */
993                                         continue;
994                                 }
995
996                                 if (chn_cnt > 1) {
997                                         add_multi_channel_controller_item(
998                                                 ctl_items, ctl, string_compose(_("Controller %1"), ctl));
999                                 } else {
1000                                         add_single_channel_controller_item(
1001                                                 ctl_items, ctl, string_compose(_("Controller %1"), ctl));
1002                                 }
1003                         }
1004
1005                         /* Add submenu for this block of controllers to controller menu */
1006                         switch (i) {
1007                                 case 0:
1008                                 case 32:
1009                                         /* skip 0x00 and 0x20 (bank-select) */
1010                                         items.push_back (MenuElem (string_compose (_("Controllers %1-%2"), i + 1, i + 31), *ctl_menu));
1011                                         break;
1012                                 default:
1013                                         items.push_back (MenuElem (string_compose (_("Controllers %1-%2"), i, i + 31), *ctl_menu));
1014                                         break;
1015                         }
1016                 }
1017         }
1018 }
1019
1020 Gtk::Menu*
1021 MidiTimeAxisView::build_note_mode_menu()
1022 {
1023         using namespace Menu_Helpers;
1024
1025         Menu* mode_menu = manage (new Menu);
1026         MenuList& items = mode_menu->items();
1027         mode_menu->set_name ("ArdourContextMenu");
1028
1029         RadioMenuItem::Group mode_group;
1030         items.push_back (
1031                 RadioMenuElem (mode_group,_("Sustained"),
1032                                sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_mode),
1033                                            Sustained, true)));
1034         _note_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
1035         _note_mode_item->set_active(_note_mode == Sustained);
1036
1037         items.push_back (
1038                 RadioMenuElem (mode_group, _("Percussive"),
1039                                sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_mode),
1040                                            Percussive, true)));
1041         _percussion_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
1042         _percussion_mode_item->set_active(_note_mode == Percussive);
1043
1044         return mode_menu;
1045 }
1046
1047 Gtk::Menu*
1048 MidiTimeAxisView::build_color_mode_menu()
1049 {
1050         using namespace Menu_Helpers;
1051
1052         Menu* mode_menu = manage (new Menu);
1053         MenuList& items = mode_menu->items();
1054         mode_menu->set_name ("ArdourContextMenu");
1055
1056         RadioMenuItem::Group mode_group;
1057         items.push_back (
1058                 RadioMenuElem (mode_group, _("Meter Colors"),
1059                                sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode),
1060                                            MeterColors, false, true, true)));
1061         _meter_color_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
1062         _meter_color_mode_item->set_active(_color_mode == MeterColors);
1063
1064         items.push_back (
1065                 RadioMenuElem (mode_group, _("Channel Colors"),
1066                                sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode),
1067                                            ChannelColors, false, true, true)));
1068         _channel_color_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
1069         _channel_color_mode_item->set_active(_color_mode == ChannelColors);
1070
1071         items.push_back (
1072                 RadioMenuElem (mode_group, _("Track Color"),
1073                                sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode),
1074                                            TrackColor, false, true, true)));
1075         _channel_color_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
1076         _channel_color_mode_item->set_active(_color_mode == TrackColor);
1077
1078         return mode_menu;
1079 }
1080
1081 void
1082 MidiTimeAxisView::set_note_mode(NoteMode mode, bool apply_to_selection)
1083 {
1084         if (apply_to_selection) {
1085                 _editor.get_selection().tracks.foreach_midi_time_axis (
1086                         boost::bind (&MidiTimeAxisView::set_note_mode, _1, mode, false));
1087         } else {
1088                 if (_note_mode != mode || midi_track()->note_mode() != mode) {
1089                         _note_mode = mode;
1090                         midi_track()->set_note_mode(mode);
1091                         set_gui_property ("note-mode", enum_2_string(_note_mode));
1092                         _view->redisplay_track();
1093                 }
1094         }
1095 }
1096
1097 void
1098 MidiTimeAxisView::set_color_mode (ColorMode mode, bool force, bool redisplay, bool apply_to_selection)
1099 {
1100         if (apply_to_selection) {
1101                 _editor.get_selection().tracks.foreach_midi_time_axis (
1102                         boost::bind (&MidiTimeAxisView::set_color_mode, _1, mode, force, redisplay, false));
1103         } else {
1104                 if (_color_mode == mode && !force) {
1105                         return;
1106                 }
1107
1108                 if (_channel_selector) {
1109                         if (mode == ChannelColors) {
1110                                 _channel_selector->set_channel_colors(NoteBase::midi_channel_colors);
1111                         } else {
1112                                 _channel_selector->set_default_channel_color();
1113                         }
1114                 }
1115
1116                 _color_mode = mode;
1117                 set_gui_property ("color-mode", enum_2_string(_color_mode));
1118                 if (redisplay) {
1119                         _view->redisplay_track();
1120                 }
1121         }
1122 }
1123
1124 void
1125 MidiTimeAxisView::set_note_range (MidiStreamView::VisibleNoteRange range, bool apply_to_selection)
1126 {
1127         if (apply_to_selection) {
1128                 _editor.get_selection().tracks.foreach_midi_time_axis (
1129                         boost::bind (&MidiTimeAxisView::set_note_range, _1, range, false));
1130         } else {
1131                 if (!_ignore_signals) {
1132                         midi_view()->set_note_range(range);
1133                 }
1134         }
1135 }
1136
1137 void
1138 MidiTimeAxisView::show_all_automation (bool apply_to_selection)
1139 {
1140         using namespace MIDI::Name;
1141
1142         if (apply_to_selection) {
1143                 _editor.get_selection().tracks.foreach_midi_time_axis (
1144                         boost::bind (&MidiTimeAxisView::show_all_automation, _1, false));
1145         } else {
1146                 if (midi_track()) {
1147                         // Show existing automation
1148                         const set<Evoral::Parameter> params = midi_track()->midi_playlist()->contained_automation();
1149
1150                         for (set<Evoral::Parameter>::const_iterator i = params.begin(); i != params.end(); ++i) {
1151                                 create_automation_child(*i, true);
1152                         }
1153
1154                         // Show automation for all controllers named in midnam file
1155                         boost::shared_ptr<MasterDeviceNames> device_names = get_device_names();
1156                         if (gui_property (X_("midnam-model-name")) != "Generic" &&
1157                              device_names && !device_names->controls().empty()) {
1158                                 const std::string device_mode       = gui_property (X_("midnam-custom-device-mode"));
1159                                 const uint16_t    selected_channels = midi_track()->get_playback_channel_mask();
1160                                 for (uint32_t chn = 0; chn < 16; ++chn) {
1161                                         if ((selected_channels & (0x0001 << chn)) == 0) {
1162                                                 // Channel not in use
1163                                                 continue;
1164                                         }
1165
1166                                         boost::shared_ptr<ChannelNameSet> chan_names = device_names->channel_name_set_by_channel(
1167                                                 device_mode, chn);
1168                                         if (!chan_names) {
1169                                                 continue;
1170                                         }
1171
1172                                         boost::shared_ptr<ControlNameList> control_names = device_names->control_name_list(
1173                                                 chan_names->control_list_name());
1174                                         if (!control_names) {
1175                                                 continue;
1176                                         }
1177
1178                                         for (ControlNameList::Controls::const_iterator c = control_names->controls().begin();
1179                                              c != control_names->controls().end();
1180                                              ++c) {
1181                                                 const uint16_t ctl = c->second->number();
1182                                                 if (ctl != MIDI_CTL_MSB_BANK && ctl != MIDI_CTL_LSB_BANK) {
1183                                                         /* Skip bank select controllers since they're handled specially */
1184                                                         const Evoral::Parameter param(MidiCCAutomation, chn, ctl);
1185                                                         create_automation_child(param, true);
1186                                                 }
1187                                         }
1188                                 }
1189                         }
1190                 }
1191
1192                 RouteTimeAxisView::show_all_automation ();
1193         }
1194 }
1195
1196 void
1197 MidiTimeAxisView::show_existing_automation (bool apply_to_selection)
1198 {
1199         if (apply_to_selection) {
1200                 _editor.get_selection().tracks.foreach_midi_time_axis (
1201                         boost::bind (&MidiTimeAxisView::show_existing_automation, _1, false));
1202         } else {
1203                 if (midi_track()) {
1204                         const set<Evoral::Parameter> params = midi_track()->midi_playlist()->contained_automation();
1205
1206                         for (set<Evoral::Parameter>::const_iterator i = params.begin(); i != params.end(); ++i) {
1207                                 create_automation_child (*i, true);
1208                         }
1209                 }
1210
1211                 RouteTimeAxisView::show_existing_automation ();
1212         }
1213 }
1214
1215 /** Create an automation track for the given parameter (pitch bend, channel pressure).
1216  */
1217 void
1218 MidiTimeAxisView::create_automation_child (const Evoral::Parameter& param, bool show)
1219 {
1220         if (param.type() == NullAutomation) {
1221                 return;
1222         }
1223
1224         AutomationTracks::iterator existing = _automation_tracks.find (param);
1225
1226         if (existing != _automation_tracks.end()) {
1227
1228                 /* automation track created because we had existing data for
1229                  * the processor, but visibility may need to be controlled
1230                  * since it will have been set visible by default.
1231                  */
1232
1233                 existing->second->set_marked_for_display (show);
1234
1235                 if (!no_redraw) {
1236                         request_redraw ();
1237                 }
1238
1239                 return;
1240         }
1241
1242         boost::shared_ptr<AutomationTimeAxisView> track;
1243         boost::shared_ptr<AutomationControl> control;
1244
1245
1246         switch (param.type()) {
1247
1248         case GainAutomation:
1249                 create_gain_automation_child (param, show);
1250                 break;
1251
1252         case MuteAutomation:
1253                 create_mute_automation_child (param, show);
1254                 break;
1255
1256         case PluginAutomation:
1257                 /* handled elsewhere */
1258                 break;
1259
1260         case MidiCCAutomation:
1261         case MidiPgmChangeAutomation:
1262         case MidiPitchBenderAutomation:
1263         case MidiChannelPressureAutomation:
1264         case MidiSystemExclusiveAutomation:
1265                 /* These controllers are region "automation" - they are owned
1266                  * by regions (and their MidiModels), not by the track. As a
1267                  * result there is no AutomationList/Line for the track, but we create
1268                  * a controller for the user to write immediate events, so the editor
1269                  * can act as a control surface for the present MIDI controllers.
1270                  *
1271                  * TODO: Record manipulation of the controller to regions?
1272                  */
1273
1274                 control = _route->automation_control(param, true);
1275                 track.reset (new AutomationTimeAxisView (
1276                                      _session,
1277                                      _route,
1278                                      control ? _route : boost::shared_ptr<Automatable> (),
1279                                      control,
1280                                      param,
1281                                      _editor,
1282                                      *this,
1283                                      true,
1284                                      parent_canvas,
1285                                      _route->describe_parameter(param)));
1286
1287                 if (_view) {
1288                         _view->foreach_regionview (
1289                                 sigc::mem_fun (*track.get(), &TimeAxisView::add_ghost));
1290                 }
1291
1292                 add_automation_child (param, track, show);
1293                 if (selected ()) {
1294                         reshow_selection (_editor.get_selection().time);
1295                 }
1296
1297                 break;
1298
1299         case PanWidthAutomation:
1300         case PanElevationAutomation:
1301         case PanAzimuthAutomation:
1302                 ensure_pan_views (show);
1303                 break;
1304
1305         default:
1306                 error << "MidiTimeAxisView: unknown automation child "
1307                       << EventTypeMap::instance().to_symbol(param) << endmsg;
1308         }
1309 }
1310
1311 void
1312 MidiTimeAxisView::route_active_changed ()
1313 {
1314         RouteUI::route_active_changed ();
1315         update_control_names();
1316 }
1317
1318 void
1319 MidiTimeAxisView::update_control_names ()
1320 {
1321         if (is_track()) {
1322                 if (_route->active()) {
1323                         controls_base_selected_name = "MidiTrackControlsBaseSelected";
1324                         controls_base_unselected_name = "MidiTrackControlsBaseUnselected";
1325                 } else {
1326                         controls_base_selected_name = "MidiTrackControlsBaseInactiveSelected";
1327                         controls_base_unselected_name = "MidiTrackControlsBaseInactiveUnselected";
1328                 }
1329         } else { // MIDI bus (which doesn't exist yet..)
1330                 if (_route->active()) {
1331                         controls_base_selected_name = "BusControlsBaseSelected";
1332                         controls_base_unselected_name = "BusControlsBaseUnselected";
1333                 } else {
1334                         controls_base_selected_name = "BusControlsBaseInactiveSelected";
1335                         controls_base_unselected_name = "BusControlsBaseInactiveUnselected";
1336                 }
1337         }
1338
1339         if (selected()) {
1340                 controls_ebox.set_name (controls_base_selected_name);
1341                 time_axis_frame.set_name (controls_base_selected_name);
1342         } else {
1343                 controls_ebox.set_name (controls_base_unselected_name);
1344                 time_axis_frame.set_name (controls_base_unselected_name);
1345         }
1346 }
1347
1348 void
1349 MidiTimeAxisView::set_note_selection (uint8_t note)
1350 {
1351         uint16_t chn_mask = midi_track()->get_playback_channel_mask();
1352
1353         _editor.begin_reversible_selection_op (X_("Set Note Selection"));
1354
1355         if (_view->num_selected_regionviews() == 0) {
1356                 _view->foreach_regionview (
1357                         sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_selection_region_view),
1358                                     note, chn_mask));
1359         } else {
1360                 _view->foreach_selected_regionview (
1361                         sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_selection_region_view),
1362                                     note, chn_mask));
1363         }
1364
1365         _editor.commit_reversible_selection_op();
1366 }
1367
1368 void
1369 MidiTimeAxisView::add_note_selection (uint8_t note)
1370 {
1371         const uint16_t chn_mask = midi_track()->get_playback_channel_mask();
1372
1373         _editor.begin_reversible_selection_op (X_("Add Note Selection"));
1374
1375         if (_view->num_selected_regionviews() == 0) {
1376                 _view->foreach_regionview (
1377                         sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection_region_view),
1378                                     note, chn_mask));
1379         } else {
1380                 _view->foreach_selected_regionview (
1381                         sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection_region_view),
1382                                     note, chn_mask));
1383         }
1384
1385         _editor.commit_reversible_selection_op();
1386 }
1387
1388 void
1389 MidiTimeAxisView::extend_note_selection (uint8_t note)
1390 {
1391         const uint16_t chn_mask = midi_track()->get_playback_channel_mask();
1392
1393         _editor.begin_reversible_selection_op (X_("Extend Note Selection"));
1394
1395         if (_view->num_selected_regionviews() == 0) {
1396                 _view->foreach_regionview (
1397                         sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::extend_note_selection_region_view),
1398                                     note, chn_mask));
1399         } else {
1400                 _view->foreach_selected_regionview (
1401                         sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::extend_note_selection_region_view),
1402                                     note, chn_mask));
1403         }
1404
1405         _editor.commit_reversible_selection_op();
1406 }
1407
1408 void
1409 MidiTimeAxisView::toggle_note_selection (uint8_t note)
1410 {
1411         const uint16_t chn_mask = midi_track()->get_playback_channel_mask();
1412
1413         _editor.begin_reversible_selection_op (X_("Toggle Note Selection"));
1414
1415         if (_view->num_selected_regionviews() == 0) {
1416                 _view->foreach_regionview (
1417                         sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection_region_view),
1418                                     note, chn_mask));
1419         } else {
1420                 _view->foreach_selected_regionview (
1421                         sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection_region_view),
1422                                     note, chn_mask));
1423         }
1424
1425         _editor.commit_reversible_selection_op();
1426 }
1427
1428 void
1429 MidiTimeAxisView::get_per_region_note_selection (list<pair<PBD::ID, set<boost::shared_ptr<Evoral::Note<Temporal::Beats> > > > >& selection)
1430 {
1431         _view->foreach_regionview (
1432                 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::get_per_region_note_selection_region_view), sigc::ref(selection)));
1433 }
1434
1435 void
1436 MidiTimeAxisView::set_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1437 {
1438         dynamic_cast<MidiRegionView*>(rv)->select_matching_notes (note, chn_mask, false, false);
1439 }
1440
1441 void
1442 MidiTimeAxisView::add_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1443 {
1444         dynamic_cast<MidiRegionView*>(rv)->select_matching_notes (note, chn_mask, true, false);
1445 }
1446
1447 void
1448 MidiTimeAxisView::extend_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1449 {
1450         dynamic_cast<MidiRegionView*>(rv)->select_matching_notes (note, chn_mask, true, true);
1451 }
1452
1453 void
1454 MidiTimeAxisView::toggle_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1455 {
1456         dynamic_cast<MidiRegionView*>(rv)->toggle_matching_notes (note, chn_mask);
1457 }
1458
1459 void
1460 MidiTimeAxisView::get_per_region_note_selection_region_view (RegionView* rv, list<pair<PBD::ID, std::set<boost::shared_ptr<Evoral::Note<Temporal::Beats> > > > > &selection)
1461 {
1462         Evoral::Sequence<Temporal::Beats>::Notes selected;
1463         dynamic_cast<MidiRegionView*>(rv)->selection_as_notelist (selected, false);
1464
1465         std::set<boost::shared_ptr<Evoral::Note<Temporal::Beats> > > notes;
1466
1467         Evoral::Sequence<Temporal::Beats>::Notes::iterator sel_it;
1468         for (sel_it = selected.begin(); sel_it != selected.end(); ++sel_it) {
1469                 notes.insert (*sel_it);
1470         }
1471
1472         if (!notes.empty()) {
1473                 selection.push_back (make_pair ((rv)->region()->id(), notes));
1474         }
1475 }
1476
1477 void
1478 MidiTimeAxisView::set_channel_mode (ChannelMode, uint16_t)
1479 {
1480         /* hide all automation tracks that use the wrong channel(s) and show all those that use
1481            the right ones.
1482         */
1483
1484         const uint16_t selected_channels = midi_track()->get_playback_channel_mask();
1485         bool changed = false;
1486
1487         no_redraw = true;
1488
1489         for (uint32_t ctl = 0; ctl < 127; ++ctl) {
1490
1491                 for (uint32_t chn = 0; chn < 16; ++chn) {
1492                         Evoral::Parameter fully_qualified_param (MidiCCAutomation, chn, ctl);
1493                         boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
1494
1495                         if (!track) {
1496                                 continue;
1497                         }
1498
1499                         if ((selected_channels & (0x0001 << chn)) == 0) {
1500                                 /* channel not in use. hiding it will trigger RouteTimeAxisView::automation_track_hidden()
1501                                    which will cause a redraw. We don't want one per channel, so block that with no_redraw.
1502                                 */
1503                                 changed = track->set_marked_for_display (false) || changed;
1504                         } else {
1505                                 changed = track->set_marked_for_display (true) || changed;
1506                         }
1507                 }
1508         }
1509
1510         no_redraw = false;
1511
1512         /* TODO: Bender, Pressure */
1513
1514         /* invalidate the controller menu, so that we rebuild it next time */
1515         _controller_menu_map.clear ();
1516         delete controller_menu;
1517         controller_menu = 0;
1518
1519         if (changed) {
1520                 request_redraw ();
1521         }
1522 }
1523
1524 Gtk::CheckMenuItem*
1525 MidiTimeAxisView::automation_child_menu_item (Evoral::Parameter param)
1526 {
1527         Gtk::CheckMenuItem* m = RouteTimeAxisView::automation_child_menu_item (param);
1528         if (m) {
1529                 return m;
1530         }
1531
1532         ParameterMenuMap::iterator i = _controller_menu_map.find (param);
1533         if (i != _controller_menu_map.end()) {
1534                 return i->second;
1535         }
1536
1537         i = _channel_command_menu_map.find (param);
1538         if (i != _channel_command_menu_map.end()) {
1539                 return i->second;
1540         }
1541
1542         return 0;
1543 }
1544
1545 boost::shared_ptr<MidiRegion>
1546 MidiTimeAxisView::add_region (samplepos_t f, samplecnt_t length, bool commit)
1547 {
1548         Editor* real_editor = dynamic_cast<Editor*> (&_editor);
1549         MusicSample pos (f, 0);
1550
1551         if (commit) {
1552                 real_editor->begin_reversible_command (Operations::create_region);
1553         }
1554         playlist()->clear_changes ();
1555
1556         real_editor->snap_to (pos, RoundNearest);
1557
1558         boost::shared_ptr<Source> src = _session->create_midi_source_by_stealing_name (view()->trackview().track());
1559         PropertyList plist;
1560
1561         plist.add (ARDOUR::Properties::start, 0);
1562         plist.add (ARDOUR::Properties::length, length);
1563         plist.add (ARDOUR::Properties::name, PBD::basename_nosuffix(src->name()));
1564
1565         boost::shared_ptr<Region> region = (RegionFactory::create (src, plist));
1566         /* sets beat position */
1567         region->set_position (pos.sample, pos.division);
1568         playlist()->add_region (region, pos.sample, 1.0, false, pos.division);
1569         _session->add_command (new StatefulDiffCommand (playlist()));
1570
1571         if (commit) {
1572                 real_editor->commit_reversible_command ();
1573         }
1574
1575         return boost::dynamic_pointer_cast<MidiRegion>(region);
1576 }
1577
1578 void
1579 MidiTimeAxisView::ensure_step_editor ()
1580 {
1581         if (!_step_editor) {
1582                 _step_editor = new StepEditor (_editor, midi_track(), *this);
1583         }
1584 }
1585
1586 void
1587 MidiTimeAxisView::start_step_editing ()
1588 {
1589         ensure_step_editor ();
1590         _step_editor->start_step_editing ();
1591
1592 }
1593 void
1594 MidiTimeAxisView::stop_step_editing ()
1595 {
1596         if (_step_editor) {
1597                 _step_editor->stop_step_editing ();
1598         }
1599 }
1600
1601 /** @return channel (counted from 0) to add an event to, based on the current setting
1602  *  of the channel selector.
1603  */
1604 uint8_t
1605 MidiTimeAxisView::get_channel_for_add () const
1606 {
1607         uint16_t const chn_mask = midi_track()->get_playback_channel_mask();
1608         int chn_cnt = 0;
1609         uint8_t channel = 0;
1610
1611         /* pick the highest selected channel, unless all channels are selected,
1612            which is interpreted to mean channel 1 (zero)
1613         */
1614
1615         for (uint16_t i = 0; i < 16; ++i) {
1616                 if (chn_mask & (1<<i)) {
1617                         channel = i;
1618                         chn_cnt++;
1619                 }
1620         }
1621
1622         if (chn_cnt == 16) {
1623                 channel = 0;
1624         }
1625
1626         return channel;
1627 }
1628
1629 void
1630 MidiTimeAxisView::note_range_changed ()
1631 {
1632         set_gui_property ("note-range-min", (int) midi_view()->lowest_note ());
1633         set_gui_property ("note-range-max", (int) midi_view()->highest_note ());
1634 }
1635
1636 void
1637 MidiTimeAxisView::contents_height_changed ()
1638 {
1639         _range_scroomer->queue_resize ();
1640 }
1641
1642 bool
1643 MidiTimeAxisView::paste (samplepos_t pos, const Selection& selection, PasteContext& ctx, const int32_t sub_num)
1644 {
1645         if (!_editor.internal_editing()) {
1646                 // Non-internal paste, paste regions like any other route
1647                 return RouteTimeAxisView::paste(pos, selection, ctx, sub_num);
1648         }
1649
1650         return midi_view()->paste(pos, selection, ctx, sub_num);
1651 }