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_button.h"
64 #include "automation_line.h"
65 #include "automation_time_axis.h"
68 #include "ghostregion.h"
69 #include "gui_thread.h"
71 #include "midi_channel_selector.h"
72 #include "midi_scroomer.h"
73 #include "midi_streamview.h"
74 #include "midi_region_view.h"
75 #include "midi_time_axis.h"
76 #include "piano_roll_header.h"
77 #include "playlist_selector.h"
78 #include "plugin_selector.h"
79 #include "plugin_ui.h"
80 #include "point_selection.h"
82 #include "region_view.h"
83 #include "rgb_macros.h"
84 #include "selection.h"
85 #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)
124 _midnam_model_selector.disable_scrolling();
125 _midnam_custom_device_mode_selector.disable_scrolling();
129 MidiTimeAxisView::set_note_highlight (uint8_t note) {
130 _piano_roll_header->set_note_highlight (note);
134 MidiTimeAxisView::set_route (boost::shared_ptr<Route> rt)
138 _view = new MidiStreamView (*this);
141 _piano_roll_header = new PianoRollHeader(*midi_view());
142 _range_scroomer = new MidiScroomer(midi_view()->note_range_adjustment);
143 _range_scroomer->DoubleClicked.connect (
144 sigc::bind (sigc::mem_fun(*this, &MidiTimeAxisView::set_note_range),
145 MidiStreamView::ContentsRange, false));
148 /* This next call will result in our height being set up, so it must come after
149 the creation of the piano roll / range scroomer as their visibility is set up
152 RouteTimeAxisView::set_route (rt);
154 _view->apply_color (gdk_color_to_rgba (color()), StreamView::RegionColor);
156 subplugin_menu.set_name ("ArdourContextMenu");
158 if (!gui_property ("note-range-min").empty ()) {
159 midi_view()->apply_note_range (atoi (gui_property ("note-range-min").c_str()),
160 atoi (gui_property ("note-range-max").c_str()),
164 midi_view()->NoteRangeChanged.connect (
165 sigc::mem_fun (*this, &MidiTimeAxisView::note_range_changed));
166 _view->ContentsHeightChanged.connect (
167 sigc::mem_fun (*this, &MidiTimeAxisView::contents_height_changed));
169 ignore_toggle = false;
171 if (is_midi_track()) {
172 controls_ebox.set_name ("MidiTimeAxisViewControlsBaseUnselected");
173 time_axis_frame.set_name ("MidiTimeAxisViewControlsBaseUnselected");
174 _note_mode = midi_track()->note_mode();
175 } else { // MIDI bus (which doesn't exist yet..)
176 controls_ebox.set_name ("MidiBusControlsBaseUnselected");
177 time_axis_frame.set_name ("MidiBusControlsBaseUnselected");
180 /* if set_state above didn't create a gain automation child, we need to make one */
181 if (automation_child (GainAutomation) == 0) {
182 create_automation_child (GainAutomation, false);
185 /* if set_state above didn't create a mute automation child, we need to make one */
186 if (automation_child (MuteAutomation) == 0) {
187 create_automation_child (MuteAutomation, false);
190 if (_route->panner_shell()) {
191 _route->panner_shell()->Changed.connect (*this, invalidator (*this), boost::bind (&MidiTimeAxisView::ensure_pan_views, this, false), gui_context());
194 /* map current state of the route */
195 ensure_pan_views (false);
197 processors_changed (RouteProcessorChange ());
199 _route->processors_changed.connect (*this, invalidator (*this),
200 boost::bind (&MidiTimeAxisView::processors_changed, this, _1),
204 _piano_roll_header->SetNoteSelection.connect (
205 sigc::mem_fun (*this, &MidiTimeAxisView::set_note_selection));
206 _piano_roll_header->AddNoteSelection.connect (
207 sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection));
208 _piano_roll_header->ExtendNoteSelection.connect (
209 sigc::mem_fun (*this, &MidiTimeAxisView::extend_note_selection));
210 _piano_roll_header->ToggleNoteSelection.connect (
211 sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection));
213 /* Suspend updates of the StreamView during scroomer drags to speed things up */
214 _range_scroomer->DragStarting.connect (
215 sigc::mem_fun (*midi_view(), &MidiStreamView::suspend_updates));
216 _range_scroomer->DragFinishing.connect (
217 sigc::mem_fun (*midi_view(), &MidiStreamView::resume_updates));
219 /* Put the scroomer and the keyboard in a VBox with a padding
220 label so that they can be reduced in height for stacked-view
224 HSeparator* separator = manage (new HSeparator());
225 separator->set_name("TrackSeparator");
226 separator->set_size_request(-1, 1);
229 VBox* v = manage (new VBox);
230 HBox* h = manage (new HBox);
231 h->pack_end (*_piano_roll_header);
232 h->pack_end (*_range_scroomer);
233 v->pack_start (*separator, false, false);
234 v->pack_start (*h, true, true);
237 top_hbox.remove(scroomer_placeholder);
238 time_axis_hbox.pack_end(*v, false, false, 0);
239 midi_scroomer_size_group->add_widget (*v);
241 controls_ebox.set_name ("MidiTrackControlsBaseUnselected");
242 time_axis_frame.set_name ("MidiTrackControlsBaseUnselected");
243 controls_base_selected_name = "MidiTrackControlsBaseSelected";
244 controls_base_unselected_name = "MidiTrackControlsBaseUnselected";
246 midi_view()->NoteRangeChanged.connect (
247 sigc::mem_fun(*this, &MidiTimeAxisView::update_range));
249 /* ask for notifications of any new RegionViews */
250 _view->RegionViewAdded.connect (
251 sigc::mem_fun(*this, &MidiTimeAxisView::region_view_added));
253 midi_track()->playback_filter().ChannelModeChanged.connect (
254 *this, invalidator (*this),
255 boost::bind (&MidiTimeAxisView::playback_channel_mode_changed, this),
257 midi_track()->playback_filter().ChannelMaskChanged.connect (
258 *this, invalidator (*this),
259 boost::bind (&MidiTimeAxisView::playback_channel_mode_changed, this),
261 midi_track()->capture_filter().ChannelModeChanged.connect (
262 *this, invalidator (*this),
263 boost::bind (&MidiTimeAxisView::capture_channel_mode_changed, this),
265 midi_track()->capture_filter().ChannelMaskChanged.connect (
266 *this, invalidator (*this),
267 boost::bind (&MidiTimeAxisView::capture_channel_mode_changed, this),
270 playback_channel_mode_changed ();
271 capture_channel_mode_changed ();
273 if (!_editor.have_idled()) {
274 /* first idle will do what we need */
280 typedef MIDI::Name::MidiPatchManager PatchManager;
282 PatchManager& patch_manager = PatchManager::instance();
284 for (PatchManager::DeviceNamesByMaker::const_iterator m = patch_manager.devices_by_manufacturer().begin();
285 m != patch_manager.devices_by_manufacturer().end(); ++m) {
286 Menu* menu = Gtk::manage(new Menu);
287 Menu_Helpers::MenuList& items = menu->items();
289 // Build manufacturer submenu
290 for (MIDI::Name::MIDINameDocument::MasterDeviceNamesList::const_iterator n = m->second.begin();
291 n != m->second.end(); ++n) {
292 Menu_Helpers::MenuElem elem = Gtk::Menu_Helpers::MenuElem(
294 sigc::bind(sigc::mem_fun(*this, &MidiTimeAxisView::model_changed),
297 items.push_back(elem);
300 // Add manufacturer submenu to selector
301 _midnam_model_selector.AddMenuElem(Menu_Helpers::MenuElem(m->first, *menu));
304 if (gui_property (X_("midnam-model-name")).empty()) {
305 set_gui_property (X_("midnam-model-name"), "Generic");
308 if (gui_property (X_("midnam-custom-device-mode")).empty()) {
309 boost::shared_ptr<MIDI::Name::MasterDeviceNames> device_names = get_device_names();
311 set_gui_property (X_("midnam-custom-device-mode"),
312 *device_names->custom_device_mode_names().begin());
316 set_tooltip (_midnam_model_selector, _("External MIDI Device"));
317 set_tooltip (_midnam_custom_device_mode_selector, _("External Device Mode"));
319 _midi_controls_box.set_homogeneous(false);
320 _midi_controls_box.set_border_width (2);
322 _channel_status_box.set_homogeneous (false);
323 _channel_status_box.set_spacing (4);
325 ArdourButton *channel_selector_button = manage (new ArdourButton(_("Chns")));
326 channel_selector_button->set_name ("route button");
327 set_tooltip (channel_selector_button, _("Click to edit channel settings"));
329 // Insert expanding space labels to get full width justification
330 _channel_status_box.pack_start (_playback_channel_status, false, false, 2);
331 _channel_status_box.pack_start (*Gtk::manage(new Gtk::Label(" ")), true, true);
332 _channel_status_box.pack_start (_capture_channel_status, false, false, 2);
333 _channel_status_box.pack_start (*Gtk::manage(new Gtk::Label(" ")), true, true);
334 _channel_status_box.pack_end (*channel_selector_button, false, false);
335 _channel_status_box.show_all ();
337 channel_selector_button->signal_clicked.connect (sigc::mem_fun (*this, &MidiTimeAxisView::toggle_channel_selector));
339 _midi_controls_box.pack_start (_channel_status_box, false, false, 10);
341 if (!patch_manager.all_models().empty()) {
343 _midnam_model_selector.show ();
344 _midi_controls_box.pack_start (_midnam_model_selector, false, false, 2);
346 _midnam_custom_device_mode_selector.show ();
348 _midi_controls_box.pack_start (_midnam_custom_device_mode_selector, false, false, 2);
351 model_changed(gui_property(X_("midnam-model-name")));
352 custom_device_mode_changed(gui_property(X_("midnam-custom-device-mode")));
354 controls_vbox.pack_start(_midi_controls_box, false, false);
356 const string color_mode = gui_property ("color-mode");
357 if (!color_mode.empty()) {
358 _color_mode = ColorMode (string_2_enum(color_mode, _color_mode));
359 if (_channel_selector && _color_mode == ChannelColors) {
360 _channel_selector->set_channel_colors(NoteBase::midi_channel_colors);
364 set_color_mode (_color_mode, true, false);
366 const string note_mode = gui_property ("note-mode");
367 if (!note_mode.empty()) {
368 _note_mode = NoteMode (string_2_enum (note_mode, _note_mode));
369 if (_percussion_mode_item) {
370 _percussion_mode_item->set_active (_note_mode == Percussive);
374 /* Look for any GUI object state nodes that represent automation children
375 * that should exist, and create the children.
378 const list<string> gui_ids = gui_object_state().all_ids ();
379 for (list<string>::const_iterator i = gui_ids.begin(); i != gui_ids.end(); ++i) {
382 Evoral::Parameter parameter (0, 0, 0);
384 bool const p = AutomationTimeAxisView::parse_state_id (
385 *i, route_id, has_parameter, parameter);
386 if (p && route_id == _route->id () && has_parameter) {
387 const std::string& visible = gui_object_state().get_string (*i, X_("visible"));
388 create_automation_child (parameter, string_is_affirmative (visible));
394 MidiTimeAxisView::first_idle ()
401 MidiTimeAxisView::~MidiTimeAxisView ()
403 delete _channel_selector;
405 delete _piano_roll_header;
406 _piano_roll_header = 0;
408 delete _range_scroomer;
411 delete controller_menu;
416 MidiTimeAxisView::check_step_edit ()
418 ensure_step_editor ();
419 _step_editor->check_step_edit ();
423 MidiTimeAxisView::model_changed(const std::string& model)
425 set_gui_property (X_("midnam-model-name"), model);
427 const std::list<std::string> device_modes = MIDI::Name::MidiPatchManager::instance()
428 .custom_device_mode_names_by_model(model);
430 _midnam_model_selector.set_text(model);
431 _midnam_custom_device_mode_selector.clear_items();
433 for (std::list<std::string>::const_iterator i = device_modes.begin();
434 i != device_modes.end(); ++i) {
435 _midnam_custom_device_mode_selector.AddMenuElem(
436 Gtk::Menu_Helpers::MenuElem(
437 *i, sigc::bind(sigc::mem_fun(*this, &MidiTimeAxisView::custom_device_mode_changed),
441 if (!device_modes.empty()) {
442 custom_device_mode_changed(device_modes.front());
445 if (device_modes.size() > 1) {
446 _midnam_custom_device_mode_selector.show();
448 _midnam_custom_device_mode_selector.hide();
451 if (device_modes.size() > 0) {
452 _route->instrument_info().set_external_instrument (model, device_modes.front());
454 _route->instrument_info().set_external_instrument (model, "");
457 // Rebuild controller menu
458 _controller_menu_map.clear ();
459 delete controller_menu;
461 build_automation_action_menu(false);
465 MidiTimeAxisView::custom_device_mode_changed(const std::string& mode)
467 const std::string model = gui_property (X_("midnam-model-name"));
469 set_gui_property (X_("midnam-custom-device-mode"), mode);
470 _midnam_custom_device_mode_selector.set_text(mode);
471 _route->instrument_info().set_external_instrument (model, mode);
475 MidiTimeAxisView::midi_view()
477 return dynamic_cast<MidiStreamView*>(_view);
481 MidiTimeAxisView::set_height (uint32_t h, TrackHeightMode m)
483 if (h >= MIDI_CONTROLS_BOX_MIN_HEIGHT) {
484 _midi_controls_box.show ();
486 _midi_controls_box.hide();
489 if (h >= KEYBOARD_MIN_HEIGHT) {
490 if (is_track() && _range_scroomer) {
491 _range_scroomer->show();
493 if (is_track() && _piano_roll_header) {
494 _piano_roll_header->show();
497 if (is_track() && _range_scroomer) {
498 _range_scroomer->hide();
500 if (is_track() && _piano_roll_header) {
501 _piano_roll_header->hide();
505 /* We need to do this after changing visibility of our stuff, as it will
506 eventually trigger a call to Editor::reset_controls_layout_width(),
507 which needs to know if we have just shown or hidden a scroomer /
510 RouteTimeAxisView::set_height (h, m);
514 MidiTimeAxisView::append_extra_display_menu_items ()
516 using namespace Menu_Helpers;
518 MenuList& items = display_menu->items();
521 Menu *range_menu = manage(new Menu);
522 MenuList& range_items = range_menu->items();
523 range_menu->set_name ("ArdourContextMenu");
525 range_items.push_back (
526 MenuElem (_("Show Full Range"),
527 sigc::bind (sigc::mem_fun(*this, &MidiTimeAxisView::set_note_range),
528 MidiStreamView::FullRange, true)));
530 range_items.push_back (
531 MenuElem (_("Fit Contents"),
532 sigc::bind (sigc::mem_fun(*this, &MidiTimeAxisView::set_note_range),
533 MidiStreamView::ContentsRange, true)));
535 items.push_back (MenuElem (_("Note Range"), *range_menu));
536 items.push_back (MenuElem (_("Note Mode"), *build_note_mode_menu()));
537 items.push_back (MenuElem (_("Channel Selector"),
538 sigc::mem_fun(*this, &MidiTimeAxisView::toggle_channel_selector)));
540 color_mode_menu = build_color_mode_menu();
541 if (color_mode_menu) {
542 items.push_back (MenuElem (_("Color Mode"), *color_mode_menu));
545 items.push_back (SeparatorElem ());
549 MidiTimeAxisView::toggle_channel_selector ()
551 if (!_channel_selector) {
552 _channel_selector = new MidiChannelSelectorWindow (midi_track());
554 if (_color_mode == ChannelColors) {
555 _channel_selector->set_channel_colors(NoteBase::midi_channel_colors);
557 _channel_selector->set_default_channel_color ();
560 _channel_selector->show_all ();
562 _channel_selector->cycle_visibility ();
567 MidiTimeAxisView::build_automation_action_menu (bool for_selection)
569 using namespace Menu_Helpers;
571 /* If we have a controller menu, we need to detach it before
572 RouteTimeAxis::build_automation_action_menu destroys the
573 menu it is attached to. Otherwise GTK destroys
574 controller_menu's gobj, meaning that it can't be reattached
575 below. See bug #3134.
578 if (controller_menu) {
579 detach_menu (*controller_menu);
582 _channel_command_menu_map.clear ();
583 RouteTimeAxisView::build_automation_action_menu (for_selection);
585 MenuList& automation_items = automation_action_menu->items();
587 uint16_t selected_channels = midi_track()->get_playback_channel_mask();
589 if (selected_channels != 0) {
591 automation_items.push_back (SeparatorElem());
593 /* these 2 MIDI "command" types are semantically more like automation
594 than note data, but they are not MIDI controllers. We give them
595 special status in this menu, since they will not show up in the
596 controller list and anyone who actually knows something about MIDI
597 (!) would not expect to find them there.
600 add_channel_command_menu_item (
601 automation_items, _("Bender"), MidiPitchBenderAutomation, 0);
602 automation_items.back().set_sensitive (
603 !for_selection || _editor.get_selection().tracks.size() == 1);
604 add_channel_command_menu_item (
605 automation_items, _("Pressure"), MidiChannelPressureAutomation, 0);
606 automation_items.back().set_sensitive (
607 !for_selection || _editor.get_selection().tracks.size() == 1);
609 /* now all MIDI controllers. Always offer the possibility that we will
610 rebuild the controllers menu since it might need to be updated after
611 a channel mode change or other change. Also detach it first in case
612 it has been used anywhere else.
615 build_controller_menu ();
617 automation_items.push_back (MenuElem (_("Controllers"), *controller_menu));
618 automation_items.back().set_sensitive (
619 !for_selection || _editor.get_selection().tracks.size() == 1);
621 automation_items.push_back (
622 MenuElem (string_compose ("<i>%1</i>", _("No MIDI Channels selected"))));
623 dynamic_cast<Label*> (automation_items.back().get_child())->set_use_markup (true);
628 MidiTimeAxisView::change_all_channel_tracks_visibility (bool yn, Evoral::Parameter param)
630 const uint16_t selected_channels = midi_track()->get_playback_channel_mask();
632 for (uint8_t chn = 0; chn < 16; chn++) {
633 if (selected_channels & (0x0001 << chn)) {
635 Evoral::Parameter fully_qualified_param (param.type(), chn, param.id());
636 Gtk::CheckMenuItem* menu = automation_child_menu_item (fully_qualified_param);
639 menu->set_active (yn);
646 MidiTimeAxisView::add_channel_command_menu_item (Menu_Helpers::MenuList& items,
648 AutomationType auto_type,
651 using namespace Menu_Helpers;
653 /* count the number of selected channels because we will build a different menu
654 structure if there is more than 1 selected.
657 const uint16_t selected_channels = midi_track()->get_playback_channel_mask();
660 for (uint8_t chn = 0; chn < 16; chn++) {
661 if (selected_channels & (0x0001 << chn)) {
670 /* multiple channels - create a submenu, with 1 item per channel */
672 Menu* chn_menu = manage (new Menu);
673 MenuList& chn_items (chn_menu->items());
674 Evoral::Parameter param_without_channel (auto_type, 0, cmd);
676 /* add a couple of items to hide/show all of them */
678 chn_items.push_back (
679 MenuElem (_("Hide all channels"),
680 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
681 false, param_without_channel)));
682 chn_items.push_back (
683 MenuElem (_("Show all channels"),
684 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
685 true, param_without_channel)));
687 for (uint8_t chn = 0; chn < 16; chn++) {
688 if (selected_channels & (0x0001 << chn)) {
690 /* for each selected channel, add a menu item for this controller */
692 Evoral::Parameter fully_qualified_param (auto_type, chn, cmd);
693 chn_items.push_back (
694 CheckMenuElem (string_compose (_("Channel %1"), chn+1),
695 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
696 fully_qualified_param)));
698 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
699 bool visible = false;
702 if (track->marked_for_display()) {
707 Gtk::CheckMenuItem* cmi = static_cast<Gtk::CheckMenuItem*>(&chn_items.back());
708 _channel_command_menu_map[fully_qualified_param] = cmi;
709 cmi->set_active (visible);
713 /* now create an item in the parent menu that has the per-channel list as a submenu */
715 items.push_back (MenuElem (label, *chn_menu));
719 /* just one channel - create a single menu item for this command+channel combination*/
721 for (uint8_t chn = 0; chn < 16; chn++) {
722 if (selected_channels & (0x0001 << chn)) {
724 Evoral::Parameter fully_qualified_param (auto_type, chn, cmd);
726 CheckMenuElem (label,
727 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
728 fully_qualified_param)));
730 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
731 bool visible = false;
734 if (track->marked_for_display()) {
739 Gtk::CheckMenuItem* cmi = static_cast<Gtk::CheckMenuItem*>(&items.back());
740 _channel_command_menu_map[fully_qualified_param] = cmi;
741 cmi->set_active (visible);
743 /* one channel only */
750 /** Add a single menu item for a controller on one channel. */
752 MidiTimeAxisView::add_single_channel_controller_item(Menu_Helpers::MenuList& ctl_items,
754 const std::string& name)
756 using namespace Menu_Helpers;
758 const uint16_t selected_channels = midi_track()->get_playback_channel_mask();
759 for (uint8_t chn = 0; chn < 16; chn++) {
760 if (selected_channels & (0x0001 << chn)) {
762 Evoral::Parameter fully_qualified_param (MidiCCAutomation, chn, ctl);
763 ctl_items.push_back (
765 string_compose ("<b>%1</b>: %2 [%3]", ctl, name, int (chn + 1)),
767 sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
768 fully_qualified_param)));
769 dynamic_cast<Label*> (ctl_items.back().get_child())->set_use_markup (true);
771 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (
772 fully_qualified_param);
774 bool visible = false;
776 if (track->marked_for_display()) {
781 Gtk::CheckMenuItem* cmi = static_cast<Gtk::CheckMenuItem*>(&ctl_items.back());
782 _controller_menu_map[fully_qualified_param] = cmi;
783 cmi->set_active (visible);
785 /* one channel only */
791 /** Add a submenu with 1 item per channel for a controller on many channels. */
793 MidiTimeAxisView::add_multi_channel_controller_item(Menu_Helpers::MenuList& ctl_items,
795 const std::string& name)
797 using namespace Menu_Helpers;
799 const uint16_t selected_channels = midi_track()->get_playback_channel_mask();
801 Menu* chn_menu = manage (new Menu);
802 MenuList& chn_items (chn_menu->items());
804 /* add a couple of items to hide/show this controller on all channels */
806 Evoral::Parameter param_without_channel (MidiCCAutomation, 0, ctl);
807 chn_items.push_back (
808 MenuElem (_("Hide all channels"),
809 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
810 false, param_without_channel)));
811 chn_items.push_back (
812 MenuElem (_("Show all channels"),
813 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
814 true, param_without_channel)));
816 for (uint8_t chn = 0; chn < 16; chn++) {
817 if (selected_channels & (0x0001 << chn)) {
819 /* for each selected channel, add a menu item for this controller */
821 Evoral::Parameter fully_qualified_param (MidiCCAutomation, chn, ctl);
822 chn_items.push_back (
823 CheckMenuElem (string_compose (_("Channel %1"), chn+1),
824 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
825 fully_qualified_param)));
827 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (
828 fully_qualified_param);
829 bool visible = false;
832 if (track->marked_for_display()) {
837 Gtk::CheckMenuItem* cmi = static_cast<Gtk::CheckMenuItem*>(&chn_items.back());
838 _controller_menu_map[fully_qualified_param] = cmi;
839 cmi->set_active (visible);
843 /* add the per-channel menu to the list of controllers, with the name of the controller */
844 ctl_items.push_back (MenuElem (string_compose ("<b>%1</b>: %2", ctl, name),
846 dynamic_cast<Label*> (ctl_items.back().get_child())->set_use_markup (true);
849 boost::shared_ptr<MIDI::Name::CustomDeviceMode>
850 MidiTimeAxisView::get_device_mode()
852 using namespace MIDI::Name;
854 boost::shared_ptr<MasterDeviceNames> device_names = get_device_names();
856 return boost::shared_ptr<MIDI::Name::CustomDeviceMode>();
859 return device_names->custom_device_mode_by_name(
860 gui_property (X_("midnam-custom-device-mode")));
863 boost::shared_ptr<MIDI::Name::MasterDeviceNames>
864 MidiTimeAxisView::get_device_names()
866 using namespace MIDI::Name;
868 const std::string model = gui_property (X_("midnam-model-name"));
870 boost::shared_ptr<MIDINameDocument> midnam = MidiPatchManager::instance()
871 .document_by_model(model);
873 return midnam->master_device_names(model);
875 return boost::shared_ptr<MasterDeviceNames>();
880 MidiTimeAxisView::build_controller_menu ()
882 using namespace Menu_Helpers;
884 if (controller_menu) {
885 /* it exists and has not been invalidated by a channel mode change */
889 controller_menu = new Menu; // explicitly managed by us
890 MenuList& items (controller_menu->items());
892 /* create several "top level" menu items for sets of controllers (16 at a
893 time), and populate each one with a submenu for each controller+channel
894 combination covering the currently selected channels for this track
897 const uint16_t selected_channels = midi_track()->get_playback_channel_mask();
899 /* count the number of selected channels because we will build a different menu
900 structure if there is more than 1 selected.
904 for (uint8_t chn = 0; chn < 16; chn++) {
905 if (selected_channels & (0x0001 << chn)) {
912 using namespace MIDI::Name;
913 boost::shared_ptr<MasterDeviceNames> device_names = get_device_names();
915 if (device_names && !device_names->controls().empty()) {
916 /* Controllers names available in midnam file, generate fancy menu */
917 unsigned n_items = 0;
918 unsigned n_groups = 0;
920 /* TODO: This is not correct, should look up the currently applicable ControlNameList
921 and only build a menu for that one. */
922 for (MasterDeviceNames::ControlNameLists::const_iterator l = device_names->controls().begin();
923 l != device_names->controls().end(); ++l) {
924 boost::shared_ptr<ControlNameList> name_list = l->second;
925 Menu* ctl_menu = NULL;
927 for (ControlNameList::Controls::const_iterator c = name_list->controls().begin();
928 c != name_list->controls().end();) {
929 const uint16_t ctl = c->second->number();
930 if (ctl != MIDI_CTL_MSB_BANK && ctl != MIDI_CTL_LSB_BANK) {
931 /* Skip bank select controllers since they're handled specially */
933 /* Create a new submenu */
934 ctl_menu = manage (new Menu);
937 MenuList& ctl_items (ctl_menu->items());
939 add_multi_channel_controller_item(ctl_items, ctl, c->second->name());
941 add_single_channel_controller_item(ctl_items, ctl, c->second->name());
946 if (ctl_menu && (++n_items == 16 || c == name_list->controls().end())) {
947 /* Submenu has 16 items or we're done, add it to controller menu and reset */
949 MenuElem(string_compose(_("Controllers %1-%2"),
950 (16 * n_groups), (16 * n_groups) + n_items - 1),
959 /* No controllers names, generate generic numeric menu */
960 for (int i = 0; i < 127; i += 16) {
961 Menu* ctl_menu = manage (new Menu);
962 MenuList& ctl_items (ctl_menu->items());
964 for (int ctl = i; ctl < i+16; ++ctl) {
965 if (ctl == MIDI_CTL_MSB_BANK || ctl == MIDI_CTL_LSB_BANK) {
966 /* Skip bank select controllers since they're handled specially */
971 add_multi_channel_controller_item(
972 ctl_items, ctl, string_compose(_("Controller %1"), ctl));
974 add_single_channel_controller_item(
975 ctl_items, ctl, string_compose(_("Controller %1"), ctl));
979 /* Add submenu for this block of controllers to controller menu */
981 MenuElem (string_compose (_("Controllers %1-%2"), i, i + 15),
988 MidiTimeAxisView::build_note_mode_menu()
990 using namespace Menu_Helpers;
992 Menu* mode_menu = manage (new Menu);
993 MenuList& items = mode_menu->items();
994 mode_menu->set_name ("ArdourContextMenu");
996 RadioMenuItem::Group mode_group;
998 RadioMenuElem (mode_group,_("Sustained"),
999 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_mode),
1001 _note_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
1002 _note_mode_item->set_active(_note_mode == Sustained);
1005 RadioMenuElem (mode_group, _("Percussive"),
1006 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_mode),
1007 Percussive, true)));
1008 _percussion_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
1009 _percussion_mode_item->set_active(_note_mode == Percussive);
1015 MidiTimeAxisView::build_color_mode_menu()
1017 using namespace Menu_Helpers;
1019 Menu* mode_menu = manage (new Menu);
1020 MenuList& items = mode_menu->items();
1021 mode_menu->set_name ("ArdourContextMenu");
1023 RadioMenuItem::Group mode_group;
1025 RadioMenuElem (mode_group, _("Meter Colors"),
1026 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode),
1027 MeterColors, false, true, true)));
1028 _meter_color_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
1029 _meter_color_mode_item->set_active(_color_mode == MeterColors);
1032 RadioMenuElem (mode_group, _("Channel Colors"),
1033 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode),
1034 ChannelColors, false, true, true)));
1035 _channel_color_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
1036 _channel_color_mode_item->set_active(_color_mode == ChannelColors);
1039 RadioMenuElem (mode_group, _("Track Color"),
1040 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode),
1041 TrackColor, false, true, true)));
1042 _channel_color_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
1043 _channel_color_mode_item->set_active(_color_mode == TrackColor);
1049 MidiTimeAxisView::set_note_mode(NoteMode mode, bool apply_to_selection)
1051 if (apply_to_selection) {
1052 _editor.get_selection().tracks.foreach_midi_time_axis (
1053 boost::bind (&MidiTimeAxisView::set_note_mode, _1, mode, false));
1055 if (_note_mode != mode || midi_track()->note_mode() != mode) {
1057 midi_track()->set_note_mode(mode);
1058 set_gui_property ("note-mode", enum_2_string(_note_mode));
1059 _view->redisplay_track();
1065 MidiTimeAxisView::set_color_mode (ColorMode mode, bool force, bool redisplay, bool apply_to_selection)
1067 if (apply_to_selection) {
1068 _editor.get_selection().tracks.foreach_midi_time_axis (
1069 boost::bind (&MidiTimeAxisView::set_color_mode, _1, mode, force, redisplay, false));
1071 if (_color_mode == mode && !force) {
1075 if (_channel_selector) {
1076 if (mode == ChannelColors) {
1077 _channel_selector->set_channel_colors(NoteBase::midi_channel_colors);
1079 _channel_selector->set_default_channel_color();
1084 set_gui_property ("color-mode", enum_2_string(_color_mode));
1086 _view->redisplay_track();
1092 MidiTimeAxisView::set_note_range (MidiStreamView::VisibleNoteRange range, bool apply_to_selection)
1094 if (apply_to_selection) {
1095 _editor.get_selection().tracks.foreach_midi_time_axis (
1096 boost::bind (&MidiTimeAxisView::set_note_range, _1, range, false));
1098 if (!_ignore_signals) {
1099 midi_view()->set_note_range(range);
1105 MidiTimeAxisView::update_range()
1107 MidiGhostRegion* mgr;
1109 for (list<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
1110 if ((mgr = dynamic_cast<MidiGhostRegion*>(*i)) != 0) {
1111 mgr->update_range();
1117 MidiTimeAxisView::show_all_automation (bool apply_to_selection)
1119 using namespace MIDI::Name;
1121 if (apply_to_selection) {
1122 _editor.get_selection().tracks.foreach_midi_time_axis (
1123 boost::bind (&MidiTimeAxisView::show_all_automation, _1, false));
1126 // Show existing automation
1127 const set<Evoral::Parameter> params = midi_track()->midi_playlist()->contained_automation();
1129 for (set<Evoral::Parameter>::const_iterator i = params.begin(); i != params.end(); ++i) {
1130 create_automation_child(*i, true);
1133 // Show automation for all controllers named in midnam file
1134 boost::shared_ptr<MasterDeviceNames> device_names = get_device_names();
1135 if (gui_property (X_("midnam-model-name")) != "Generic" &&
1136 device_names && !device_names->controls().empty()) {
1137 const std::string device_mode = gui_property (X_("midnam-custom-device-mode"));
1138 const uint16_t selected_channels = midi_track()->get_playback_channel_mask();
1139 for (uint32_t chn = 0; chn < 16; ++chn) {
1140 if ((selected_channels & (0x0001 << chn)) == 0) {
1141 // Channel not in use
1145 boost::shared_ptr<ChannelNameSet> chan_names = device_names->channel_name_set_by_channel(
1151 boost::shared_ptr<ControlNameList> control_names = device_names->control_name_list(
1152 chan_names->control_list_name());
1153 if (!control_names) {
1157 for (ControlNameList::Controls::const_iterator c = control_names->controls().begin();
1158 c != control_names->controls().end();
1160 const uint16_t ctl = c->second->number();
1161 if (ctl != MIDI_CTL_MSB_BANK && ctl != MIDI_CTL_LSB_BANK) {
1162 /* Skip bank select controllers since they're handled specially */
1163 const Evoral::Parameter param(MidiCCAutomation, chn, ctl);
1164 create_automation_child(param, true);
1171 RouteTimeAxisView::show_all_automation ();
1176 MidiTimeAxisView::show_existing_automation (bool apply_to_selection)
1178 if (apply_to_selection) {
1179 _editor.get_selection().tracks.foreach_midi_time_axis (
1180 boost::bind (&MidiTimeAxisView::show_existing_automation, _1, false));
1183 const set<Evoral::Parameter> params = midi_track()->midi_playlist()->contained_automation();
1185 for (set<Evoral::Parameter>::const_iterator i = params.begin(); i != params.end(); ++i) {
1186 create_automation_child (*i, true);
1190 RouteTimeAxisView::show_existing_automation ();
1194 /** Create an automation track for the given parameter (pitch bend, channel pressure).
1197 MidiTimeAxisView::create_automation_child (const Evoral::Parameter& param, bool show)
1199 if (param.type() == NullAutomation) {
1203 AutomationTracks::iterator existing = _automation_tracks.find (param);
1205 if (existing != _automation_tracks.end()) {
1207 /* automation track created because we had existing data for
1208 * the processor, but visibility may need to be controlled
1209 * since it will have been set visible by default.
1212 existing->second->set_marked_for_display (show);
1221 boost::shared_ptr<AutomationTimeAxisView> track;
1222 boost::shared_ptr<AutomationControl> control;
1225 switch (param.type()) {
1227 case GainAutomation:
1228 create_gain_automation_child (param, show);
1231 case MuteAutomation:
1232 create_mute_automation_child (param, show);
1235 case PluginAutomation:
1236 /* handled elsewhere */
1239 case MidiCCAutomation:
1240 case MidiPgmChangeAutomation:
1241 case MidiPitchBenderAutomation:
1242 case MidiChannelPressureAutomation:
1243 case MidiSystemExclusiveAutomation:
1244 /* These controllers are region "automation" - they are owned
1245 * by regions (and their MidiModels), not by the track. As a
1246 * result there is no AutomationList/Line for the track, but we create
1247 * a controller for the user to write immediate events, so the editor
1248 * can act as a control surface for the present MIDI controllers.
1250 * TODO: Record manipulation of the controller to regions?
1253 control = _route->automation_control(param, true);
1254 track.reset (new AutomationTimeAxisView (
1257 control ? _route : boost::shared_ptr<Automatable> (),
1264 _route->describe_parameter(param)));
1267 _view->foreach_regionview (
1268 sigc::mem_fun (*track.get(), &TimeAxisView::add_ghost));
1271 add_automation_child (param, track, show);
1274 case PanWidthAutomation:
1275 case PanElevationAutomation:
1276 case PanAzimuthAutomation:
1277 ensure_pan_views (show);
1281 error << "MidiTimeAxisView: unknown automation child "
1282 << EventTypeMap::instance().to_symbol(param) << endmsg;
1287 MidiTimeAxisView::route_active_changed ()
1289 RouteUI::route_active_changed ();
1292 if (_route->active()) {
1293 controls_ebox.set_name ("MidiTrackControlsBaseUnselected");
1294 time_axis_frame.set_name ("MidiTrackControlsBaseUnselected");
1295 controls_base_selected_name = "MidiTrackControlsBaseSelected";
1296 controls_base_unselected_name = "MidiTrackControlsBaseUnselected";
1298 controls_ebox.set_name ("MidiTrackControlsBaseInactiveUnselected");
1299 time_axis_frame.set_name ("MidiTrackControlsBaseInactiveUnselected");
1300 controls_base_selected_name = "MidiTrackControlsBaseInactiveSelected";
1301 controls_base_unselected_name = "MidiTrackControlsBaseInactiveUnselected";
1304 if (_route->active()) {
1305 controls_ebox.set_name ("BusControlsBaseUnselected");
1306 time_axis_frame.set_name ("BusControlsBaseUnselected");
1307 controls_base_selected_name = "BusControlsBaseSelected";
1308 controls_base_unselected_name = "BusControlsBaseUnselected";
1310 controls_ebox.set_name ("BusControlsBaseInactiveUnselected");
1311 time_axis_frame.set_name ("BusControlsBaseInactiveUnselected");
1312 controls_base_selected_name = "BusControlsBaseInactiveSelected";
1313 controls_base_unselected_name = "BusControlsBaseInactiveUnselected";
1319 MidiTimeAxisView::set_note_selection (uint8_t note)
1321 uint16_t chn_mask = midi_track()->get_playback_channel_mask();
1323 _editor.begin_reversible_selection_op (X_("Set Note Selection"));
1325 if (_view->num_selected_regionviews() == 0) {
1326 _view->foreach_regionview (
1327 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_selection_region_view),
1330 _view->foreach_selected_regionview (
1331 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_selection_region_view),
1335 _editor.commit_reversible_selection_op();
1339 MidiTimeAxisView::add_note_selection (uint8_t note)
1341 const uint16_t chn_mask = midi_track()->get_playback_channel_mask();
1343 _editor.begin_reversible_selection_op (X_("Add Note Selection"));
1345 if (_view->num_selected_regionviews() == 0) {
1346 _view->foreach_regionview (
1347 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection_region_view),
1350 _view->foreach_selected_regionview (
1351 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection_region_view),
1355 _editor.commit_reversible_selection_op();
1359 MidiTimeAxisView::extend_note_selection (uint8_t note)
1361 const uint16_t chn_mask = midi_track()->get_playback_channel_mask();
1363 _editor.begin_reversible_selection_op (X_("Extend Note Selection"));
1365 if (_view->num_selected_regionviews() == 0) {
1366 _view->foreach_regionview (
1367 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::extend_note_selection_region_view),
1370 _view->foreach_selected_regionview (
1371 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::extend_note_selection_region_view),
1375 _editor.commit_reversible_selection_op();
1379 MidiTimeAxisView::toggle_note_selection (uint8_t note)
1381 const uint16_t chn_mask = midi_track()->get_playback_channel_mask();
1383 _editor.begin_reversible_selection_op (X_("Toggle Note Selection"));
1385 if (_view->num_selected_regionviews() == 0) {
1386 _view->foreach_regionview (
1387 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection_region_view),
1390 _view->foreach_selected_regionview (
1391 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection_region_view),
1395 _editor.commit_reversible_selection_op();
1399 MidiTimeAxisView::get_per_region_note_selection (list<pair<PBD::ID, set<boost::shared_ptr<Evoral::Note<Evoral::Beats> > > > >& selection)
1401 _view->foreach_regionview (
1402 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::get_per_region_note_selection_region_view), sigc::ref(selection)));
1406 MidiTimeAxisView::set_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1408 dynamic_cast<MidiRegionView*>(rv)->select_matching_notes (note, chn_mask, false, false);
1412 MidiTimeAxisView::add_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1414 dynamic_cast<MidiRegionView*>(rv)->select_matching_notes (note, chn_mask, true, false);
1418 MidiTimeAxisView::extend_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1420 dynamic_cast<MidiRegionView*>(rv)->select_matching_notes (note, chn_mask, true, true);
1424 MidiTimeAxisView::toggle_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1426 dynamic_cast<MidiRegionView*>(rv)->toggle_matching_notes (note, chn_mask);
1430 MidiTimeAxisView::get_per_region_note_selection_region_view (RegionView* rv, list<pair<PBD::ID, std::set<boost::shared_ptr<Evoral::Note<Evoral::Beats> > > > > &selection)
1432 Evoral::Sequence<Evoral::Beats>::Notes selected;
1433 dynamic_cast<MidiRegionView*>(rv)->selection_as_notelist (selected, false);
1435 std::set<boost::shared_ptr<Evoral::Note<Evoral::Beats> > > notes;
1437 Evoral::Sequence<Evoral::Beats>::Notes::iterator sel_it;
1438 for (sel_it = selected.begin(); sel_it != selected.end(); ++sel_it) {
1439 notes.insert (*sel_it);
1442 if (!notes.empty()) {
1443 selection.push_back (make_pair ((rv)->region()->id(), notes));
1448 MidiTimeAxisView::set_channel_mode (ChannelMode, uint16_t)
1450 /* hide all automation tracks that use the wrong channel(s) and show all those that use
1454 const uint16_t selected_channels = midi_track()->get_playback_channel_mask();
1455 bool changed = false;
1459 for (uint32_t ctl = 0; ctl < 127; ++ctl) {
1461 for (uint32_t chn = 0; chn < 16; ++chn) {
1462 Evoral::Parameter fully_qualified_param (MidiCCAutomation, chn, ctl);
1463 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
1469 if ((selected_channels & (0x0001 << chn)) == 0) {
1470 /* channel not in use. hiding it will trigger RouteTimeAxisView::automation_track_hidden()
1471 which will cause a redraw. We don't want one per channel, so block that with no_redraw.
1473 changed = track->set_marked_for_display (false) || changed;
1475 changed = track->set_marked_for_display (true) || changed;
1482 /* TODO: Bender, Pressure */
1484 /* invalidate the controller menu, so that we rebuild it next time */
1485 _controller_menu_map.clear ();
1486 delete controller_menu;
1487 controller_menu = 0;
1495 MidiTimeAxisView::automation_child_menu_item (Evoral::Parameter param)
1497 Gtk::CheckMenuItem* m = RouteTimeAxisView::automation_child_menu_item (param);
1502 ParameterMenuMap::iterator i = _controller_menu_map.find (param);
1503 if (i != _controller_menu_map.end()) {
1507 i = _channel_command_menu_map.find (param);
1508 if (i != _channel_command_menu_map.end()) {
1515 boost::shared_ptr<MidiRegion>
1516 MidiTimeAxisView::add_region (framepos_t pos, framecnt_t length, bool commit)
1518 Editor* real_editor = dynamic_cast<Editor*> (&_editor);
1520 real_editor->begin_reversible_command (Operations::create_region);
1522 playlist()->clear_changes ();
1524 real_editor->snap_to (pos, RoundNearest);
1526 boost::shared_ptr<Source> src = _session->create_midi_source_by_stealing_name (view()->trackview().track());
1529 plist.add (ARDOUR::Properties::start, 0);
1530 plist.add (ARDOUR::Properties::length, length);
1531 plist.add (ARDOUR::Properties::name, PBD::basename_nosuffix(src->name()));
1533 boost::shared_ptr<Region> region = (RegionFactory::create (src, plist));
1535 playlist()->add_region (region, pos);
1536 _session->add_command (new StatefulDiffCommand (playlist()));
1539 real_editor->commit_reversible_command ();
1542 return boost::dynamic_pointer_cast<MidiRegion>(region);
1546 MidiTimeAxisView::ensure_step_editor ()
1548 if (!_step_editor) {
1549 _step_editor = new StepEditor (_editor, midi_track(), *this);
1554 MidiTimeAxisView::start_step_editing ()
1556 ensure_step_editor ();
1557 _step_editor->start_step_editing ();
1561 MidiTimeAxisView::stop_step_editing ()
1564 _step_editor->stop_step_editing ();
1568 /** @return channel (counted from 0) to add an event to, based on the current setting
1569 * of the channel selector.
1572 MidiTimeAxisView::get_channel_for_add () const
1574 uint16_t const chn_mask = midi_track()->get_playback_channel_mask();
1576 uint8_t channel = 0;
1578 /* pick the highest selected channel, unless all channels are selected,
1579 which is interpreted to mean channel 1 (zero)
1582 for (uint16_t i = 0; i < 16; ++i) {
1583 if (chn_mask & (1<<i)) {
1589 if (chn_cnt == 16) {
1597 MidiTimeAxisView::note_range_changed ()
1599 set_gui_property ("note-range-min", (int) midi_view()->lowest_note ());
1600 set_gui_property ("note-range-max", (int) midi_view()->highest_note ());
1604 MidiTimeAxisView::contents_height_changed ()
1606 _range_scroomer->queue_resize ();
1610 MidiTimeAxisView::playback_channel_mode_changed ()
1612 switch (midi_track()->get_playback_channel_mode()) {
1614 _playback_channel_status.set_markup (string_compose ("<b>%1</b>: <i>%2</i>", _("Play"), _("all")));
1616 case FilterChannels:
1617 _playback_channel_status.set_markup (string_compose ("<b>%1</b>: <i>%2</i>", _("Play"), _("some")));
1620 _playback_channel_status.set_markup (string_compose ("<b>%1</b>: <i>%2>%3</i>", _("Play"), _("all"), PBD::ffs (midi_track()->get_playback_channel_mask())));
1626 MidiTimeAxisView::capture_channel_mode_changed ()
1628 switch (midi_track()->get_capture_channel_mode()) {
1630 _capture_channel_status.set_markup (string_compose ("<b>%1</b>: <i>%2</i>", _("Rec"), _("all")));
1632 case FilterChannels:
1633 _capture_channel_status.set_markup (string_compose ("<b>%1</b>: <i>%2</i>", _("Rec"), _("some")));
1636 _capture_channel_status.set_markup (string_compose ("<b>%1</b>: <i>%2>%3</i>", _("Rec"), _("all"), PBD::ffs (midi_track()->get_capture_channel_mask())));
1642 MidiTimeAxisView::paste (framepos_t pos, const Selection& selection, PasteContext& ctx)
1644 if (!_editor.internal_editing()) {
1645 // Non-internal paste, paste regions like any other route
1646 return RouteTimeAxisView::paste(pos, selection, ctx);
1649 return midi_view()->paste(pos, selection, ctx);