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