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