Replace a bunch of potential crashes with graceful handling of the situation.
[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         model_changed();
231         _midnam_custom_device_mode_selector.set_active_text (gui_property (X_("midnam-custom-device-mode")));
232
233         _midnam_model_selector.signal_changed().connect(
234                 sigc::mem_fun(*this, &MidiTimeAxisView::model_changed));
235         _midnam_custom_device_mode_selector.signal_changed().connect(
236                 sigc::mem_fun(*this, &MidiTimeAxisView::custom_device_mode_changed));
237
238         ARDOUR_UI::instance()->set_tip (_midnam_model_selector, _("External MIDI Device"));
239         ARDOUR_UI::instance()->set_tip (_midnam_custom_device_mode_selector, _("External Device Mode"));
240
241         _midi_controls_box.set_homogeneous(false);
242         _midi_controls_box.set_border_width (10);
243         if (!patch_manager.all_models().empty()) {
244                 _channel_selector.set_border_width(2);
245                 _midi_controls_box.resize(3, 2);
246                 _midi_controls_box.attach(_channel_selector, 0, 2, 0, 1);
247
248                 _midi_controls_box.attach(*manage(new HSeparator()), 0, 2, 1, 2);
249
250                 _midnam_model_selector.set_size_request(22, 30);
251                 _midnam_model_selector.set_border_width(2);
252                 _midi_controls_box.attach(_midnam_model_selector, 0, 1, 2, 3);
253
254                 _midnam_custom_device_mode_selector.set_size_request(10, 30);
255                 _midnam_custom_device_mode_selector.set_border_width(2);
256                 _midi_controls_box.attach(_midnam_custom_device_mode_selector, 0, 1, 3, 4);
257         } else {
258                 _midi_controls_box.attach(_channel_selector, 0, 1, 0, 1);
259         }
260
261         controls_vbox.pack_start(_midi_controls_box, false, false);
262
263         // restore channel selector settings
264         _channel_selector.set_channel_mode(midi_track()->get_channel_mode(),
265                                            midi_track()->get_channel_mask());
266         _channel_selector.mode_changed.connect(
267                 sigc::mem_fun(*midi_track(), &MidiTrack::set_channel_mode));
268         _channel_selector.mode_changed.connect(
269                 sigc::mem_fun(*this, &MidiTimeAxisView::set_channel_mode));
270
271         const string color_mode = gui_property ("color-mode");
272         if (!color_mode.empty()) {
273                 _color_mode = ColorMode (string_2_enum(color_mode, _color_mode));
274                 if (_color_mode == ChannelColors) {
275                         _channel_selector.set_channel_colors(CanvasNoteEvent::midi_channel_colors);
276                 }
277         }
278
279         set_color_mode (_color_mode, true, false);
280
281         const string note_mode = gui_property ("note-mode");
282         if (!note_mode.empty()) {
283                 _note_mode = NoteMode (string_2_enum (note_mode, _note_mode));
284                 if (_percussion_mode_item) {
285                         _percussion_mode_item->set_active (_note_mode == Percussive);
286                 }
287         }
288
289         /* Look for any GUI object state nodes that represent automation children
290          * that should exist, and create the children.
291          */
292
293         const list<string> gui_ids = gui_object_state().all_ids ();
294         for (list<string>::const_iterator i = gui_ids.begin(); i != gui_ids.end(); ++i) {
295                 PBD::ID route_id;
296                 bool has_parameter;
297                 Evoral::Parameter parameter (0, 0, 0);
298
299                 bool const p = AutomationTimeAxisView::parse_state_id (
300                         *i, route_id, has_parameter, parameter);
301                 if (p && route_id == _route->id () && has_parameter) {
302                         const std::string& visible = gui_object_state().get_string (*i, X_("visible"));
303                         create_automation_child (parameter, string_is_affirmative (visible));
304                 }
305         }
306 }
307
308 void
309 MidiTimeAxisView::first_idle ()
310 {
311         if (is_track ()) {
312                 _view->attach ();
313         }
314 }
315
316 MidiTimeAxisView::~MidiTimeAxisView ()
317 {
318         delete _piano_roll_header;
319         _piano_roll_header = 0;
320
321         delete _range_scroomer;
322         _range_scroomer = 0;
323
324         delete controller_menu;
325         delete _step_editor;
326 }
327
328 void
329 MidiTimeAxisView::enter_internal_edit_mode ()
330 {
331         if (midi_view()) {
332                 midi_view()->enter_internal_edit_mode ();
333         }
334 }
335
336 void
337 MidiTimeAxisView::leave_internal_edit_mode ()
338 {
339         if (midi_view()) {
340                 midi_view()->leave_internal_edit_mode ();
341         }
342 }
343
344 void
345 MidiTimeAxisView::check_step_edit ()
346 {
347         ensure_step_editor ();
348         _step_editor->check_step_edit ();
349 }
350
351 void
352 MidiTimeAxisView::model_changed()
353 {
354         const Glib::ustring model = _midnam_model_selector.get_active_text();
355         set_gui_property (X_("midnam-model-name"), model);
356
357         std::list<std::string> device_modes = MIDI::Name::MidiPatchManager::instance()
358                 .custom_device_mode_names_by_model(model);
359
360         _midnam_custom_device_mode_selector.clear_items();
361
362         if (device_modes.size() < 2) {
363                 _midnam_custom_device_mode_selector.hide();
364         } else {
365                 _midnam_custom_device_mode_selector.show();
366                 for (std::list<std::string>::const_iterator i = device_modes.begin();
367                      i != device_modes.end(); ++i) {
368                         _midnam_custom_device_mode_selector.append_text(*i);
369                 }
370         }
371
372         _midnam_custom_device_mode_selector.set_active(0);
373         
374         _route->instrument_info().set_external_instrument (
375                 _midnam_model_selector.get_active_text(),
376                 _midnam_custom_device_mode_selector.get_active_text());
377
378         // Rebuild controller menu
379         _controller_menu_map.clear ();
380         delete controller_menu;
381         controller_menu = 0;
382         build_automation_action_menu(false);
383 }
384
385 void
386 MidiTimeAxisView::custom_device_mode_changed()
387 {
388         const Glib::ustring mode = _midnam_custom_device_mode_selector.get_active_text();
389         set_gui_property (X_("midnam-custom-device-mode"), mode);
390         _route->instrument_info().set_external_instrument (
391                 _midnam_model_selector.get_active_text(), mode);
392 }
393
394 MidiStreamView*
395 MidiTimeAxisView::midi_view()
396 {
397         return dynamic_cast<MidiStreamView*>(_view);
398 }
399
400 void
401 MidiTimeAxisView::set_height (uint32_t h)
402 {
403         if (h >= MIDI_CONTROLS_BOX_MIN_HEIGHT) {
404                 _midi_controls_box.show ();
405         } else {
406                 _midi_controls_box.hide();
407         }
408         
409         if (h >= KEYBOARD_MIN_HEIGHT) {
410                 if (is_track() && _range_scroomer) {
411                         _range_scroomer->show();
412                 }
413                 if (is_track() && _piano_roll_header) {
414                         _piano_roll_header->show();
415                 }
416         } else {
417                 if (is_track() && _range_scroomer) {
418                         _range_scroomer->hide();
419                 }
420                 if (is_track() && _piano_roll_header) {
421                         _piano_roll_header->hide();
422                 }
423         }
424
425         /* We need to do this after changing visibility of our stuff, as it will
426            eventually trigger a call to Editor::reset_controls_layout_width(),
427            which needs to know if we have just shown or hidden a scroomer /
428            piano roll.
429         */
430         RouteTimeAxisView::set_height (h);
431 }
432
433 void
434 MidiTimeAxisView::append_extra_display_menu_items ()
435 {
436         using namespace Menu_Helpers;
437
438         MenuList& items = display_menu->items();
439
440         // Note range
441         Menu *range_menu = manage(new Menu);
442         MenuList& range_items = range_menu->items();
443         range_menu->set_name ("ArdourContextMenu");
444
445         range_items.push_back (
446                 MenuElem (_("Show Full Range"),
447                           sigc::bind (sigc::mem_fun(*this, &MidiTimeAxisView::set_note_range), 
448                                       MidiStreamView::FullRange, true)));
449
450         range_items.push_back (
451                 MenuElem (_("Fit Contents"),
452                           sigc::bind (sigc::mem_fun(*this, &MidiTimeAxisView::set_note_range),
453                                       MidiStreamView::ContentsRange, true)));
454
455         items.push_back (MenuElem (_("Note Range"), *range_menu));
456         items.push_back (MenuElem (_("Note Mode"), *build_note_mode_menu()));
457
458         items.push_back (SeparatorElem ());
459 }
460
461 void
462 MidiTimeAxisView::build_automation_action_menu (bool for_selection)
463 {
464         using namespace Menu_Helpers;
465
466         /* If we have a controller menu, we need to detach it before
467            RouteTimeAxis::build_automation_action_menu destroys the
468            menu it is attached to.  Otherwise GTK destroys
469            controller_menu's gobj, meaning that it can't be reattached
470            below.  See bug #3134.
471         */
472
473         if (controller_menu) {
474                 detach_menu (*controller_menu);
475         }
476
477         _channel_command_menu_map.clear ();
478         RouteTimeAxisView::build_automation_action_menu (for_selection);
479
480         MenuList& automation_items = automation_action_menu->items();
481
482         uint16_t selected_channels = _channel_selector.get_selected_channels();
483
484         if (selected_channels !=  0) {
485
486                 automation_items.push_back (SeparatorElem());
487
488                 /* these 2 MIDI "command" types are semantically more like automation
489                    than note data, but they are not MIDI controllers. We give them
490                    special status in this menu, since they will not show up in the
491                    controller list and anyone who actually knows something about MIDI
492                    (!) would not expect to find them there.
493                 */
494
495                 add_channel_command_menu_item (
496                         automation_items, _("Bender"), MidiPitchBenderAutomation, 0);
497                 automation_items.back().set_sensitive (
498                         !for_selection || _editor.get_selection().tracks.size() == 1);
499                 add_channel_command_menu_item (
500                         automation_items, _("Pressure"), MidiChannelPressureAutomation, 0);
501                 automation_items.back().set_sensitive (
502                         !for_selection || _editor.get_selection().tracks.size() == 1);
503
504                 /* now all MIDI controllers. Always offer the possibility that we will
505                    rebuild the controllers menu since it might need to be updated after
506                    a channel mode change or other change. Also detach it first in case
507                    it has been used anywhere else.
508                 */
509
510                 build_controller_menu ();
511
512                 automation_items.push_back (SeparatorElem());
513                 automation_items.push_back (MenuElem (_("Controllers"), *controller_menu));
514                 automation_items.back().set_sensitive (
515                         !for_selection || _editor.get_selection().tracks.size() == 1);
516         } else {
517                 automation_items.push_back (
518                         MenuElem (string_compose ("<i>%1</i>", _("No MIDI Channels selected"))));
519                 dynamic_cast<Label*> (automation_items.back().get_child())->set_use_markup (true);
520         }
521 }
522
523 void
524 MidiTimeAxisView::change_all_channel_tracks_visibility (bool yn, Evoral::Parameter param)
525 {
526         const uint16_t selected_channels = _channel_selector.get_selected_channels();
527
528         for (uint8_t chn = 0; chn < 16; chn++) {
529                 if (selected_channels & (0x0001 << chn)) {
530
531                         Evoral::Parameter fully_qualified_param (param.type(), chn, param.id());
532                         Gtk::CheckMenuItem* menu = automation_child_menu_item (fully_qualified_param);
533
534                         if (menu) {
535                                 menu->set_active (yn);
536                         }
537                 }
538         }
539 }
540
541 void
542 MidiTimeAxisView::add_channel_command_menu_item (Menu_Helpers::MenuList& items,
543                                                  const string&           label,
544                                                  AutomationType          auto_type,
545                                                  uint8_t                 cmd)
546 {
547         using namespace Menu_Helpers;
548
549         /* count the number of selected channels because we will build a different menu
550            structure if there is more than 1 selected.
551          */
552
553         const uint16_t selected_channels = _channel_selector.get_selected_channels();
554         int chn_cnt = 0;
555
556         for (uint8_t chn = 0; chn < 16; chn++) {
557                 if (selected_channels & (0x0001 << chn)) {
558                         if (++chn_cnt > 1) {
559                                 break;
560                         }
561                 }
562         }
563
564         if (chn_cnt > 1) {
565
566                 /* multiple channels - create a submenu, with 1 item per channel */
567
568                 Menu* chn_menu = manage (new Menu);
569                 MenuList& chn_items (chn_menu->items());
570                 Evoral::Parameter param_without_channel (auto_type, 0, cmd);
571
572                 /* add a couple of items to hide/show all of them */
573
574                 chn_items.push_back (
575                         MenuElem (_("Hide all channels"),
576                                   sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
577                                               false, param_without_channel)));
578                 chn_items.push_back (
579                         MenuElem (_("Show all channels"),
580                                   sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
581                                               true, param_without_channel)));
582
583                 for (uint8_t chn = 0; chn < 16; chn++) {
584                         if (selected_channels & (0x0001 << chn)) {
585
586                                 /* for each selected channel, add a menu item for this controller */
587
588                                 Evoral::Parameter fully_qualified_param (auto_type, chn, cmd);
589                                 chn_items.push_back (
590                                         CheckMenuElem (string_compose (_("Channel %1"), chn+1),
591                                                        sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
592                                                                    fully_qualified_param)));
593
594                                 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
595                                 bool visible = false;
596
597                                 if (track) {
598                                         if (track->marked_for_display()) {
599                                                 visible = true;
600                                         }
601                                 }
602
603                                 CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&chn_items.back());
604                                 _channel_command_menu_map[fully_qualified_param] = cmi;
605                                 cmi->set_active (visible);
606                         }
607                 }
608
609                 /* now create an item in the parent menu that has the per-channel list as a submenu */
610
611                 items.push_back (MenuElem (label, *chn_menu));
612
613         } else {
614
615                 /* just one channel - create a single menu item for this command+channel combination*/
616
617                 for (uint8_t chn = 0; chn < 16; chn++) {
618                         if (selected_channels & (0x0001 << chn)) {
619
620                                 Evoral::Parameter fully_qualified_param (auto_type, chn, cmd);
621                                 items.push_back (
622                                         CheckMenuElem (label,
623                                                        sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
624                                                                    fully_qualified_param)));
625
626                                 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
627                                 bool visible = false;
628
629                                 if (track) {
630                                         if (track->marked_for_display()) {
631                                                 visible = true;
632                                         }
633                                 }
634
635                                 CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&items.back());
636                                 _channel_command_menu_map[fully_qualified_param] = cmi;
637                                 cmi->set_active (visible);
638
639                                 /* one channel only */
640                                 break;
641                         }
642                 }
643         }
644 }
645
646 void
647 MidiTimeAxisView::build_controller_menu ()
648 {
649         using namespace Menu_Helpers;
650
651         if (controller_menu) {
652                 /* it exists and has not been invalidated by a channel mode change */
653                 return;
654         }
655
656         controller_menu = new Menu; // explicitly managed by us
657         MenuList& items (controller_menu->items());
658
659         /* create several "top level" menu items for sets of controllers (16 at a
660            time), and populate each one with a submenu for each controller+channel
661            combination covering the currently selected channels for this track
662         */
663
664         const uint16_t selected_channels = _channel_selector.get_selected_channels();
665
666         /* count the number of selected channels because we will build a different menu
667            structure if there is more than 1 selected.
668         */
669
670         int chn_cnt = 0;
671         for (uint8_t chn = 0; chn < 16; chn++) {
672                 if (selected_channels & (0x0001 << chn)) {
673                         if (++chn_cnt > 1) {
674                                 break;
675                         }
676                 }
677         }
678
679         using namespace MIDI::Name;
680         const Glib::ustring model = _midnam_model_selector.get_active_text();
681         boost::shared_ptr<MIDINameDocument> midnam = MidiPatchManager::instance()
682                 .document_by_model(model);
683         boost::shared_ptr<MasterDeviceNames> device_names;
684         if (midnam && !midnam->master_device_names_by_model().empty()) {
685                 device_names = boost::shared_ptr<MasterDeviceNames>(
686                         midnam->master_device_names_by_model().begin()->second);
687         }
688
689         if (device_names && !device_names->controls().empty()) {
690                 /* Controllers names available from the midnam file,
691                    generate a custom controller menu */
692                 for (MasterDeviceNames::ControlNameLists::const_iterator l = device_names->controls().begin();
693                      l != device_names->controls().end(); ++l) {
694                         boost::shared_ptr<ControlNameList> name_list = *l;
695
696                         int       chn        = 0;  // TODO
697                         Menu*     group_menu = manage(new Menu());
698                         MenuList& group_items(group_menu->items());
699
700                         for (ControlNameList::Controls::const_iterator c = (*l)->controls().begin();
701                              c != (*l)->controls().end(); ++c) {
702                                 Evoral::Parameter fully_qualified_param(MidiCCAutomation, chn, atoi((*c)->number().c_str()));
703                                 group_items.push_back(
704                                         CheckMenuElem(string_compose("<b>%1</b>: %2 [%3]",
705                                                                      (*c)->number(), (*c)->name(), int(chn)),
706                                                       sigc::bind(
707                                                               sigc::mem_fun(*this, &RouteTimeAxisView::toggle_automation_track),
708                                                               fully_qualified_param)));
709                                 dynamic_cast<Label*> (group_items.back().get_child())->set_use_markup (true);
710                         }
711                         items.push_back(MenuElem(name_list->name(), *group_menu));
712                 }
713                 return;
714         }
715
716         /* loop over all 127 MIDI controllers, in groups of 16; except don't offer
717            bank select controllers, as they are handled by the `patch' code */
718
719         for (int i = 0; i < 127; i += 16) {
720
721                 Menu* ctl_menu = manage (new Menu);
722                 MenuList& ctl_items (ctl_menu->items());
723
724                 /* for each controller, consider whether to create a submenu or a single item */
725
726                 for (int ctl = i; ctl < i+16; ++ctl) {
727
728                         if (ctl == MIDI_CTL_MSB_BANK || ctl == MIDI_CTL_LSB_BANK) {
729                                 continue;
730                         }
731
732                         if (chn_cnt > 1) {
733
734                                 /* multiple channels - create a submenu, with 1 item per channel */
735
736                                 Menu* chn_menu = manage (new Menu);
737                                 MenuList& chn_items (chn_menu->items());
738
739                                 /* add a couple of items to hide/show this controller on all channels */
740
741                                 Evoral::Parameter param_without_channel (MidiCCAutomation, 0, ctl);
742                                 chn_items.push_back (
743                                         MenuElem (_("Hide all channels"),
744                                                   sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
745                                                               false, param_without_channel)));
746                                 chn_items.push_back (
747                                         MenuElem (_("Show all channels"),
748                                                   sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
749                                                               true, param_without_channel)));
750
751                                 for (uint8_t chn = 0; chn < 16; chn++) {
752                                         if (selected_channels & (0x0001 << chn)) {
753
754                                                 /* for each selected channel, add a menu item for this controller */
755
756                                                 Evoral::Parameter fully_qualified_param (MidiCCAutomation, chn, ctl);
757                                                 chn_items.push_back (
758                                                         CheckMenuElem (string_compose (_("Channel %1"), chn+1),
759                                                                        sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
760                                                                                    fully_qualified_param)));
761
762                                                 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (
763                                                         fully_qualified_param);
764                                                 bool visible = false;
765
766                                                 if (track) {
767                                                         if (track->marked_for_display()) {
768                                                                 visible = true;
769                                                         }
770                                                 }
771
772                                                 CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&chn_items.back());
773                                                 _controller_menu_map[fully_qualified_param] = cmi;
774                                                 cmi->set_active (visible);
775                                         }
776                                 }
777
778                                 /* add the per-channel menu to the list of controllers, with the name of the controller */
779                                 ctl_items.push_back (MenuElem (string_compose ("<b>%1</b>: %2", ctl, midi_name (ctl)),
780                                                                *chn_menu));
781                                 dynamic_cast<Label*> (ctl_items.back().get_child())->set_use_markup (true);
782
783                         } else {
784
785                                 /* just one channel - create a single menu item for this ctl+channel combination */
786
787                                 for (uint8_t chn = 0; chn < 16; chn++) {
788                                         if (selected_channels & (0x0001 << chn)) {
789
790                                                 Evoral::Parameter fully_qualified_param (MidiCCAutomation, chn, ctl);
791                                                 ctl_items.push_back (
792                                                         CheckMenuElem (
793                                                                 string_compose ("<b>%1</b>: %2 [%3]", ctl, midi_name (ctl), int (chn)),
794                                                                 sigc::bind (
795                                                                         sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
796                                                                         fully_qualified_param)));
797                                                 dynamic_cast<Label*> (ctl_items.back().get_child())->set_use_markup (true);
798
799                                                 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (
800                                                         fully_qualified_param);
801
802                                                 bool visible = false;
803                                                 if (track) {
804                                                         if (track->marked_for_display()) {
805                                                                 visible = true;
806                                                         }
807                                                 }
808
809                                                 CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&ctl_items.back());
810                                                 _controller_menu_map[fully_qualified_param] = cmi;
811                                                 cmi->set_active (visible);
812
813                                                 /* one channel only */
814                                                 break;
815                                         }
816                                 }
817                         }
818                 }
819
820                 /* add the menu for this block of controllers to the overall controller menu */
821
822                 items.push_back (MenuElem (string_compose (_("Controllers %1-%2"), i, i+15), *ctl_menu));
823         }
824 }
825
826 Gtk::Menu*
827 MidiTimeAxisView::build_note_mode_menu()
828 {
829         using namespace Menu_Helpers;
830
831         Menu* mode_menu = manage (new Menu);
832         MenuList& items = mode_menu->items();
833         mode_menu->set_name ("ArdourContextMenu");
834
835         RadioMenuItem::Group mode_group;
836         items.push_back (
837                 RadioMenuElem (mode_group,_("Sustained"),
838                                sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_mode),
839                                            Sustained, true)));
840         _note_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
841         _note_mode_item->set_active(_note_mode == Sustained);
842
843         items.push_back (
844                 RadioMenuElem (mode_group, _("Percussive"),
845                                sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_mode),
846                                            Percussive, true)));
847         _percussion_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
848         _percussion_mode_item->set_active(_note_mode == Percussive);
849
850         return mode_menu;
851 }
852
853 Gtk::Menu*
854 MidiTimeAxisView::build_color_mode_menu()
855 {
856         using namespace Menu_Helpers;
857
858         Menu* mode_menu = manage (new Menu);
859         MenuList& items = mode_menu->items();
860         mode_menu->set_name ("ArdourContextMenu");
861
862         RadioMenuItem::Group mode_group;
863         items.push_back (
864                 RadioMenuElem (mode_group, _("Meter Colors"),
865                                sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode),
866                                            MeterColors, false, true, true)));
867         _meter_color_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
868         _meter_color_mode_item->set_active(_color_mode == MeterColors);
869
870         items.push_back (
871                 RadioMenuElem (mode_group, _("Channel Colors"),
872                                sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode),
873                                            ChannelColors, false, true, true)));
874         _channel_color_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
875         _channel_color_mode_item->set_active(_color_mode == ChannelColors);
876
877         items.push_back (
878                 RadioMenuElem (mode_group, _("Track Color"),
879                                sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode),
880                                            TrackColor, false, true, true)));
881         _channel_color_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
882         _channel_color_mode_item->set_active(_color_mode == TrackColor);
883
884         return mode_menu;
885 }
886
887 void
888 MidiTimeAxisView::set_note_mode(NoteMode mode, bool apply_to_selection)
889 {
890         if (apply_to_selection) {
891                 _editor.get_selection().tracks.foreach_midi_time_axis (
892                         boost::bind (&MidiTimeAxisView::set_note_mode, _1, mode, false));
893         } else {
894                 if (_note_mode != mode || midi_track()->note_mode() != mode) {
895                         _note_mode = mode;
896                         midi_track()->set_note_mode(mode);
897                         set_gui_property ("note-mode", enum_2_string(_note_mode));
898                         _view->redisplay_track();
899                 }
900         }
901 }
902
903 void
904 MidiTimeAxisView::set_color_mode (ColorMode mode, bool force, bool redisplay, bool apply_to_selection)
905 {
906         if (apply_to_selection) {
907                 _editor.get_selection().tracks.foreach_midi_time_axis (
908                         boost::bind (&MidiTimeAxisView::set_color_mode, _1, mode, force, redisplay, false));
909         } else {
910                 if (_color_mode == mode && !force) {
911                         return;
912                 }
913                 
914                 if (mode == ChannelColors) {
915                         _channel_selector.set_channel_colors(CanvasNoteEvent::midi_channel_colors);
916                 } else {
917                         _channel_selector.set_default_channel_color();
918                 }
919                 
920                 _color_mode = mode;
921                 set_gui_property ("color-mode", enum_2_string(_color_mode));
922                 if (redisplay) {
923                         _view->redisplay_track();
924                 }
925         }
926 }
927
928 void
929 MidiTimeAxisView::set_note_range (MidiStreamView::VisibleNoteRange range, 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_range, _1, range, false));
934         } else {
935                 if (!_ignore_signals) {
936                         midi_view()->set_note_range(range);
937                 }
938         }
939 }
940
941 void
942 MidiTimeAxisView::update_range()
943 {
944         MidiGhostRegion* mgr;
945
946         for (list<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
947                 if ((mgr = dynamic_cast<MidiGhostRegion*>(*i)) != 0) {
948                         mgr->update_range();
949                 }
950         }
951 }
952
953 void
954 MidiTimeAxisView::show_all_automation (bool apply_to_selection)
955 {
956         if (apply_to_selection) {
957                 _editor.get_selection().tracks.foreach_midi_time_axis (
958                         boost::bind (&MidiTimeAxisView::show_all_automation, _1, false));
959         } else {
960                 if (midi_track()) {
961                         const set<Evoral::Parameter> params = midi_track()->midi_playlist()->contained_automation();
962
963                         for (set<Evoral::Parameter>::const_iterator i = params.begin(); i != params.end(); ++i) {
964                                 create_automation_child(*i, true);
965                         }
966                 }
967
968                 RouteTimeAxisView::show_all_automation ();
969         }
970 }
971
972 void
973 MidiTimeAxisView::show_existing_automation (bool apply_to_selection)
974 {
975         if (apply_to_selection) {
976                 _editor.get_selection().tracks.foreach_midi_time_axis (
977                         boost::bind (&MidiTimeAxisView::show_existing_automation, _1, false));
978         } else {
979                 if (midi_track()) {
980                         const set<Evoral::Parameter> params = midi_track()->midi_playlist()->contained_automation();
981
982                         for (set<Evoral::Parameter>::const_iterator i = params.begin(); i != params.end(); ++i) {
983                                 create_automation_child (*i, true);
984                         }
985                 }
986
987                 RouteTimeAxisView::show_existing_automation ();
988         }
989 }
990
991 /** Create an automation track for the given parameter (pitch bend, channel pressure).
992  */
993 void
994 MidiTimeAxisView::create_automation_child (const Evoral::Parameter& param, bool show)
995 {
996         if (param.type() == NullAutomation) {
997                 return;
998         }
999
1000         AutomationTracks::iterator existing = _automation_tracks.find (param);
1001
1002         if (existing != _automation_tracks.end()) {
1003
1004                 /* automation track created because we had existing data for
1005                  * the processor, but visibility may need to be controlled
1006                  * since it will have been set visible by default.
1007                  */
1008
1009                 if (existing->second->set_marked_for_display (show) && !no_redraw) {
1010                         request_redraw ();
1011                 }
1012
1013                 return;
1014         }
1015
1016         boost::shared_ptr<AutomationTimeAxisView> track;
1017
1018         switch (param.type()) {
1019
1020         case GainAutomation:
1021                 create_gain_automation_child (param, show);
1022                 break;
1023
1024         case PluginAutomation:
1025                 /* handled elsewhere */
1026                 break;
1027
1028         case MidiCCAutomation:
1029         case MidiPgmChangeAutomation:
1030         case MidiPitchBenderAutomation:
1031         case MidiChannelPressureAutomation:
1032         case MidiSystemExclusiveAutomation:
1033                 /* These controllers are region "automation" - they are owned
1034                  * by regions (and their MidiModels), not by the track. As a
1035                  * result we do not create an AutomationList/Line for the track
1036                  * ... except here we are doing something!! XXX 
1037                  */
1038
1039                 track.reset (new AutomationTimeAxisView (
1040                                      _session,
1041                                      _route,
1042                                      boost::shared_ptr<Automatable> (),
1043                                      boost::shared_ptr<AutomationControl> (),
1044                                      param,
1045                                      _editor,
1046                                      *this,
1047                                      true,
1048                                      parent_canvas,
1049                                      _route->describe_parameter(param)));
1050
1051                 if (_view) {
1052                         _view->foreach_regionview (
1053                                 sigc::mem_fun (*track.get(), &TimeAxisView::add_ghost));
1054                 }
1055
1056                 add_automation_child (param, track, show);
1057                 break;
1058
1059         default:
1060                 error << "MidiTimeAxisView: unknown automation child "
1061                       << EventTypeMap::instance().to_symbol(param) << endmsg;
1062         }
1063 }
1064
1065 void
1066 MidiTimeAxisView::route_active_changed ()
1067 {
1068         RouteUI::route_active_changed ();
1069
1070         if (is_track()) {
1071                 if (_route->active()) {
1072                         controls_ebox.set_name ("MidiTrackControlsBaseUnselected");
1073                         controls_base_selected_name = "MidiTrackControlsBaseSelected";
1074                         controls_base_unselected_name = "MidiTrackControlsBaseUnselected";
1075                 } else {
1076                         controls_ebox.set_name ("MidiTrackControlsBaseInactiveUnselected");
1077                         controls_base_selected_name = "MidiTrackControlsBaseInactiveSelected";
1078                         controls_base_unselected_name = "MidiTrackControlsBaseInactiveUnselected";
1079                 }
1080         } else {
1081                 if (_route->active()) {
1082                         controls_ebox.set_name ("BusControlsBaseUnselected");
1083                         controls_base_selected_name = "BusControlsBaseSelected";
1084                         controls_base_unselected_name = "BusControlsBaseUnselected";
1085                 } else {
1086                         controls_ebox.set_name ("BusControlsBaseInactiveUnselected");
1087                         controls_base_selected_name = "BusControlsBaseInactiveSelected";
1088                         controls_base_unselected_name = "BusControlsBaseInactiveUnselected";
1089                 }
1090         }
1091 }
1092
1093 void
1094 MidiTimeAxisView::set_note_selection (uint8_t note)
1095 {
1096         if (!_editor.internal_editing()) {
1097                 return;
1098         }
1099
1100         uint16_t chn_mask = _channel_selector.get_selected_channels();
1101
1102         if (_view->num_selected_regionviews() == 0) {
1103                 _view->foreach_regionview (
1104                         sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_selection_region_view),
1105                                     note, chn_mask));
1106         } else {
1107                 _view->foreach_selected_regionview (
1108                         sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_selection_region_view),
1109                                     note, chn_mask));
1110         }
1111 }
1112
1113 void
1114 MidiTimeAxisView::add_note_selection (uint8_t note)
1115 {
1116         if (!_editor.internal_editing()) {
1117                 return;
1118         }
1119
1120         const uint16_t chn_mask = _channel_selector.get_selected_channels();
1121
1122         if (_view->num_selected_regionviews() == 0) {
1123                 _view->foreach_regionview (
1124                         sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection_region_view),
1125                                     note, chn_mask));
1126         } else {
1127                 _view->foreach_selected_regionview (
1128                         sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection_region_view),
1129                                     note, chn_mask));
1130         }
1131 }
1132
1133 void
1134 MidiTimeAxisView::extend_note_selection (uint8_t note)
1135 {
1136         if (!_editor.internal_editing()) {
1137                 return;
1138         }
1139
1140         const uint16_t chn_mask = _channel_selector.get_selected_channels();
1141
1142         if (_view->num_selected_regionviews() == 0) {
1143                 _view->foreach_regionview (
1144                         sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::extend_note_selection_region_view),
1145                                     note, chn_mask));
1146         } else {
1147                 _view->foreach_selected_regionview (
1148                         sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::extend_note_selection_region_view),
1149                                     note, chn_mask));
1150         }
1151 }
1152
1153 void
1154 MidiTimeAxisView::toggle_note_selection (uint8_t note)
1155 {
1156         if (!_editor.internal_editing()) {
1157                 return;
1158         }
1159
1160         const uint16_t chn_mask = _channel_selector.get_selected_channels();
1161
1162         if (_view->num_selected_regionviews() == 0) {
1163                 _view->foreach_regionview (
1164                         sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection_region_view),
1165                                     note, chn_mask));
1166         } else {
1167                 _view->foreach_selected_regionview (
1168                         sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection_region_view),
1169                                     note, chn_mask));
1170         }
1171 }
1172
1173 void
1174 MidiTimeAxisView::set_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1175 {
1176         dynamic_cast<MidiRegionView*>(rv)->select_matching_notes (note, chn_mask, false, false);
1177 }
1178
1179 void
1180 MidiTimeAxisView::add_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1181 {
1182         dynamic_cast<MidiRegionView*>(rv)->select_matching_notes (note, chn_mask, true, false);
1183 }
1184
1185 void
1186 MidiTimeAxisView::extend_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1187 {
1188         dynamic_cast<MidiRegionView*>(rv)->select_matching_notes (note, chn_mask, true, true);
1189 }
1190
1191 void
1192 MidiTimeAxisView::toggle_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1193 {
1194         dynamic_cast<MidiRegionView*>(rv)->toggle_matching_notes (note, chn_mask);
1195 }
1196
1197 void
1198 MidiTimeAxisView::set_channel_mode (ChannelMode, uint16_t)
1199 {
1200         /* hide all automation tracks that use the wrong channel(s) and show all those that use
1201            the right ones.
1202         */
1203
1204         const uint16_t selected_channels = _channel_selector.get_selected_channels();
1205         bool changed = false;
1206
1207         no_redraw = true;
1208
1209         for (uint32_t ctl = 0; ctl < 127; ++ctl) {
1210
1211                 for (uint32_t chn = 0; chn < 16; ++chn) {
1212                         Evoral::Parameter fully_qualified_param (MidiCCAutomation, chn, ctl);
1213                         boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
1214
1215                         if (!track) {
1216                                 continue;
1217                         }
1218
1219                         if ((selected_channels & (0x0001 << chn)) == 0) {
1220                                 /* channel not in use. hiding it will trigger RouteTimeAxisView::automation_track_hidden()
1221                                    which will cause a redraw. We don't want one per channel, so block that with no_redraw.
1222                                 */
1223                                 changed = track->set_marked_for_display (false) || changed;
1224                         } else {
1225                                 changed = track->set_marked_for_display (true) || changed;
1226                         }
1227                 }
1228         }
1229
1230         no_redraw = false;
1231
1232         /* TODO: Bender, Pressure */
1233
1234         /* invalidate the controller menu, so that we rebuild it next time */
1235         _controller_menu_map.clear ();
1236         delete controller_menu;
1237         controller_menu = 0;
1238
1239         if (changed) {
1240                 request_redraw ();
1241         }
1242 }
1243
1244 Gtk::CheckMenuItem*
1245 MidiTimeAxisView::automation_child_menu_item (Evoral::Parameter param)
1246 {
1247         Gtk::CheckMenuItem* m = RouteTimeAxisView::automation_child_menu_item (param);
1248         if (m) {
1249                 return m;
1250         }
1251
1252         ParameterMenuMap::iterator i = _controller_menu_map.find (param);
1253         if (i != _controller_menu_map.end()) {
1254                 return i->second;
1255         }
1256
1257         i = _channel_command_menu_map.find (param);
1258         if (i != _channel_command_menu_map.end()) {
1259                 return i->second;
1260         }
1261
1262         return 0;
1263 }
1264
1265 boost::shared_ptr<MidiRegion>
1266 MidiTimeAxisView::add_region (framepos_t pos, framecnt_t length, bool commit)
1267 {
1268         Editor* real_editor = dynamic_cast<Editor*> (&_editor);
1269
1270         real_editor->begin_reversible_command (Operations::create_region);
1271         playlist()->clear_changes ();
1272
1273         real_editor->snap_to (pos, 0);
1274
1275         boost::shared_ptr<Source> src = _session->create_midi_source_for_session (
1276                 view()->trackview().track().get(), view()->trackview().track()->name());
1277         PropertyList plist;
1278
1279         plist.add (ARDOUR::Properties::start, 0);
1280         plist.add (ARDOUR::Properties::length, length);
1281         plist.add (ARDOUR::Properties::name, PBD::basename_nosuffix(src->name()));
1282
1283         boost::shared_ptr<Region> region = (RegionFactory::create (src, plist));
1284
1285         playlist()->add_region (region, pos);
1286         _session->add_command (new StatefulDiffCommand (playlist()));
1287
1288         if (commit) {
1289                 real_editor->commit_reversible_command ();
1290         }
1291
1292         return boost::dynamic_pointer_cast<MidiRegion>(region);
1293 }
1294
1295 void
1296 MidiTimeAxisView::ensure_step_editor ()
1297 {
1298         if (!_step_editor) {
1299                 _step_editor = new StepEditor (_editor, midi_track(), *this);
1300         }
1301 }
1302
1303 void
1304 MidiTimeAxisView::start_step_editing ()
1305 {
1306         ensure_step_editor ();
1307         _step_editor->start_step_editing ();
1308
1309 }
1310 void
1311 MidiTimeAxisView::stop_step_editing ()
1312 {
1313         if (_step_editor) {
1314                 _step_editor->stop_step_editing ();
1315         }
1316 }
1317
1318
1319 /** @return channel (counted from 0) to add an event to, based on the current setting
1320  *  of the channel selector.
1321  */
1322 uint8_t
1323 MidiTimeAxisView::get_channel_for_add () const
1324 {
1325         uint16_t const chn_mask = _channel_selector.get_selected_channels ();
1326         int chn_cnt = 0;
1327         uint8_t channel = 0;
1328
1329         /* pick the highest selected channel, unless all channels are selected,
1330            which is interpreted to mean channel 1 (zero)
1331         */
1332
1333         for (uint16_t i = 0; i < 16; ++i) {
1334                 if (chn_mask & (1<<i)) {
1335                         channel = i;
1336                         chn_cnt++;
1337                 }
1338         }
1339
1340         if (chn_cnt == 16) {
1341                 channel = 0;
1342         }
1343
1344         return channel;
1345 }
1346
1347 void
1348 MidiTimeAxisView::note_range_changed ()
1349 {
1350         set_gui_property ("note-range-min", (int) midi_view()->lowest_note ());
1351         set_gui_property ("note-range-max", (int) midi_view()->highest_note ());
1352 }
1353
1354 void
1355 MidiTimeAxisView::contents_height_changed ()
1356 {
1357         _range_scroomer->set_size_request (-1, _view->child_height ());
1358 }