2 Copyright (C) 2000 Paul Davis
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.
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.
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.
26 #include <sigc++/bind.h>
28 #include "pbd/error.h"
30 #include "pbd/stl_delete.h"
31 #include "pbd/whitespace.h"
32 #include "pbd/basename.h"
33 #include "pbd/enumwriter.h"
34 #include "pbd/memento_command.h"
35 #include "pbd/stateful_diff_command.h"
37 #include "gtkmm2ext/gtk_ui.h"
38 #include "gtkmm2ext/selector.h"
39 #include "gtkmm2ext/bindable_button.h"
40 #include "gtkmm2ext/utils.h"
42 #include "ardour/event_type_map.h"
43 #include "ardour/midi_patch_manager.h"
44 #include "ardour/midi_playlist.h"
45 #include "ardour/midi_region.h"
46 #include "ardour/midi_source.h"
47 #include "ardour/midi_track.h"
48 #include "ardour/operations.h"
49 #include "ardour/pannable.h"
50 #include "ardour/panner.h"
51 #include "ardour/panner_shell.h"
52 #include "ardour/playlist.h"
53 #include "ardour/profile.h"
54 #include "ardour/region.h"
55 #include "ardour/region_factory.h"
56 #include "ardour/route.h"
57 #include "ardour/session.h"
58 #include "ardour/session_object.h"
59 #include "ardour/source.h"
60 #include "ardour/track.h"
61 #include "ardour/types.h"
63 #include "ardour_ui.h"
64 #include "ardour_button.h"
65 #include "automation_line.h"
66 #include "automation_time_axis.h"
69 #include "ghostregion.h"
70 #include "gui_thread.h"
72 #include "midi_channel_selector.h"
73 #include "midi_scroomer.h"
74 #include "midi_streamview.h"
75 #include "midi_region_view.h"
76 #include "midi_time_axis.h"
77 #include "piano_roll_header.h"
78 #include "playlist_selector.h"
79 #include "plugin_selector.h"
80 #include "plugin_ui.h"
81 #include "point_selection.h"
83 #include "region_view.h"
84 #include "rgb_macros.h"
85 #include "selection.h"
86 #include "step_editor.h"
88 #include "note_base.h"
90 #include "ardour/midi_track.h"
94 using namespace ARDOUR;
95 using namespace ARDOUR_UI_UTILS;
98 using namespace Gtkmm2ext;
99 using namespace Editing;
102 // Minimum height at which a control is displayed
103 static const uint32_t MIDI_CONTROLS_BOX_MIN_HEIGHT = 160;
104 static const uint32_t KEYBOARD_MIN_HEIGHT = 130;
106 MidiTimeAxisView::MidiTimeAxisView (PublicEditor& ed, Session* sess, ArdourCanvas::Canvas& canvas)
107 : AxisView(sess) // virtually inherited
108 , RouteTimeAxisView(ed, sess, canvas)
109 , _ignore_signals(false)
111 , _piano_roll_header(0)
112 , _note_mode(Sustained)
114 , _percussion_mode_item(0)
115 , _color_mode(MeterColors)
116 , _meter_color_mode_item(0)
117 , _channel_color_mode_item(0)
118 , _track_color_mode_item(0)
119 , _channel_selector (0)
120 , _step_edit_item (0)
121 , controller_menu (0)
127 MidiTimeAxisView::set_note_highlight (uint8_t note) {
128 _piano_roll_header->set_note_highlight (note);
132 MidiTimeAxisView::set_route (boost::shared_ptr<Route> rt)
136 _view = new MidiStreamView (*this);
139 _piano_roll_header = new PianoRollHeader(*midi_view());
140 _range_scroomer = new MidiScroomer(midi_view()->note_range_adjustment);
141 _range_scroomer->DoubleClicked.connect (
142 sigc::bind (sigc::mem_fun(*this, &MidiTimeAxisView::set_note_range),
143 MidiStreamView::ContentsRange, false));
146 /* This next call will result in our height being set up, so it must come after
147 the creation of the piano roll / range scroomer as their visibility is set up
150 RouteTimeAxisView::set_route (rt);
152 _view->apply_color (gdk_color_to_rgba (color()), StreamView::RegionColor);
154 subplugin_menu.set_name ("ArdourContextMenu");
156 if (!gui_property ("note-range-min").empty ()) {
157 midi_view()->apply_note_range (atoi (gui_property ("note-range-min").c_str()),
158 atoi (gui_property ("note-range-max").c_str()),
162 midi_view()->NoteRangeChanged.connect (
163 sigc::mem_fun (*this, &MidiTimeAxisView::note_range_changed));
164 _view->ContentsHeightChanged.connect (
165 sigc::mem_fun (*this, &MidiTimeAxisView::contents_height_changed));
167 ignore_toggle = false;
169 if (is_midi_track()) {
170 controls_ebox.set_name ("MidiTimeAxisViewControlsBaseUnselected");
171 time_axis_frame.set_name ("MidiTimeAxisViewControlsBaseUnselected");
172 _note_mode = midi_track()->note_mode();
173 } else { // MIDI bus (which doesn't exist yet..)
174 controls_ebox.set_name ("MidiBusControlsBaseUnselected");
175 time_axis_frame.set_name ("MidiBusControlsBaseUnselected");
178 /* if set_state above didn't create a gain automation child, we need to make one */
179 if (automation_child (GainAutomation) == 0) {
180 create_automation_child (GainAutomation, false);
183 /* if set_state above didn't create a mute automation child, we need to make one */
184 if (automation_child (MuteAutomation) == 0) {
185 create_automation_child (MuteAutomation, false);
188 if (_route->panner_shell()) {
189 _route->panner_shell()->Changed.connect (*this, invalidator (*this), boost::bind (&MidiTimeAxisView::ensure_pan_views, this, false), gui_context());
192 /* map current state of the route */
193 ensure_pan_views (false);
195 processors_changed (RouteProcessorChange ());
197 _route->processors_changed.connect (*this, invalidator (*this),
198 boost::bind (&MidiTimeAxisView::processors_changed, this, _1),
202 _piano_roll_header->SetNoteSelection.connect (
203 sigc::mem_fun (*this, &MidiTimeAxisView::set_note_selection));
204 _piano_roll_header->AddNoteSelection.connect (
205 sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection));
206 _piano_roll_header->ExtendNoteSelection.connect (
207 sigc::mem_fun (*this, &MidiTimeAxisView::extend_note_selection));
208 _piano_roll_header->ToggleNoteSelection.connect (
209 sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection));
211 /* Suspend updates of the StreamView during scroomer drags to speed things up */
212 _range_scroomer->DragStarting.connect (
213 sigc::mem_fun (*midi_view(), &MidiStreamView::suspend_updates));
214 _range_scroomer->DragFinishing.connect (
215 sigc::mem_fun (*midi_view(), &MidiStreamView::resume_updates));
217 /* Put the scroomer and the keyboard in a VBox with a padding
218 label so that they can be reduced in height for stacked-view
222 HSeparator* separator = manage (new HSeparator());
223 separator->set_name("TrackSeparator");
224 separator->set_size_request(-1, 1);
227 VBox* v = manage (new VBox);
228 HBox* h = manage (new HBox);
229 h->pack_end (*_piano_roll_header);
230 h->pack_end (*_range_scroomer);
231 v->pack_start (*separator, false, false);
232 v->pack_start (*h, true, true);
235 top_hbox.remove(scroomer_placeholder);
236 time_axis_hbox.pack_end(*v, false, false, 0);
237 midi_scroomer_size_group->add_widget (*v);
239 controls_ebox.set_name ("MidiTrackControlsBaseUnselected");
240 time_axis_frame.set_name ("MidiTrackControlsBaseUnselected");
241 controls_base_selected_name = "MidiTrackControlsBaseSelected";
242 controls_base_unselected_name = "MidiTrackControlsBaseUnselected";
244 midi_view()->NoteRangeChanged.connect (
245 sigc::mem_fun(*this, &MidiTimeAxisView::update_range));
247 /* ask for notifications of any new RegionViews */
248 _view->RegionViewAdded.connect (
249 sigc::mem_fun(*this, &MidiTimeAxisView::region_view_added));
251 midi_track()->playback_filter().ChannelModeChanged.connect (
252 *this, invalidator (*this),
253 boost::bind (&MidiTimeAxisView::playback_channel_mode_changed, this),
255 midi_track()->playback_filter().ChannelMaskChanged.connect (
256 *this, invalidator (*this),
257 boost::bind (&MidiTimeAxisView::playback_channel_mode_changed, this),
259 midi_track()->capture_filter().ChannelModeChanged.connect (
260 *this, invalidator (*this),
261 boost::bind (&MidiTimeAxisView::capture_channel_mode_changed, this),
263 midi_track()->capture_filter().ChannelMaskChanged.connect (
264 *this, invalidator (*this),
265 boost::bind (&MidiTimeAxisView::capture_channel_mode_changed, this),
268 playback_channel_mode_changed ();
269 capture_channel_mode_changed ();
271 if (!_editor.have_idled()) {
272 /* first idle will do what we need */
278 typedef MIDI::Name::MidiPatchManager PatchManager;
280 PatchManager& patch_manager = PatchManager::instance();
282 for (PatchManager::DeviceNamesByMaker::const_iterator m = patch_manager.devices_by_manufacturer().begin();
283 m != patch_manager.devices_by_manufacturer().end(); ++m) {
284 Menu* menu = Gtk::manage(new Menu);
285 Menu_Helpers::MenuList& items = menu->items();
287 // Build manufacturer submenu
288 for (MIDI::Name::MIDINameDocument::MasterDeviceNamesList::const_iterator n = m->second.begin();
289 n != m->second.end(); ++n) {
290 Menu_Helpers::MenuElem elem = Gtk::Menu_Helpers::MenuElem(
292 sigc::bind(sigc::mem_fun(*this, &MidiTimeAxisView::model_changed),
295 items.push_back(elem);
298 // Add manufacturer submenu to selector
299 _midnam_model_selector.AddMenuElem(Menu_Helpers::MenuElem(m->first, *menu));
302 if (gui_property (X_("midnam-model-name")).empty()) {
303 set_gui_property (X_("midnam-model-name"), "Generic");
306 if (gui_property (X_("midnam-custom-device-mode")).empty()) {
307 boost::shared_ptr<MIDI::Name::MasterDeviceNames> device_names = get_device_names();
309 set_gui_property (X_("midnam-custom-device-mode"),
310 *device_names->custom_device_mode_names().begin());
314 ARDOUR_UI::instance()->set_tip (_midnam_model_selector, _("External MIDI Device"));
315 ARDOUR_UI::instance()->set_tip (_midnam_custom_device_mode_selector, _("External Device Mode"));
317 _midi_controls_box.set_homogeneous(false);
318 _midi_controls_box.set_border_width (2);
320 _channel_status_box.set_homogeneous (false);
321 _channel_status_box.set_spacing (4);
323 ArdourButton *channel_selector_button = manage (new ArdourButton(_("Chns")));
324 channel_selector_button->set_name ("route button");
325 ARDOUR_UI::instance()->set_tip (channel_selector_button, _("Click to edit channel settings"));
327 // Insert expanding space labels to get full width justification
328 _channel_status_box.pack_start (_playback_channel_status, false, false, 2);
329 _channel_status_box.pack_start (*Gtk::manage(new Gtk::Label(" ")), true, true);
330 _channel_status_box.pack_start (_capture_channel_status, false, false, 2);
331 _channel_status_box.pack_start (*Gtk::manage(new Gtk::Label(" ")), true, true);
332 _channel_status_box.pack_end (*channel_selector_button, false, false);
333 _channel_status_box.show_all ();
335 channel_selector_button->signal_clicked.connect (sigc::mem_fun (*this, &MidiTimeAxisView::toggle_channel_selector));
337 _midi_controls_box.pack_start (_channel_status_box, false, false, 10);
339 if (!patch_manager.all_models().empty()) {
341 _midnam_model_selector.show ();
342 _midi_controls_box.pack_start (_midnam_model_selector, false, false, 2);
344 _midnam_custom_device_mode_selector.show ();
346 _midi_controls_box.pack_start (_midnam_custom_device_mode_selector, false, false, 2);
349 model_changed(gui_property(X_("midnam-model-name")));
350 custom_device_mode_changed(gui_property(X_("midnam-custom-device-mode")));
352 controls_vbox.pack_start(_midi_controls_box, false, false);
354 const string color_mode = gui_property ("color-mode");
355 if (!color_mode.empty()) {
356 _color_mode = ColorMode (string_2_enum(color_mode, _color_mode));
357 if (_channel_selector && _color_mode == ChannelColors) {
358 _channel_selector->set_channel_colors(NoteBase::midi_channel_colors);
362 set_color_mode (_color_mode, true, false);
364 const string note_mode = gui_property ("note-mode");
365 if (!note_mode.empty()) {
366 _note_mode = NoteMode (string_2_enum (note_mode, _note_mode));
367 if (_percussion_mode_item) {
368 _percussion_mode_item->set_active (_note_mode == Percussive);
372 /* Look for any GUI object state nodes that represent automation children
373 * that should exist, and create the children.
376 const list<string> gui_ids = gui_object_state().all_ids ();
377 for (list<string>::const_iterator i = gui_ids.begin(); i != gui_ids.end(); ++i) {
380 Evoral::Parameter parameter (0, 0, 0);
382 bool const p = AutomationTimeAxisView::parse_state_id (
383 *i, route_id, has_parameter, parameter);
384 if (p && route_id == _route->id () && has_parameter) {
385 const std::string& visible = gui_object_state().get_string (*i, X_("visible"));
386 create_automation_child (parameter, string_is_affirmative (visible));
392 MidiTimeAxisView::first_idle ()
399 MidiTimeAxisView::~MidiTimeAxisView ()
401 delete _channel_selector;
403 delete _piano_roll_header;
404 _piano_roll_header = 0;
406 delete _range_scroomer;
409 delete controller_menu;
414 MidiTimeAxisView::check_step_edit ()
416 ensure_step_editor ();
417 _step_editor->check_step_edit ();
421 MidiTimeAxisView::model_changed(const std::string& model)
423 set_gui_property (X_("midnam-model-name"), model);
425 const std::list<std::string> device_modes = MIDI::Name::MidiPatchManager::instance()
426 .custom_device_mode_names_by_model(model);
428 _midnam_model_selector.set_text(model);
429 _midnam_custom_device_mode_selector.clear_items();
431 for (std::list<std::string>::const_iterator i = device_modes.begin();
432 i != device_modes.end(); ++i) {
433 _midnam_custom_device_mode_selector.AddMenuElem(
434 Gtk::Menu_Helpers::MenuElem(
435 *i, sigc::bind(sigc::mem_fun(*this, &MidiTimeAxisView::custom_device_mode_changed),
439 if (!device_modes.empty()) {
440 custom_device_mode_changed(device_modes.front());
443 if (device_modes.size() > 1) {
444 _midnam_custom_device_mode_selector.show();
446 _midnam_custom_device_mode_selector.hide();
449 if (device_modes.size() > 0) {
450 _route->instrument_info().set_external_instrument (model, device_modes.front());
452 _route->instrument_info().set_external_instrument (model, "");
455 // Rebuild controller menu
456 _controller_menu_map.clear ();
457 delete controller_menu;
459 build_automation_action_menu(false);
463 MidiTimeAxisView::custom_device_mode_changed(const std::string& mode)
465 const std::string model = gui_property (X_("midnam-model-name"));
467 set_gui_property (X_("midnam-custom-device-mode"), mode);
468 _midnam_custom_device_mode_selector.set_text(mode);
469 _route->instrument_info().set_external_instrument (model, mode);
473 MidiTimeAxisView::midi_view()
475 return dynamic_cast<MidiStreamView*>(_view);
479 MidiTimeAxisView::set_height (uint32_t h, TrackHeightMode m)
481 if (h >= MIDI_CONTROLS_BOX_MIN_HEIGHT) {
482 _midi_controls_box.show ();
484 _midi_controls_box.hide();
487 if (h >= KEYBOARD_MIN_HEIGHT) {
488 if (is_track() && _range_scroomer) {
489 _range_scroomer->show();
491 if (is_track() && _piano_roll_header) {
492 _piano_roll_header->show();
495 if (is_track() && _range_scroomer) {
496 _range_scroomer->hide();
498 if (is_track() && _piano_roll_header) {
499 _piano_roll_header->hide();
503 /* We need to do this after changing visibility of our stuff, as it will
504 eventually trigger a call to Editor::reset_controls_layout_width(),
505 which needs to know if we have just shown or hidden a scroomer /
508 RouteTimeAxisView::set_height (h, m);
512 MidiTimeAxisView::append_extra_display_menu_items ()
514 using namespace Menu_Helpers;
516 MenuList& items = display_menu->items();
519 Menu *range_menu = manage(new Menu);
520 MenuList& range_items = range_menu->items();
521 range_menu->set_name ("ArdourContextMenu");
523 range_items.push_back (
524 MenuElem (_("Show Full Range"),
525 sigc::bind (sigc::mem_fun(*this, &MidiTimeAxisView::set_note_range),
526 MidiStreamView::FullRange, true)));
528 range_items.push_back (
529 MenuElem (_("Fit Contents"),
530 sigc::bind (sigc::mem_fun(*this, &MidiTimeAxisView::set_note_range),
531 MidiStreamView::ContentsRange, true)));
533 items.push_back (MenuElem (_("Note Range"), *range_menu));
534 items.push_back (MenuElem (_("Note Mode"), *build_note_mode_menu()));
535 items.push_back (MenuElem (_("Channel Selector"),
536 sigc::mem_fun(*this, &MidiTimeAxisView::toggle_channel_selector)));
538 color_mode_menu = build_color_mode_menu();
539 if (color_mode_menu) {
540 items.push_back (MenuElem (_("Color Mode"), *color_mode_menu));
543 items.push_back (SeparatorElem ());
547 MidiTimeAxisView::toggle_channel_selector ()
549 if (!_channel_selector) {
550 _channel_selector = new MidiChannelSelectorWindow (midi_track());
552 if (_color_mode == ChannelColors) {
553 _channel_selector->set_channel_colors(NoteBase::midi_channel_colors);
555 _channel_selector->set_default_channel_color ();
558 _channel_selector->show_all ();
560 _channel_selector->cycle_visibility ();
565 MidiTimeAxisView::build_automation_action_menu (bool for_selection)
567 using namespace Menu_Helpers;
569 /* If we have a controller menu, we need to detach it before
570 RouteTimeAxis::build_automation_action_menu destroys the
571 menu it is attached to. Otherwise GTK destroys
572 controller_menu's gobj, meaning that it can't be reattached
573 below. See bug #3134.
576 if (controller_menu) {
577 detach_menu (*controller_menu);
580 _channel_command_menu_map.clear ();
581 RouteTimeAxisView::build_automation_action_menu (for_selection);
583 MenuList& automation_items = automation_action_menu->items();
585 uint16_t selected_channels = midi_track()->get_playback_channel_mask();
587 if (selected_channels != 0) {
589 automation_items.push_back (SeparatorElem());
591 /* these 2 MIDI "command" types are semantically more like automation
592 than note data, but they are not MIDI controllers. We give them
593 special status in this menu, since they will not show up in the
594 controller list and anyone who actually knows something about MIDI
595 (!) would not expect to find them there.
598 add_channel_command_menu_item (
599 automation_items, _("Bender"), MidiPitchBenderAutomation, 0);
600 automation_items.back().set_sensitive (
601 !for_selection || _editor.get_selection().tracks.size() == 1);
602 add_channel_command_menu_item (
603 automation_items, _("Pressure"), MidiChannelPressureAutomation, 0);
604 automation_items.back().set_sensitive (
605 !for_selection || _editor.get_selection().tracks.size() == 1);
607 /* now all MIDI controllers. Always offer the possibility that we will
608 rebuild the controllers menu since it might need to be updated after
609 a channel mode change or other change. Also detach it first in case
610 it has been used anywhere else.
613 build_controller_menu ();
615 automation_items.push_back (MenuElem (_("Controllers"), *controller_menu));
616 automation_items.back().set_sensitive (
617 !for_selection || _editor.get_selection().tracks.size() == 1);
619 automation_items.push_back (
620 MenuElem (string_compose ("<i>%1</i>", _("No MIDI Channels selected"))));
621 dynamic_cast<Label*> (automation_items.back().get_child())->set_use_markup (true);
626 MidiTimeAxisView::change_all_channel_tracks_visibility (bool yn, Evoral::Parameter param)
628 const uint16_t selected_channels = midi_track()->get_playback_channel_mask();
630 for (uint8_t chn = 0; chn < 16; chn++) {
631 if (selected_channels & (0x0001 << chn)) {
633 Evoral::Parameter fully_qualified_param (param.type(), chn, param.id());
634 Gtk::CheckMenuItem* menu = automation_child_menu_item (fully_qualified_param);
637 menu->set_active (yn);
644 MidiTimeAxisView::add_channel_command_menu_item (Menu_Helpers::MenuList& items,
646 AutomationType auto_type,
649 using namespace Menu_Helpers;
651 /* count the number of selected channels because we will build a different menu
652 structure if there is more than 1 selected.
655 const uint16_t selected_channels = midi_track()->get_playback_channel_mask();
658 for (uint8_t chn = 0; chn < 16; chn++) {
659 if (selected_channels & (0x0001 << chn)) {
668 /* multiple channels - create a submenu, with 1 item per channel */
670 Menu* chn_menu = manage (new Menu);
671 MenuList& chn_items (chn_menu->items());
672 Evoral::Parameter param_without_channel (auto_type, 0, cmd);
674 /* add a couple of items to hide/show all of them */
676 chn_items.push_back (
677 MenuElem (_("Hide all channels"),
678 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
679 false, param_without_channel)));
680 chn_items.push_back (
681 MenuElem (_("Show all channels"),
682 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
683 true, param_without_channel)));
685 for (uint8_t chn = 0; chn < 16; chn++) {
686 if (selected_channels & (0x0001 << chn)) {
688 /* for each selected channel, add a menu item for this controller */
690 Evoral::Parameter fully_qualified_param (auto_type, chn, cmd);
691 chn_items.push_back (
692 CheckMenuElem (string_compose (_("Channel %1"), chn+1),
693 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
694 fully_qualified_param)));
696 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
697 bool visible = false;
700 if (track->marked_for_display()) {
705 Gtk::CheckMenuItem* cmi = static_cast<Gtk::CheckMenuItem*>(&chn_items.back());
706 _channel_command_menu_map[fully_qualified_param] = cmi;
707 cmi->set_active (visible);
711 /* now create an item in the parent menu that has the per-channel list as a submenu */
713 items.push_back (MenuElem (label, *chn_menu));
717 /* just one channel - create a single menu item for this command+channel combination*/
719 for (uint8_t chn = 0; chn < 16; chn++) {
720 if (selected_channels & (0x0001 << chn)) {
722 Evoral::Parameter fully_qualified_param (auto_type, chn, cmd);
724 CheckMenuElem (label,
725 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
726 fully_qualified_param)));
728 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
729 bool visible = false;
732 if (track->marked_for_display()) {
737 Gtk::CheckMenuItem* cmi = static_cast<Gtk::CheckMenuItem*>(&items.back());
738 _channel_command_menu_map[fully_qualified_param] = cmi;
739 cmi->set_active (visible);
741 /* one channel only */
748 /** Add a single menu item for a controller on one channel. */
750 MidiTimeAxisView::add_single_channel_controller_item(Menu_Helpers::MenuList& ctl_items,
752 const std::string& name)
754 using namespace Menu_Helpers;
756 const uint16_t selected_channels = midi_track()->get_playback_channel_mask();
757 for (uint8_t chn = 0; chn < 16; chn++) {
758 if (selected_channels & (0x0001 << chn)) {
760 Evoral::Parameter fully_qualified_param (MidiCCAutomation, chn, ctl);
761 ctl_items.push_back (
763 string_compose ("<b>%1</b>: %2 [%3]", ctl, name, int (chn + 1)),
765 sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
766 fully_qualified_param)));
767 dynamic_cast<Label*> (ctl_items.back().get_child())->set_use_markup (true);
769 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (
770 fully_qualified_param);
772 bool visible = false;
774 if (track->marked_for_display()) {
779 Gtk::CheckMenuItem* cmi = static_cast<Gtk::CheckMenuItem*>(&ctl_items.back());
780 _controller_menu_map[fully_qualified_param] = cmi;
781 cmi->set_active (visible);
783 /* one channel only */
789 /** Add a submenu with 1 item per channel for a controller on many channels. */
791 MidiTimeAxisView::add_multi_channel_controller_item(Menu_Helpers::MenuList& ctl_items,
793 const std::string& name)
795 using namespace Menu_Helpers;
797 const uint16_t selected_channels = midi_track()->get_playback_channel_mask();
799 Menu* chn_menu = manage (new Menu);
800 MenuList& chn_items (chn_menu->items());
802 /* add a couple of items to hide/show this controller on all channels */
804 Evoral::Parameter param_without_channel (MidiCCAutomation, 0, ctl);
805 chn_items.push_back (
806 MenuElem (_("Hide all channels"),
807 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
808 false, param_without_channel)));
809 chn_items.push_back (
810 MenuElem (_("Show all channels"),
811 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
812 true, param_without_channel)));
814 for (uint8_t chn = 0; chn < 16; chn++) {
815 if (selected_channels & (0x0001 << chn)) {
817 /* for each selected channel, add a menu item for this controller */
819 Evoral::Parameter fully_qualified_param (MidiCCAutomation, chn, ctl);
820 chn_items.push_back (
821 CheckMenuElem (string_compose (_("Channel %1"), chn+1),
822 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
823 fully_qualified_param)));
825 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (
826 fully_qualified_param);
827 bool visible = false;
830 if (track->marked_for_display()) {
835 Gtk::CheckMenuItem* cmi = static_cast<Gtk::CheckMenuItem*>(&chn_items.back());
836 _controller_menu_map[fully_qualified_param] = cmi;
837 cmi->set_active (visible);
841 /* add the per-channel menu to the list of controllers, with the name of the controller */
842 ctl_items.push_back (MenuElem (string_compose ("<b>%1</b>: %2", ctl, name),
844 dynamic_cast<Label*> (ctl_items.back().get_child())->set_use_markup (true);
847 boost::shared_ptr<MIDI::Name::CustomDeviceMode>
848 MidiTimeAxisView::get_device_mode()
850 using namespace MIDI::Name;
852 boost::shared_ptr<MasterDeviceNames> device_names = get_device_names();
854 return boost::shared_ptr<MIDI::Name::CustomDeviceMode>();
857 return device_names->custom_device_mode_by_name(
858 gui_property (X_("midnam-custom-device-mode")));
861 boost::shared_ptr<MIDI::Name::MasterDeviceNames>
862 MidiTimeAxisView::get_device_names()
864 using namespace MIDI::Name;
866 const std::string model = gui_property (X_("midnam-model-name"));
868 boost::shared_ptr<MIDINameDocument> midnam = MidiPatchManager::instance()
869 .document_by_model(model);
871 return midnam->master_device_names(model);
873 return boost::shared_ptr<MasterDeviceNames>();
878 MidiTimeAxisView::build_controller_menu ()
880 using namespace Menu_Helpers;
882 if (controller_menu) {
883 /* it exists and has not been invalidated by a channel mode change */
887 controller_menu = new Menu; // explicitly managed by us
888 MenuList& items (controller_menu->items());
890 /* create several "top level" menu items for sets of controllers (16 at a
891 time), and populate each one with a submenu for each controller+channel
892 combination covering the currently selected channels for this track
895 const uint16_t selected_channels = midi_track()->get_playback_channel_mask();
897 /* count the number of selected channels because we will build a different menu
898 structure if there is more than 1 selected.
902 for (uint8_t chn = 0; chn < 16; chn++) {
903 if (selected_channels & (0x0001 << chn)) {
910 using namespace MIDI::Name;
911 boost::shared_ptr<MasterDeviceNames> device_names = get_device_names();
913 if (device_names && !device_names->controls().empty()) {
914 /* Controllers names available in midnam file, generate fancy menu */
915 unsigned n_items = 0;
916 unsigned n_groups = 0;
918 /* TODO: This is not correct, should look up the currently applicable ControlNameList
919 and only build a menu for that one. */
920 for (MasterDeviceNames::ControlNameLists::const_iterator l = device_names->controls().begin();
921 l != device_names->controls().end(); ++l) {
922 boost::shared_ptr<ControlNameList> name_list = l->second;
923 Menu* ctl_menu = NULL;
925 for (ControlNameList::Controls::const_iterator c = name_list->controls().begin();
926 c != name_list->controls().end();) {
927 const uint16_t ctl = c->second->number();
928 if (ctl != MIDI_CTL_MSB_BANK && ctl != MIDI_CTL_LSB_BANK) {
929 /* Skip bank select controllers since they're handled specially */
931 /* Create a new submenu */
932 ctl_menu = manage (new Menu);
935 MenuList& ctl_items (ctl_menu->items());
937 add_multi_channel_controller_item(ctl_items, ctl, c->second->name());
939 add_single_channel_controller_item(ctl_items, ctl, c->second->name());
944 if (ctl_menu && (++n_items == 16 || c == name_list->controls().end())) {
945 /* Submenu has 16 items or we're done, add it to controller menu and reset */
947 MenuElem(string_compose(_("Controllers %1-%2"),
948 (16 * n_groups), (16 * n_groups) + n_items - 1),
957 /* No controllers names, generate generic numeric menu */
958 for (int i = 0; i < 127; i += 16) {
959 Menu* ctl_menu = manage (new Menu);
960 MenuList& ctl_items (ctl_menu->items());
962 for (int ctl = i; ctl < i+16; ++ctl) {
963 if (ctl == MIDI_CTL_MSB_BANK || ctl == MIDI_CTL_LSB_BANK) {
964 /* Skip bank select controllers since they're handled specially */
969 add_multi_channel_controller_item(
970 ctl_items, ctl, string_compose(_("Controller %1"), ctl));
972 add_single_channel_controller_item(
973 ctl_items, ctl, string_compose(_("Controller %1"), ctl));
977 /* Add submenu for this block of controllers to controller menu */
979 MenuElem (string_compose (_("Controllers %1-%2"), i, i + 15),
986 MidiTimeAxisView::build_note_mode_menu()
988 using namespace Menu_Helpers;
990 Menu* mode_menu = manage (new Menu);
991 MenuList& items = mode_menu->items();
992 mode_menu->set_name ("ArdourContextMenu");
994 RadioMenuItem::Group mode_group;
996 RadioMenuElem (mode_group,_("Sustained"),
997 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_mode),
999 _note_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
1000 _note_mode_item->set_active(_note_mode == Sustained);
1003 RadioMenuElem (mode_group, _("Percussive"),
1004 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_mode),
1005 Percussive, true)));
1006 _percussion_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
1007 _percussion_mode_item->set_active(_note_mode == Percussive);
1013 MidiTimeAxisView::build_color_mode_menu()
1015 using namespace Menu_Helpers;
1017 Menu* mode_menu = manage (new Menu);
1018 MenuList& items = mode_menu->items();
1019 mode_menu->set_name ("ArdourContextMenu");
1021 RadioMenuItem::Group mode_group;
1023 RadioMenuElem (mode_group, _("Meter Colors"),
1024 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode),
1025 MeterColors, false, true, true)));
1026 _meter_color_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
1027 _meter_color_mode_item->set_active(_color_mode == MeterColors);
1030 RadioMenuElem (mode_group, _("Channel Colors"),
1031 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode),
1032 ChannelColors, false, true, true)));
1033 _channel_color_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
1034 _channel_color_mode_item->set_active(_color_mode == ChannelColors);
1037 RadioMenuElem (mode_group, _("Track Color"),
1038 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode),
1039 TrackColor, false, true, true)));
1040 _channel_color_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
1041 _channel_color_mode_item->set_active(_color_mode == TrackColor);
1047 MidiTimeAxisView::set_note_mode(NoteMode mode, bool apply_to_selection)
1049 if (apply_to_selection) {
1050 _editor.get_selection().tracks.foreach_midi_time_axis (
1051 boost::bind (&MidiTimeAxisView::set_note_mode, _1, mode, false));
1053 if (_note_mode != mode || midi_track()->note_mode() != mode) {
1055 midi_track()->set_note_mode(mode);
1056 set_gui_property ("note-mode", enum_2_string(_note_mode));
1057 _view->redisplay_track();
1063 MidiTimeAxisView::set_color_mode (ColorMode mode, bool force, bool redisplay, bool apply_to_selection)
1065 if (apply_to_selection) {
1066 _editor.get_selection().tracks.foreach_midi_time_axis (
1067 boost::bind (&MidiTimeAxisView::set_color_mode, _1, mode, force, redisplay, false));
1069 if (_color_mode == mode && !force) {
1073 if (_channel_selector) {
1074 if (mode == ChannelColors) {
1075 _channel_selector->set_channel_colors(NoteBase::midi_channel_colors);
1077 _channel_selector->set_default_channel_color();
1082 set_gui_property ("color-mode", enum_2_string(_color_mode));
1084 _view->redisplay_track();
1090 MidiTimeAxisView::set_note_range (MidiStreamView::VisibleNoteRange range, bool apply_to_selection)
1092 if (apply_to_selection) {
1093 _editor.get_selection().tracks.foreach_midi_time_axis (
1094 boost::bind (&MidiTimeAxisView::set_note_range, _1, range, false));
1096 if (!_ignore_signals) {
1097 midi_view()->set_note_range(range);
1103 MidiTimeAxisView::update_range()
1105 MidiGhostRegion* mgr;
1107 for (list<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
1108 if ((mgr = dynamic_cast<MidiGhostRegion*>(*i)) != 0) {
1109 mgr->update_range();
1115 MidiTimeAxisView::show_all_automation (bool apply_to_selection)
1117 using namespace MIDI::Name;
1119 if (apply_to_selection) {
1120 _editor.get_selection().tracks.foreach_midi_time_axis (
1121 boost::bind (&MidiTimeAxisView::show_all_automation, _1, false));
1124 // Show existing automation
1125 const set<Evoral::Parameter> params = midi_track()->midi_playlist()->contained_automation();
1127 for (set<Evoral::Parameter>::const_iterator i = params.begin(); i != params.end(); ++i) {
1128 create_automation_child(*i, true);
1131 // Show automation for all controllers named in midnam file
1132 boost::shared_ptr<MasterDeviceNames> device_names = get_device_names();
1133 if (gui_property (X_("midnam-model-name")) != "Generic" &&
1134 device_names && !device_names->controls().empty()) {
1135 const std::string device_mode = gui_property (X_("midnam-custom-device-mode"));
1136 const uint16_t selected_channels = midi_track()->get_playback_channel_mask();
1137 for (uint32_t chn = 0; chn < 16; ++chn) {
1138 if ((selected_channels & (0x0001 << chn)) == 0) {
1139 // Channel not in use
1143 boost::shared_ptr<ChannelNameSet> chan_names = device_names->channel_name_set_by_channel(
1149 boost::shared_ptr<ControlNameList> control_names = device_names->control_name_list(
1150 chan_names->control_list_name());
1151 if (!control_names) {
1155 for (ControlNameList::Controls::const_iterator c = control_names->controls().begin();
1156 c != control_names->controls().end();
1158 const uint16_t ctl = c->second->number();
1159 if (ctl != MIDI_CTL_MSB_BANK && ctl != MIDI_CTL_LSB_BANK) {
1160 /* Skip bank select controllers since they're handled specially */
1161 const Evoral::Parameter param(MidiCCAutomation, chn, ctl);
1162 create_automation_child(param, true);
1169 RouteTimeAxisView::show_all_automation ();
1174 MidiTimeAxisView::show_existing_automation (bool apply_to_selection)
1176 if (apply_to_selection) {
1177 _editor.get_selection().tracks.foreach_midi_time_axis (
1178 boost::bind (&MidiTimeAxisView::show_existing_automation, _1, false));
1181 const set<Evoral::Parameter> params = midi_track()->midi_playlist()->contained_automation();
1183 for (set<Evoral::Parameter>::const_iterator i = params.begin(); i != params.end(); ++i) {
1184 create_automation_child (*i, true);
1188 RouteTimeAxisView::show_existing_automation ();
1192 /** Create an automation track for the given parameter (pitch bend, channel pressure).
1195 MidiTimeAxisView::create_automation_child (const Evoral::Parameter& param, bool show)
1197 if (param.type() == NullAutomation) {
1201 AutomationTracks::iterator existing = _automation_tracks.find (param);
1203 if (existing != _automation_tracks.end()) {
1205 /* automation track created because we had existing data for
1206 * the processor, but visibility may need to be controlled
1207 * since it will have been set visible by default.
1210 existing->second->set_marked_for_display (show);
1219 boost::shared_ptr<AutomationTimeAxisView> track;
1220 boost::shared_ptr<AutomationControl> control;
1223 switch (param.type()) {
1225 case GainAutomation:
1226 create_gain_automation_child (param, show);
1229 case MuteAutomation:
1230 create_mute_automation_child (param, show);
1233 case PluginAutomation:
1234 /* handled elsewhere */
1237 case MidiCCAutomation:
1238 case MidiPgmChangeAutomation:
1239 case MidiPitchBenderAutomation:
1240 case MidiChannelPressureAutomation:
1241 case MidiSystemExclusiveAutomation:
1242 /* These controllers are region "automation" - they are owned
1243 * by regions (and their MidiModels), not by the track. As a
1244 * result there is no AutomationList/Line for the track, but we create
1245 * a controller for the user to write immediate events, so the editor
1246 * can act as a control surface for the present MIDI controllers.
1248 * TODO: Record manipulation of the controller to regions?
1251 control = _route->automation_control(param, true);
1252 track.reset (new AutomationTimeAxisView (
1255 control ? _route : boost::shared_ptr<Automatable> (),
1262 _route->describe_parameter(param)));
1265 _view->foreach_regionview (
1266 sigc::mem_fun (*track.get(), &TimeAxisView::add_ghost));
1269 add_automation_child (param, track, show);
1272 case PanWidthAutomation:
1273 case PanElevationAutomation:
1274 case PanAzimuthAutomation:
1275 ensure_pan_views (show);
1279 error << "MidiTimeAxisView: unknown automation child "
1280 << EventTypeMap::instance().to_symbol(param) << endmsg;
1285 MidiTimeAxisView::route_active_changed ()
1287 RouteUI::route_active_changed ();
1290 if (_route->active()) {
1291 controls_ebox.set_name ("MidiTrackControlsBaseUnselected");
1292 time_axis_frame.set_name ("MidiTrackControlsBaseUnselected");
1293 controls_base_selected_name = "MidiTrackControlsBaseSelected";
1294 controls_base_unselected_name = "MidiTrackControlsBaseUnselected";
1296 controls_ebox.set_name ("MidiTrackControlsBaseInactiveUnselected");
1297 time_axis_frame.set_name ("MidiTrackControlsBaseInactiveUnselected");
1298 controls_base_selected_name = "MidiTrackControlsBaseInactiveSelected";
1299 controls_base_unselected_name = "MidiTrackControlsBaseInactiveUnselected";
1302 if (_route->active()) {
1303 controls_ebox.set_name ("BusControlsBaseUnselected");
1304 time_axis_frame.set_name ("BusControlsBaseUnselected");
1305 controls_base_selected_name = "BusControlsBaseSelected";
1306 controls_base_unselected_name = "BusControlsBaseUnselected";
1308 controls_ebox.set_name ("BusControlsBaseInactiveUnselected");
1309 time_axis_frame.set_name ("BusControlsBaseInactiveUnselected");
1310 controls_base_selected_name = "BusControlsBaseInactiveSelected";
1311 controls_base_unselected_name = "BusControlsBaseInactiveUnselected";
1317 MidiTimeAxisView::set_note_selection (uint8_t note)
1319 uint16_t chn_mask = midi_track()->get_playback_channel_mask();
1321 _editor.begin_reversible_selection_op (X_("Set Note Selection"));
1323 if (_view->num_selected_regionviews() == 0) {
1324 _view->foreach_regionview (
1325 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_selection_region_view),
1328 _view->foreach_selected_regionview (
1329 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_selection_region_view),
1333 _editor.commit_reversible_selection_op();
1337 MidiTimeAxisView::add_note_selection (uint8_t note)
1339 const uint16_t chn_mask = midi_track()->get_playback_channel_mask();
1341 _editor.begin_reversible_selection_op (X_("Add Note Selection"));
1343 if (_view->num_selected_regionviews() == 0) {
1344 _view->foreach_regionview (
1345 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection_region_view),
1348 _view->foreach_selected_regionview (
1349 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection_region_view),
1353 _editor.commit_reversible_selection_op();
1357 MidiTimeAxisView::extend_note_selection (uint8_t note)
1359 const uint16_t chn_mask = midi_track()->get_playback_channel_mask();
1361 _editor.begin_reversible_selection_op (X_("Extend Note Selection"));
1363 if (_view->num_selected_regionviews() == 0) {
1364 _view->foreach_regionview (
1365 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::extend_note_selection_region_view),
1368 _view->foreach_selected_regionview (
1369 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::extend_note_selection_region_view),
1373 _editor.commit_reversible_selection_op();
1377 MidiTimeAxisView::toggle_note_selection (uint8_t note)
1379 const uint16_t chn_mask = midi_track()->get_playback_channel_mask();
1381 _editor.begin_reversible_selection_op (X_("Toggle Note Selection"));
1383 if (_view->num_selected_regionviews() == 0) {
1384 _view->foreach_regionview (
1385 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection_region_view),
1388 _view->foreach_selected_regionview (
1389 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection_region_view),
1393 _editor.commit_reversible_selection_op();
1397 MidiTimeAxisView::get_per_region_note_selection (list<pair<PBD::ID, set<boost::shared_ptr<Evoral::Note<Evoral::Beats> > > > >& selection)
1399 _view->foreach_regionview (
1400 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::get_per_region_note_selection_region_view), sigc::ref(selection)));
1404 MidiTimeAxisView::set_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1406 dynamic_cast<MidiRegionView*>(rv)->select_matching_notes (note, chn_mask, false, false);
1410 MidiTimeAxisView::add_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1412 dynamic_cast<MidiRegionView*>(rv)->select_matching_notes (note, chn_mask, true, false);
1416 MidiTimeAxisView::extend_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1418 dynamic_cast<MidiRegionView*>(rv)->select_matching_notes (note, chn_mask, true, true);
1422 MidiTimeAxisView::toggle_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1424 dynamic_cast<MidiRegionView*>(rv)->toggle_matching_notes (note, chn_mask);
1428 MidiTimeAxisView::get_per_region_note_selection_region_view (RegionView* rv, list<pair<PBD::ID, std::set<boost::shared_ptr<Evoral::Note<Evoral::Beats> > > > > &selection)
1430 Evoral::Sequence<Evoral::Beats>::Notes selected;
1431 dynamic_cast<MidiRegionView*>(rv)->selection_as_notelist (selected, false);
1433 std::set<boost::shared_ptr<Evoral::Note<Evoral::Beats> > > notes;
1435 Evoral::Sequence<Evoral::Beats>::Notes::iterator sel_it;
1436 for (sel_it = selected.begin(); sel_it != selected.end(); ++sel_it) {
1437 notes.insert (*sel_it);
1440 if (!notes.empty()) {
1441 selection.push_back (make_pair ((rv)->region()->id(), notes));
1446 MidiTimeAxisView::set_channel_mode (ChannelMode, uint16_t)
1448 /* hide all automation tracks that use the wrong channel(s) and show all those that use
1452 const uint16_t selected_channels = midi_track()->get_playback_channel_mask();
1453 bool changed = false;
1457 for (uint32_t ctl = 0; ctl < 127; ++ctl) {
1459 for (uint32_t chn = 0; chn < 16; ++chn) {
1460 Evoral::Parameter fully_qualified_param (MidiCCAutomation, chn, ctl);
1461 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
1467 if ((selected_channels & (0x0001 << chn)) == 0) {
1468 /* channel not in use. hiding it will trigger RouteTimeAxisView::automation_track_hidden()
1469 which will cause a redraw. We don't want one per channel, so block that with no_redraw.
1471 changed = track->set_marked_for_display (false) || changed;
1473 changed = track->set_marked_for_display (true) || changed;
1480 /* TODO: Bender, Pressure */
1482 /* invalidate the controller menu, so that we rebuild it next time */
1483 _controller_menu_map.clear ();
1484 delete controller_menu;
1485 controller_menu = 0;
1493 MidiTimeAxisView::automation_child_menu_item (Evoral::Parameter param)
1495 Gtk::CheckMenuItem* m = RouteTimeAxisView::automation_child_menu_item (param);
1500 ParameterMenuMap::iterator i = _controller_menu_map.find (param);
1501 if (i != _controller_menu_map.end()) {
1505 i = _channel_command_menu_map.find (param);
1506 if (i != _channel_command_menu_map.end()) {
1513 boost::shared_ptr<MidiRegion>
1514 MidiTimeAxisView::add_region (framepos_t pos, framecnt_t length, bool commit)
1516 Editor* real_editor = dynamic_cast<Editor*> (&_editor);
1518 real_editor->begin_reversible_command (Operations::create_region);
1520 playlist()->clear_changes ();
1522 real_editor->snap_to (pos, RoundNearest);
1524 boost::shared_ptr<Source> src = _session->create_midi_source_by_stealing_name (view()->trackview().track());
1527 plist.add (ARDOUR::Properties::start, 0);
1528 plist.add (ARDOUR::Properties::length, length);
1529 plist.add (ARDOUR::Properties::name, PBD::basename_nosuffix(src->name()));
1531 boost::shared_ptr<Region> region = (RegionFactory::create (src, plist));
1533 playlist()->add_region (region, pos);
1534 _session->add_command (new StatefulDiffCommand (playlist()));
1537 real_editor->commit_reversible_command ();
1540 return boost::dynamic_pointer_cast<MidiRegion>(region);
1544 MidiTimeAxisView::ensure_step_editor ()
1546 if (!_step_editor) {
1547 _step_editor = new StepEditor (_editor, midi_track(), *this);
1552 MidiTimeAxisView::start_step_editing ()
1554 ensure_step_editor ();
1555 _step_editor->start_step_editing ();
1559 MidiTimeAxisView::stop_step_editing ()
1562 _step_editor->stop_step_editing ();
1566 /** @return channel (counted from 0) to add an event to, based on the current setting
1567 * of the channel selector.
1570 MidiTimeAxisView::get_channel_for_add () const
1572 uint16_t const chn_mask = midi_track()->get_playback_channel_mask();
1574 uint8_t channel = 0;
1576 /* pick the highest selected channel, unless all channels are selected,
1577 which is interpreted to mean channel 1 (zero)
1580 for (uint16_t i = 0; i < 16; ++i) {
1581 if (chn_mask & (1<<i)) {
1587 if (chn_cnt == 16) {
1595 MidiTimeAxisView::note_range_changed ()
1597 set_gui_property ("note-range-min", (int) midi_view()->lowest_note ());
1598 set_gui_property ("note-range-max", (int) midi_view()->highest_note ());
1602 MidiTimeAxisView::contents_height_changed ()
1604 _range_scroomer->queue_resize ();
1608 MidiTimeAxisView::playback_channel_mode_changed ()
1610 switch (midi_track()->get_playback_channel_mode()) {
1612 _playback_channel_status.set_markup (string_compose ("<b>%1</b>: <i>%2</i>", _("Play"), _("all")));
1614 case FilterChannels:
1615 _playback_channel_status.set_markup (string_compose ("<b>%1</b>: <i>%2</i>", _("Play"), _("some")));
1618 _playback_channel_status.set_markup (string_compose ("<b>%1</b>: <i>%2>%3</i>", _("Play"), _("all"), PBD::ffs (midi_track()->get_playback_channel_mask())));
1624 MidiTimeAxisView::capture_channel_mode_changed ()
1626 switch (midi_track()->get_capture_channel_mode()) {
1628 _capture_channel_status.set_markup (string_compose ("<b>%1</b>: <i>%2</i>", _("Rec"), _("all")));
1630 case FilterChannels:
1631 _capture_channel_status.set_markup (string_compose ("<b>%1</b>: <i>%2</i>", _("Rec"), _("some")));
1634 _capture_channel_status.set_markup (string_compose ("<b>%1</b>: <i>%2>%3</i>", _("Rec"), _("all"), PBD::ffs (midi_track()->get_capture_channel_mask())));
1640 MidiTimeAxisView::paste (framepos_t pos, const Selection& selection, PasteContext& ctx)
1642 if (!_editor.internal_editing()) {
1643 // Non-internal paste, paste regions like any other route
1644 return RouteTimeAxisView::paste(pos, selection, ctx);
1647 return midi_view()->paste(pos, selection, ctx);