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/region.h"
54 #include "ardour/region_factory.h"
55 #include "ardour/route.h"
56 #include "ardour/session.h"
57 #include "ardour/session_object.h"
58 #include "ardour/source.h"
59 #include "ardour/track.h"
60 #include "ardour/types.h"
62 #include "ardour_ui.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"
87 #include "note_base.h"
89 #include "ardour/midi_track.h"
93 using namespace ARDOUR;
94 using namespace ARDOUR_UI_UTILS;
97 using namespace Gtkmm2ext;
98 using namespace Editing;
100 // Minimum height at which a control is displayed
101 static const uint32_t MIDI_CONTROLS_BOX_MIN_HEIGHT = 140;
102 static const uint32_t KEYBOARD_MIN_HEIGHT = 130;
104 MidiTimeAxisView::MidiTimeAxisView (PublicEditor& ed, Session* sess, ArdourCanvas::Canvas& canvas)
105 : AxisView(sess) // virtually inherited
106 , RouteTimeAxisView(ed, sess, canvas)
107 , _ignore_signals(false)
109 , _piano_roll_header(0)
110 , _note_mode(Sustained)
112 , _percussion_mode_item(0)
113 , _color_mode(MeterColors)
114 , _meter_color_mode_item(0)
115 , _channel_color_mode_item(0)
116 , _track_color_mode_item(0)
117 , _channel_selector (0)
118 , _step_edit_item (0)
119 , controller_menu (0)
125 MidiTimeAxisView::set_route (boost::shared_ptr<Route> rt)
129 _view = new MidiStreamView (*this);
132 _piano_roll_header = new PianoRollHeader(*midi_view());
133 _range_scroomer = new MidiScroomer(midi_view()->note_range_adjustment);
134 _range_scroomer->DoubleClicked.connect (
135 sigc::bind (sigc::mem_fun(*this, &MidiTimeAxisView::set_note_range),
136 MidiStreamView::ContentsRange, false));
139 /* This next call will result in our height being set up, so it must come after
140 the creation of the piano roll / range scroomer as their visibility is set up
143 RouteTimeAxisView::set_route (rt);
145 _view->apply_color (_color, StreamView::RegionColor);
147 subplugin_menu.set_name ("ArdourContextMenu");
149 if (!gui_property ("note-range-min").empty ()) {
150 midi_view()->apply_note_range (atoi (gui_property ("note-range-min").c_str()),
151 atoi (gui_property ("note-range-max").c_str()),
155 midi_view()->NoteRangeChanged.connect (
156 sigc::mem_fun (*this, &MidiTimeAxisView::note_range_changed));
157 _view->ContentsHeightChanged.connect (
158 sigc::mem_fun (*this, &MidiTimeAxisView::contents_height_changed));
160 ignore_toggle = false;
162 if (is_midi_track()) {
163 controls_ebox.set_name ("MidiTimeAxisViewControlsBaseUnselected");
164 _note_mode = midi_track()->note_mode();
165 } else { // MIDI bus (which doesn't exist yet..)
166 controls_ebox.set_name ("MidiBusControlsBaseUnselected");
169 /* if set_state above didn't create a gain automation child, we need to make one */
170 if (automation_child (GainAutomation) == 0) {
171 create_automation_child (GainAutomation, false);
174 if (_route->panner_shell()) {
175 _route->panner_shell()->Changed.connect (*this, invalidator (*this), boost::bind (&MidiTimeAxisView::ensure_pan_views, this, false), gui_context());
178 /* map current state of the route */
179 ensure_pan_views (false);
181 processors_changed (RouteProcessorChange ());
183 _route->processors_changed.connect (*this, invalidator (*this),
184 boost::bind (&MidiTimeAxisView::processors_changed, this, _1),
188 _piano_roll_header->SetNoteSelection.connect (
189 sigc::mem_fun (*this, &MidiTimeAxisView::set_note_selection));
190 _piano_roll_header->AddNoteSelection.connect (
191 sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection));
192 _piano_roll_header->ExtendNoteSelection.connect (
193 sigc::mem_fun (*this, &MidiTimeAxisView::extend_note_selection));
194 _piano_roll_header->ToggleNoteSelection.connect (
195 sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection));
197 /* Suspend updates of the StreamView during scroomer drags to speed things up */
198 _range_scroomer->DragStarting.connect (
199 sigc::mem_fun (*midi_view(), &MidiStreamView::suspend_updates));
200 _range_scroomer->DragFinishing.connect (
201 sigc::mem_fun (*midi_view(), &MidiStreamView::resume_updates));
203 /* Put the scroomer and the keyboard in a VBox with a padding
204 label so that they can be reduced in height for stacked-view
207 VBox* v = manage (new VBox);
208 HBox* h = manage (new HBox);
209 h->pack_start (*_range_scroomer);
210 h->pack_start (*_piano_roll_header);
211 v->pack_start (*h, false, false);
212 v->pack_start (*manage (new Label ("")), true, true);
215 controls_hbox.pack_start(*v, false, false);
217 controls_ebox.set_name ("MidiTrackControlsBaseUnselected");
218 controls_base_selected_name = "MidiTrackControlsBaseSelected";
219 controls_base_unselected_name = "MidiTrackControlsBaseUnselected";
221 midi_view()->NoteRangeChanged.connect (
222 sigc::mem_fun(*this, &MidiTimeAxisView::update_range));
224 /* ask for notifications of any new RegionViews */
225 _view->RegionViewAdded.connect (
226 sigc::mem_fun(*this, &MidiTimeAxisView::region_view_added));
228 midi_track()->PlaybackChannelModeChanged.connect (*this, invalidator (*this),
229 boost::bind (&MidiTimeAxisView::playback_channel_mode_changed, this),
231 midi_track()->PlaybackChannelMaskChanged.connect (*this, invalidator (*this),
232 boost::bind (&MidiTimeAxisView::playback_channel_mode_changed, this),
234 midi_track()->CaptureChannelModeChanged.connect (*this, invalidator (*this),
235 boost::bind (&MidiTimeAxisView::capture_channel_mode_changed, this),
237 midi_track()->CaptureChannelMaskChanged.connect (*this, invalidator (*this),
238 boost::bind (&MidiTimeAxisView::capture_channel_mode_changed, this),
241 playback_channel_mode_changed ();
242 capture_channel_mode_changed ();
244 if (!_editor.have_idled()) {
245 /* first idle will do what we need */
251 MIDI::Name::MidiPatchManager& patch_manager = MIDI::Name::MidiPatchManager::instance();
253 MIDI::Name::MasterDeviceNames::Models::const_iterator m = patch_manager.all_models().begin();
254 for (; m != patch_manager.all_models().end(); ++m) {
255 _midnam_model_selector.append_text(m->c_str());
258 if (gui_property (X_("midnam-model-name")).empty()) {
259 set_gui_property (X_("midnam-model-name"), "Generic");
262 if (gui_property (X_("midnam-custom-device-mode")).empty()) {
263 boost::shared_ptr<MIDI::Name::MasterDeviceNames> device_names = get_device_names();
265 set_gui_property (X_("midnam-custom-device-mode"),
266 *device_names->custom_device_mode_names().begin());
270 _midnam_model_selector.set_active_text (gui_property (X_("midnam-model-name")));
271 _midnam_custom_device_mode_selector.set_active_text (gui_property (X_("midnam-custom-device-mode")));
273 ARDOUR_UI::instance()->set_tip (_midnam_model_selector, _("External MIDI Device"));
274 ARDOUR_UI::instance()->set_tip (_midnam_custom_device_mode_selector, _("External Device Mode"));
276 _midi_controls_box.set_homogeneous(false);
277 _midi_controls_box.set_border_width (10);
279 _channel_status_box.set_homogeneous (false);
280 _channel_status_box.set_spacing (6);
282 _channel_selector_button.set_label (_("Chns"));
283 ARDOUR_UI::instance()->set_tip (_channel_selector_button, _("Click to edit channel settings"));
285 /* fixed sized labels to prevent silly nonsense (though obviously,
286 * they cause their own too)
289 _playback_channel_status.set_size_request (65, -1);
290 _capture_channel_status.set_size_request (60, -1);
292 _channel_status_box.pack_start (_playback_channel_status, false, false);
293 _channel_status_box.pack_start (_capture_channel_status, false, false);
294 _channel_status_box.pack_start (_channel_selector_button, false, false);
295 _channel_status_box.show_all ();
297 _channel_selector_button.signal_clicked().connect (sigc::mem_fun (*this, &MidiTimeAxisView::toggle_channel_selector));
299 _midi_controls_box.pack_start (_channel_status_box, false, false, 10);
301 if (!patch_manager.all_models().empty()) {
303 _midnam_model_selector.set_size_request(22, 30);
304 _midnam_model_selector.set_border_width(2);
305 _midnam_model_selector.show ();
306 _midi_controls_box.pack_start (_midnam_model_selector);
308 _midnam_custom_device_mode_selector.set_size_request(10, 30);
309 _midnam_custom_device_mode_selector.set_border_width(2);
310 _midnam_custom_device_mode_selector.show ();
312 _midi_controls_box.pack_start (_midnam_custom_device_mode_selector);
316 custom_device_mode_changed();
318 _midnam_model_selector.signal_changed().connect(
319 sigc::mem_fun(*this, &MidiTimeAxisView::model_changed));
320 _midnam_custom_device_mode_selector.signal_changed().connect(
321 sigc::mem_fun(*this, &MidiTimeAxisView::custom_device_mode_changed));
323 controls_vbox.pack_start(_midi_controls_box, false, false);
325 const string color_mode = gui_property ("color-mode");
326 if (!color_mode.empty()) {
327 _color_mode = ColorMode (string_2_enum(color_mode, _color_mode));
328 if (_channel_selector && _color_mode == ChannelColors) {
329 _channel_selector->set_channel_colors(NoteBase::midi_channel_colors);
333 set_color_mode (_color_mode, true, false);
335 const string note_mode = gui_property ("note-mode");
336 if (!note_mode.empty()) {
337 _note_mode = NoteMode (string_2_enum (note_mode, _note_mode));
338 if (_percussion_mode_item) {
339 _percussion_mode_item->set_active (_note_mode == Percussive);
343 /* Look for any GUI object state nodes that represent automation children
344 * that should exist, and create the children.
347 const list<string> gui_ids = gui_object_state().all_ids ();
348 for (list<string>::const_iterator i = gui_ids.begin(); i != gui_ids.end(); ++i) {
351 Evoral::Parameter parameter (0, 0, 0);
353 bool const p = AutomationTimeAxisView::parse_state_id (
354 *i, route_id, has_parameter, parameter);
355 if (p && route_id == _route->id () && has_parameter) {
356 const std::string& visible = gui_object_state().get_string (*i, X_("visible"));
357 create_automation_child (parameter, string_is_affirmative (visible));
363 MidiTimeAxisView::first_idle ()
370 MidiTimeAxisView::~MidiTimeAxisView ()
372 delete _channel_selector;
374 delete _piano_roll_header;
375 _piano_roll_header = 0;
377 delete _range_scroomer;
380 delete controller_menu;
385 MidiTimeAxisView::enter_internal_edit_mode ()
388 midi_view()->enter_internal_edit_mode ();
393 MidiTimeAxisView::leave_internal_edit_mode ()
396 midi_view()->leave_internal_edit_mode ();
401 MidiTimeAxisView::check_step_edit ()
403 ensure_step_editor ();
404 _step_editor->check_step_edit ();
408 MidiTimeAxisView::model_changed()
410 const Glib::ustring model = _midnam_model_selector.get_active_text();
411 set_gui_property (X_("midnam-model-name"), model);
413 const std::list<std::string> device_modes = MIDI::Name::MidiPatchManager::instance()
414 .custom_device_mode_names_by_model(model);
416 _midnam_custom_device_mode_selector.clear_items();
418 for (std::list<std::string>::const_iterator i = device_modes.begin();
419 i != device_modes.end(); ++i) {
420 _midnam_custom_device_mode_selector.append_text(*i);
423 _midnam_custom_device_mode_selector.set_active(0);
425 _route->instrument_info().set_external_instrument (
426 _midnam_model_selector.get_active_text(),
427 _midnam_custom_device_mode_selector.get_active_text());
429 // Rebuild controller menu
430 _controller_menu_map.clear ();
431 delete controller_menu;
433 build_automation_action_menu(false);
437 MidiTimeAxisView::custom_device_mode_changed()
439 const Glib::ustring mode = _midnam_custom_device_mode_selector.get_active_text();
440 set_gui_property (X_("midnam-custom-device-mode"), mode);
441 _route->instrument_info().set_external_instrument (
442 _midnam_model_selector.get_active_text(), mode);
446 MidiTimeAxisView::midi_view()
448 return dynamic_cast<MidiStreamView*>(_view);
452 MidiTimeAxisView::set_height (uint32_t h)
454 if (h >= MIDI_CONTROLS_BOX_MIN_HEIGHT) {
455 _midi_controls_box.show ();
457 _midi_controls_box.hide();
460 if (h >= KEYBOARD_MIN_HEIGHT) {
461 if (is_track() && _range_scroomer) {
462 _range_scroomer->show();
464 if (is_track() && _piano_roll_header) {
465 _piano_roll_header->show();
468 if (is_track() && _range_scroomer) {
469 _range_scroomer->hide();
471 if (is_track() && _piano_roll_header) {
472 _piano_roll_header->hide();
476 /* We need to do this after changing visibility of our stuff, as it will
477 eventually trigger a call to Editor::reset_controls_layout_width(),
478 which needs to know if we have just shown or hidden a scroomer /
481 RouteTimeAxisView::set_height (h);
485 MidiTimeAxisView::append_extra_display_menu_items ()
487 using namespace Menu_Helpers;
489 MenuList& items = display_menu->items();
492 Menu *range_menu = manage(new Menu);
493 MenuList& range_items = range_menu->items();
494 range_menu->set_name ("ArdourContextMenu");
496 range_items.push_back (
497 MenuElem (_("Show Full Range"),
498 sigc::bind (sigc::mem_fun(*this, &MidiTimeAxisView::set_note_range),
499 MidiStreamView::FullRange, true)));
501 range_items.push_back (
502 MenuElem (_("Fit Contents"),
503 sigc::bind (sigc::mem_fun(*this, &MidiTimeAxisView::set_note_range),
504 MidiStreamView::ContentsRange, true)));
506 items.push_back (MenuElem (_("Note Range"), *range_menu));
507 items.push_back (MenuElem (_("Note Mode"), *build_note_mode_menu()));
508 items.push_back (MenuElem (_("Channel Selector"),
509 sigc::mem_fun(*this, &MidiTimeAxisView::toggle_channel_selector)));
511 color_mode_menu = build_color_mode_menu();
512 if (color_mode_menu) {
513 items.push_back (MenuElem (_("Color Mode"), *color_mode_menu));
516 items.push_back (SeparatorElem ());
520 MidiTimeAxisView::toggle_channel_selector ()
522 if (!_channel_selector) {
523 _channel_selector = new MidiChannelSelectorWindow (midi_track());
525 if (_color_mode == ChannelColors) {
526 _channel_selector->set_channel_colors(NoteBase::midi_channel_colors);
528 _channel_selector->set_default_channel_color ();
531 _channel_selector->show_all ();
533 _channel_selector->cycle_visibility ();
538 MidiTimeAxisView::build_automation_action_menu (bool for_selection)
540 using namespace Menu_Helpers;
542 /* If we have a controller menu, we need to detach it before
543 RouteTimeAxis::build_automation_action_menu destroys the
544 menu it is attached to. Otherwise GTK destroys
545 controller_menu's gobj, meaning that it can't be reattached
546 below. See bug #3134.
549 if (controller_menu) {
550 detach_menu (*controller_menu);
553 _channel_command_menu_map.clear ();
554 RouteTimeAxisView::build_automation_action_menu (for_selection);
556 MenuList& automation_items = automation_action_menu->items();
558 uint16_t selected_channels = midi_track()->get_playback_channel_mask();
560 if (selected_channels != 0) {
562 automation_items.push_back (SeparatorElem());
564 /* these 2 MIDI "command" types are semantically more like automation
565 than note data, but they are not MIDI controllers. We give them
566 special status in this menu, since they will not show up in the
567 controller list and anyone who actually knows something about MIDI
568 (!) would not expect to find them there.
571 add_channel_command_menu_item (
572 automation_items, _("Bender"), MidiPitchBenderAutomation, 0);
573 automation_items.back().set_sensitive (
574 !for_selection || _editor.get_selection().tracks.size() == 1);
575 add_channel_command_menu_item (
576 automation_items, _("Pressure"), MidiChannelPressureAutomation, 0);
577 automation_items.back().set_sensitive (
578 !for_selection || _editor.get_selection().tracks.size() == 1);
580 /* now all MIDI controllers. Always offer the possibility that we will
581 rebuild the controllers menu since it might need to be updated after
582 a channel mode change or other change. Also detach it first in case
583 it has been used anywhere else.
586 build_controller_menu ();
588 automation_items.push_back (SeparatorElem());
589 automation_items.push_back (MenuElem (_("Controllers"), *controller_menu));
590 automation_items.back().set_sensitive (
591 !for_selection || _editor.get_selection().tracks.size() == 1);
593 automation_items.push_back (
594 MenuElem (string_compose ("<i>%1</i>", _("No MIDI Channels selected"))));
595 dynamic_cast<Label*> (automation_items.back().get_child())->set_use_markup (true);
598 automation_items.push_back (SeparatorElem());
599 automation_items.push_back (CheckMenuElem (_("Fader"), sigc::mem_fun (*this, &MidiTimeAxisView::update_gain_track_visibility)));
600 gain_automation_item = dynamic_cast<Gtk::CheckMenuItem*> (&automation_items.back ());
601 gain_automation_item->set_active ((!for_selection || _editor.get_selection().tracks.size() == 1) &&
602 (gain_track && string_is_affirmative (gain_track->gui_property ("visible"))));
604 _main_automation_menu_map[Evoral::Parameter(GainAutomation)] = gain_automation_item;
606 if (!pan_tracks.empty()) {
607 automation_items.push_back (CheckMenuElem (_("Pan"), sigc::mem_fun (*this, &MidiTimeAxisView::update_pan_track_visibility)));
608 pan_automation_item = dynamic_cast<Gtk::CheckMenuItem*> (&automation_items.back ());
609 pan_automation_item->set_active ((!for_selection || _editor.get_selection().tracks.size() == 1) &&
610 (!pan_tracks.empty() && string_is_affirmative (pan_tracks.front()->gui_property ("visible"))));
612 set<Evoral::Parameter> const & params = _route->pannable()->what_can_be_automated ();
613 for (set<Evoral::Parameter>::const_iterator p = params.begin(); p != params.end(); ++p) {
614 _main_automation_menu_map[*p] = pan_automation_item;
621 MidiTimeAxisView::change_all_channel_tracks_visibility (bool yn, Evoral::Parameter param)
623 const uint16_t selected_channels = midi_track()->get_playback_channel_mask();
625 for (uint8_t chn = 0; chn < 16; chn++) {
626 if (selected_channels & (0x0001 << chn)) {
628 Evoral::Parameter fully_qualified_param (param.type(), chn, param.id());
629 Gtk::CheckMenuItem* menu = automation_child_menu_item (fully_qualified_param);
632 menu->set_active (yn);
639 MidiTimeAxisView::add_channel_command_menu_item (Menu_Helpers::MenuList& items,
641 AutomationType auto_type,
644 using namespace Menu_Helpers;
646 /* count the number of selected channels because we will build a different menu
647 structure if there is more than 1 selected.
650 const uint16_t selected_channels = midi_track()->get_playback_channel_mask();
653 for (uint8_t chn = 0; chn < 16; chn++) {
654 if (selected_channels & (0x0001 << chn)) {
663 /* multiple channels - create a submenu, with 1 item per channel */
665 Menu* chn_menu = manage (new Menu);
666 MenuList& chn_items (chn_menu->items());
667 Evoral::Parameter param_without_channel (auto_type, 0, cmd);
669 /* add a couple of items to hide/show all of them */
671 chn_items.push_back (
672 MenuElem (_("Hide all channels"),
673 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
674 false, param_without_channel)));
675 chn_items.push_back (
676 MenuElem (_("Show all channels"),
677 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
678 true, param_without_channel)));
680 for (uint8_t chn = 0; chn < 16; chn++) {
681 if (selected_channels & (0x0001 << chn)) {
683 /* for each selected channel, add a menu item for this controller */
685 Evoral::Parameter fully_qualified_param (auto_type, chn, cmd);
686 chn_items.push_back (
687 CheckMenuElem (string_compose (_("Channel %1"), chn+1),
688 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
689 fully_qualified_param)));
691 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
692 bool visible = false;
695 if (track->marked_for_display()) {
700 Gtk::CheckMenuItem* cmi = static_cast<Gtk::CheckMenuItem*>(&chn_items.back());
701 _channel_command_menu_map[fully_qualified_param] = cmi;
702 cmi->set_active (visible);
706 /* now create an item in the parent menu that has the per-channel list as a submenu */
708 items.push_back (MenuElem (label, *chn_menu));
712 /* just one channel - create a single menu item for this command+channel combination*/
714 for (uint8_t chn = 0; chn < 16; chn++) {
715 if (selected_channels & (0x0001 << chn)) {
717 Evoral::Parameter fully_qualified_param (auto_type, chn, cmd);
719 CheckMenuElem (label,
720 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
721 fully_qualified_param)));
723 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
724 bool visible = false;
727 if (track->marked_for_display()) {
732 Gtk::CheckMenuItem* cmi = static_cast<Gtk::CheckMenuItem*>(&items.back());
733 _channel_command_menu_map[fully_qualified_param] = cmi;
734 cmi->set_active (visible);
736 /* one channel only */
743 /** Add a single menu item for a controller on one channel. */
745 MidiTimeAxisView::add_single_channel_controller_item(Menu_Helpers::MenuList& ctl_items,
747 const std::string& name)
749 using namespace Menu_Helpers;
751 const uint16_t selected_channels = midi_track()->get_playback_channel_mask();
752 for (uint8_t chn = 0; chn < 16; chn++) {
753 if (selected_channels & (0x0001 << chn)) {
755 Evoral::Parameter fully_qualified_param (MidiCCAutomation, chn, ctl);
756 ctl_items.push_back (
758 string_compose ("<b>%1</b>: %2 [%3]", ctl, name, int (chn)),
760 sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
761 fully_qualified_param)));
762 dynamic_cast<Label*> (ctl_items.back().get_child())->set_use_markup (true);
764 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (
765 fully_qualified_param);
767 bool visible = false;
769 if (track->marked_for_display()) {
774 Gtk::CheckMenuItem* cmi = static_cast<Gtk::CheckMenuItem*>(&ctl_items.back());
775 _controller_menu_map[fully_qualified_param] = cmi;
776 cmi->set_active (visible);
778 /* one channel only */
784 /** Add a submenu with 1 item per channel for a controller on many channels. */
786 MidiTimeAxisView::add_multi_channel_controller_item(Menu_Helpers::MenuList& ctl_items,
788 const std::string& name)
790 using namespace Menu_Helpers;
792 const uint16_t selected_channels = midi_track()->get_playback_channel_mask();
794 Menu* chn_menu = manage (new Menu);
795 MenuList& chn_items (chn_menu->items());
797 /* add a couple of items to hide/show this controller on all channels */
799 Evoral::Parameter param_without_channel (MidiCCAutomation, 0, ctl);
800 chn_items.push_back (
801 MenuElem (_("Hide all channels"),
802 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
803 false, param_without_channel)));
804 chn_items.push_back (
805 MenuElem (_("Show all channels"),
806 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
807 true, param_without_channel)));
809 for (uint8_t chn = 0; chn < 16; chn++) {
810 if (selected_channels & (0x0001 << chn)) {
812 /* for each selected channel, add a menu item for this controller */
814 Evoral::Parameter fully_qualified_param (MidiCCAutomation, chn, ctl);
815 chn_items.push_back (
816 CheckMenuElem (string_compose (_("Channel %1"), chn+1),
817 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
818 fully_qualified_param)));
820 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (
821 fully_qualified_param);
822 bool visible = false;
825 if (track->marked_for_display()) {
830 Gtk::CheckMenuItem* cmi = static_cast<Gtk::CheckMenuItem*>(&chn_items.back());
831 _controller_menu_map[fully_qualified_param] = cmi;
832 cmi->set_active (visible);
836 /* add the per-channel menu to the list of controllers, with the name of the controller */
837 ctl_items.push_back (MenuElem (string_compose ("<b>%1</b>: %2", ctl, name),
839 dynamic_cast<Label*> (ctl_items.back().get_child())->set_use_markup (true);
842 boost::shared_ptr<MIDI::Name::CustomDeviceMode>
843 MidiTimeAxisView::get_device_mode()
845 using namespace MIDI::Name;
847 boost::shared_ptr<MasterDeviceNames> device_names = get_device_names();
849 return boost::shared_ptr<MIDI::Name::CustomDeviceMode>();
852 return device_names->custom_device_mode_by_name(
853 gui_property (X_("midnam-custom-device-mode")));
856 boost::shared_ptr<MIDI::Name::MasterDeviceNames>
857 MidiTimeAxisView::get_device_names()
859 using namespace MIDI::Name;
861 const std::string model = gui_property (X_("midnam-model-name"));
863 boost::shared_ptr<MIDINameDocument> midnam = MidiPatchManager::instance()
864 .document_by_model(model);
866 return midnam->master_device_names(model);
868 return boost::shared_ptr<MasterDeviceNames>();
873 MidiTimeAxisView::build_controller_menu ()
875 using namespace Menu_Helpers;
877 if (controller_menu) {
878 /* it exists and has not been invalidated by a channel mode change */
882 controller_menu = new Menu; // explicitly managed by us
883 MenuList& items (controller_menu->items());
885 /* create several "top level" menu items for sets of controllers (16 at a
886 time), and populate each one with a submenu for each controller+channel
887 combination covering the currently selected channels for this track
890 const uint16_t selected_channels = midi_track()->get_playback_channel_mask();
892 /* count the number of selected channels because we will build a different menu
893 structure if there is more than 1 selected.
897 for (uint8_t chn = 0; chn < 16; chn++) {
898 if (selected_channels & (0x0001 << chn)) {
905 using namespace MIDI::Name;
906 boost::shared_ptr<MasterDeviceNames> device_names = get_device_names();
908 if (device_names && !device_names->controls().empty()) {
909 /* Controllers names available in midnam file, generate fancy menu */
910 unsigned n_items = 0;
911 unsigned n_groups = 0;
913 /* TODO: This is not correct, should look up the currently applicable ControlNameList
914 and only build a menu for that one. */
915 for (MasterDeviceNames::ControlNameLists::const_iterator l = device_names->controls().begin();
916 l != device_names->controls().end(); ++l) {
917 boost::shared_ptr<ControlNameList> name_list = l->second;
918 Menu* ctl_menu = NULL;
920 for (ControlNameList::Controls::const_iterator c = name_list->controls().begin();
921 c != name_list->controls().end();) {
922 const uint16_t ctl = c->second->number();
923 if (ctl != MIDI_CTL_MSB_BANK && ctl != MIDI_CTL_LSB_BANK) {
924 /* Skip bank select controllers since they're handled specially */
926 /* Create a new submenu */
927 ctl_menu = manage (new Menu);
930 MenuList& ctl_items (ctl_menu->items());
932 add_multi_channel_controller_item(ctl_items, ctl, c->second->name());
934 add_single_channel_controller_item(ctl_items, ctl, c->second->name());
939 if (ctl_menu && (++n_items == 16 || c == name_list->controls().end())) {
940 /* Submenu has 16 items or we're done, add it to controller menu and reset */
942 MenuElem(string_compose(_("Controllers %1-%2"),
943 (16 * n_groups), (16 * n_groups) + n_items - 1),
952 /* No controllers names, generate generic numeric menu */
953 for (int i = 0; i < 127; i += 16) {
954 Menu* ctl_menu = manage (new Menu);
955 MenuList& ctl_items (ctl_menu->items());
957 for (int ctl = i; ctl < i+16; ++ctl) {
958 if (ctl == MIDI_CTL_MSB_BANK || ctl == MIDI_CTL_LSB_BANK) {
959 /* Skip bank select controllers since they're handled specially */
964 add_multi_channel_controller_item(
965 ctl_items, ctl, string_compose(_("Controller %1"), ctl));
967 add_single_channel_controller_item(
968 ctl_items, ctl, string_compose(_("Controller %1"), ctl));
972 /* Add submenu for this block of controllers to controller menu */
974 MenuElem (string_compose (_("Controllers %1-%2"), i, i + 15),
981 MidiTimeAxisView::build_note_mode_menu()
983 using namespace Menu_Helpers;
985 Menu* mode_menu = manage (new Menu);
986 MenuList& items = mode_menu->items();
987 mode_menu->set_name ("ArdourContextMenu");
989 RadioMenuItem::Group mode_group;
991 RadioMenuElem (mode_group,_("Sustained"),
992 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_mode),
994 _note_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
995 _note_mode_item->set_active(_note_mode == Sustained);
998 RadioMenuElem (mode_group, _("Percussive"),
999 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_mode),
1000 Percussive, true)));
1001 _percussion_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
1002 _percussion_mode_item->set_active(_note_mode == Percussive);
1008 MidiTimeAxisView::build_color_mode_menu()
1010 using namespace Menu_Helpers;
1012 Menu* mode_menu = manage (new Menu);
1013 MenuList& items = mode_menu->items();
1014 mode_menu->set_name ("ArdourContextMenu");
1016 RadioMenuItem::Group mode_group;
1018 RadioMenuElem (mode_group, _("Meter Colors"),
1019 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode),
1020 MeterColors, false, true, true)));
1021 _meter_color_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
1022 _meter_color_mode_item->set_active(_color_mode == MeterColors);
1025 RadioMenuElem (mode_group, _("Channel Colors"),
1026 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode),
1027 ChannelColors, false, true, true)));
1028 _channel_color_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
1029 _channel_color_mode_item->set_active(_color_mode == ChannelColors);
1032 RadioMenuElem (mode_group, _("Track Color"),
1033 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode),
1034 TrackColor, false, true, true)));
1035 _channel_color_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
1036 _channel_color_mode_item->set_active(_color_mode == TrackColor);
1042 MidiTimeAxisView::set_note_mode(NoteMode mode, bool apply_to_selection)
1044 if (apply_to_selection) {
1045 _editor.get_selection().tracks.foreach_midi_time_axis (
1046 boost::bind (&MidiTimeAxisView::set_note_mode, _1, mode, false));
1048 if (_note_mode != mode || midi_track()->note_mode() != mode) {
1050 midi_track()->set_note_mode(mode);
1051 set_gui_property ("note-mode", enum_2_string(_note_mode));
1052 _view->redisplay_track();
1058 MidiTimeAxisView::set_color_mode (ColorMode mode, bool force, bool redisplay, bool apply_to_selection)
1060 if (apply_to_selection) {
1061 _editor.get_selection().tracks.foreach_midi_time_axis (
1062 boost::bind (&MidiTimeAxisView::set_color_mode, _1, mode, force, redisplay, false));
1064 if (_color_mode == mode && !force) {
1068 if (_channel_selector) {
1069 if (mode == ChannelColors) {
1070 _channel_selector->set_channel_colors(NoteBase::midi_channel_colors);
1072 _channel_selector->set_default_channel_color();
1077 set_gui_property ("color-mode", enum_2_string(_color_mode));
1079 _view->redisplay_track();
1085 MidiTimeAxisView::set_note_range (MidiStreamView::VisibleNoteRange range, bool apply_to_selection)
1087 if (apply_to_selection) {
1088 _editor.get_selection().tracks.foreach_midi_time_axis (
1089 boost::bind (&MidiTimeAxisView::set_note_range, _1, range, false));
1091 if (!_ignore_signals) {
1092 midi_view()->set_note_range(range);
1098 MidiTimeAxisView::update_range()
1100 MidiGhostRegion* mgr;
1102 for (list<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
1103 if ((mgr = dynamic_cast<MidiGhostRegion*>(*i)) != 0) {
1104 mgr->update_range();
1110 MidiTimeAxisView::ensure_pan_views (bool show)
1112 bool changed = false;
1113 for (list<boost::shared_ptr<AutomationTimeAxisView> >::iterator i = pan_tracks.begin(); i != pan_tracks.end(); ++i) {
1115 (*i)->set_marked_for_display (false);
1118 _route->gui_changed (X_("visible_tracks"), (void *) 0); /* EMIT_SIGNAL */
1122 if (!_route->panner()) {
1126 set<Evoral::Parameter> params = _route->panner()->what_can_be_automated();
1127 set<Evoral::Parameter>::iterator p;
1129 for (p = params.begin(); p != params.end(); ++p) {
1130 boost::shared_ptr<ARDOUR::AutomationControl> pan_control = _route->pannable()->automation_control(*p);
1132 if (pan_control->parameter().type() == NullAutomation) {
1133 error << "Pan control has NULL automation type!" << endmsg;
1137 if (automation_child (pan_control->parameter ()).get () == 0) {
1139 /* we don't already have an AutomationTimeAxisView for this parameter */
1141 std::string const name = _route->panner()->describe_parameter (pan_control->parameter ());
1143 boost::shared_ptr<AutomationTimeAxisView> t (
1144 new AutomationTimeAxisView (_session,
1148 pan_control->parameter (),
1156 pan_tracks.push_back (t);
1157 add_automation_child (*p, t, show);
1159 pan_tracks.push_back (automation_child (pan_control->parameter ()));
1165 MidiTimeAxisView::update_gain_track_visibility ()
1167 bool const showit = gain_automation_item->get_active();
1169 if (showit != string_is_affirmative (gain_track->gui_property ("visible"))) {
1170 gain_track->set_marked_for_display (showit);
1172 /* now trigger a redisplay */
1175 _route->gui_changed (X_("visible_tracks"), (void *) 0); /* EMIT_SIGNAL */
1181 MidiTimeAxisView::update_pan_track_visibility ()
1183 bool const showit = pan_automation_item->get_active();
1184 bool changed = false;
1186 for (list<boost::shared_ptr<AutomationTimeAxisView> >::iterator i = pan_tracks.begin(); i != pan_tracks.end(); ++i) {
1187 if ((*i)->set_marked_for_display (showit)) {
1193 _route->gui_changed (X_("visible_tracks"), (void *) 0); /* EMIT_SIGNAL */
1198 MidiTimeAxisView::show_all_automation (bool apply_to_selection)
1200 if (apply_to_selection) {
1201 _editor.get_selection().tracks.foreach_midi_time_axis (
1202 boost::bind (&MidiTimeAxisView::show_all_automation, _1, false));
1205 const set<Evoral::Parameter> params = midi_track()->midi_playlist()->contained_automation();
1207 for (set<Evoral::Parameter>::const_iterator i = params.begin(); i != params.end(); ++i) {
1208 create_automation_child(*i, true);
1212 RouteTimeAxisView::show_all_automation ();
1217 MidiTimeAxisView::show_existing_automation (bool apply_to_selection)
1219 if (apply_to_selection) {
1220 _editor.get_selection().tracks.foreach_midi_time_axis (
1221 boost::bind (&MidiTimeAxisView::show_existing_automation, _1, false));
1224 const set<Evoral::Parameter> params = midi_track()->midi_playlist()->contained_automation();
1226 for (set<Evoral::Parameter>::const_iterator i = params.begin(); i != params.end(); ++i) {
1227 create_automation_child (*i, true);
1231 RouteTimeAxisView::show_existing_automation ();
1235 /** Create an automation track for the given parameter (pitch bend, channel pressure).
1238 MidiTimeAxisView::create_automation_child (const Evoral::Parameter& param, bool show)
1240 if (param.type() == NullAutomation) {
1244 AutomationTracks::iterator existing = _automation_tracks.find (param);
1246 if (existing != _automation_tracks.end()) {
1248 /* automation track created because we had existing data for
1249 * the processor, but visibility may need to be controlled
1250 * since it will have been set visible by default.
1253 if (existing->second->set_marked_for_display (show) && !no_redraw) {
1260 boost::shared_ptr<AutomationTimeAxisView> track;
1262 switch (param.type()) {
1264 case GainAutomation:
1265 create_gain_automation_child (param, show);
1268 case PluginAutomation:
1269 /* handled elsewhere */
1272 case MidiCCAutomation:
1273 case MidiPgmChangeAutomation:
1274 case MidiPitchBenderAutomation:
1275 case MidiChannelPressureAutomation:
1276 case MidiSystemExclusiveAutomation:
1277 /* These controllers are region "automation" - they are owned
1278 * by regions (and their MidiModels), not by the track. As a
1279 * result we do not create an AutomationList/Line for the track
1280 * ... except here we are doing something!! XXX
1283 track.reset (new AutomationTimeAxisView (
1286 boost::shared_ptr<Automatable> (),
1287 boost::shared_ptr<AutomationControl> (),
1293 _route->describe_parameter(param)));
1296 _view->foreach_regionview (
1297 sigc::mem_fun (*track.get(), &TimeAxisView::add_ghost));
1300 add_automation_child (param, track, show);
1303 case PanWidthAutomation:
1304 case PanElevationAutomation:
1305 case PanAzimuthAutomation:
1306 ensure_pan_views (show);
1310 error << "MidiTimeAxisView: unknown automation child "
1311 << EventTypeMap::instance().to_symbol(param) << endmsg;
1316 MidiTimeAxisView::route_active_changed ()
1318 RouteUI::route_active_changed ();
1321 if (_route->active()) {
1322 controls_ebox.set_name ("MidiTrackControlsBaseUnselected");
1323 controls_base_selected_name = "MidiTrackControlsBaseSelected";
1324 controls_base_unselected_name = "MidiTrackControlsBaseUnselected";
1326 controls_ebox.set_name ("MidiTrackControlsBaseInactiveUnselected");
1327 controls_base_selected_name = "MidiTrackControlsBaseInactiveSelected";
1328 controls_base_unselected_name = "MidiTrackControlsBaseInactiveUnselected";
1331 if (_route->active()) {
1332 controls_ebox.set_name ("BusControlsBaseUnselected");
1333 controls_base_selected_name = "BusControlsBaseSelected";
1334 controls_base_unselected_name = "BusControlsBaseUnselected";
1336 controls_ebox.set_name ("BusControlsBaseInactiveUnselected");
1337 controls_base_selected_name = "BusControlsBaseInactiveSelected";
1338 controls_base_unselected_name = "BusControlsBaseInactiveUnselected";
1344 MidiTimeAxisView::set_note_selection (uint8_t note)
1346 if (!_editor.internal_editing()) {
1350 uint16_t chn_mask = midi_track()->get_playback_channel_mask();
1352 if (_view->num_selected_regionviews() == 0) {
1353 _view->foreach_regionview (
1354 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_selection_region_view),
1357 _view->foreach_selected_regionview (
1358 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_selection_region_view),
1364 MidiTimeAxisView::add_note_selection (uint8_t note)
1366 if (!_editor.internal_editing()) {
1370 const uint16_t chn_mask = midi_track()->get_playback_channel_mask();
1372 if (_view->num_selected_regionviews() == 0) {
1373 _view->foreach_regionview (
1374 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection_region_view),
1377 _view->foreach_selected_regionview (
1378 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection_region_view),
1384 MidiTimeAxisView::extend_note_selection (uint8_t note)
1386 if (!_editor.internal_editing()) {
1390 const uint16_t chn_mask = midi_track()->get_playback_channel_mask();
1392 if (_view->num_selected_regionviews() == 0) {
1393 _view->foreach_regionview (
1394 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::extend_note_selection_region_view),
1397 _view->foreach_selected_regionview (
1398 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::extend_note_selection_region_view),
1404 MidiTimeAxisView::toggle_note_selection (uint8_t note)
1406 if (!_editor.internal_editing()) {
1410 const uint16_t chn_mask = midi_track()->get_playback_channel_mask();
1412 if (_view->num_selected_regionviews() == 0) {
1413 _view->foreach_regionview (
1414 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection_region_view),
1417 _view->foreach_selected_regionview (
1418 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection_region_view),
1424 MidiTimeAxisView::set_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1426 dynamic_cast<MidiRegionView*>(rv)->select_matching_notes (note, chn_mask, false, false);
1430 MidiTimeAxisView::add_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1432 dynamic_cast<MidiRegionView*>(rv)->select_matching_notes (note, chn_mask, true, false);
1436 MidiTimeAxisView::extend_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1438 dynamic_cast<MidiRegionView*>(rv)->select_matching_notes (note, chn_mask, true, true);
1442 MidiTimeAxisView::toggle_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1444 dynamic_cast<MidiRegionView*>(rv)->toggle_matching_notes (note, chn_mask);
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);
1521 playlist()->clear_changes ();
1523 real_editor->snap_to (pos, 0);
1525 boost::shared_ptr<Source> src = _session->create_midi_source_by_stealing_name (view()->trackview().track());
1528 plist.add (ARDOUR::Properties::start, 0);
1529 plist.add (ARDOUR::Properties::length, length);
1530 plist.add (ARDOUR::Properties::name, PBD::basename_nosuffix(src->name()));
1532 boost::shared_ptr<Region> region = (RegionFactory::create (src, plist));
1534 playlist()->add_region (region, pos);
1535 _session->add_command (new StatefulDiffCommand (playlist()));
1538 real_editor->commit_reversible_command ();
1541 return boost::dynamic_pointer_cast<MidiRegion>(region);
1545 MidiTimeAxisView::ensure_step_editor ()
1547 if (!_step_editor) {
1548 _step_editor = new StepEditor (_editor, midi_track(), *this);
1553 MidiTimeAxisView::start_step_editing ()
1555 ensure_step_editor ();
1556 _step_editor->start_step_editing ();
1560 MidiTimeAxisView::stop_step_editing ()
1563 _step_editor->stop_step_editing ();
1567 /** @return channel (counted from 0) to add an event to, based on the current setting
1568 * of the channel selector.
1571 MidiTimeAxisView::get_channel_for_add () const
1573 uint16_t const chn_mask = midi_track()->get_playback_channel_mask();
1575 uint8_t channel = 0;
1577 /* pick the highest selected channel, unless all channels are selected,
1578 which is interpreted to mean channel 1 (zero)
1581 for (uint16_t i = 0; i < 16; ++i) {
1582 if (chn_mask & (1<<i)) {
1588 if (chn_cnt == 16) {
1596 MidiTimeAxisView::note_range_changed ()
1598 set_gui_property ("note-range-min", (int) midi_view()->lowest_note ());
1599 set_gui_property ("note-range-max", (int) midi_view()->highest_note ());
1603 MidiTimeAxisView::contents_height_changed ()
1605 _range_scroomer->set_size_request (-1, _view->child_height ());
1609 MidiTimeAxisView::playback_channel_mode_changed ()
1611 switch (midi_track()->get_playback_channel_mode()) {
1613 _playback_channel_status.set_markup (string_compose ("<b>%1</b>: <i>%2</i>", _("Play"), _("all")));
1615 case FilterChannels:
1616 _playback_channel_status.set_markup (string_compose ("<b>%1</b>: <i>%2</i>", _("Play"), _("some")));
1619 _playback_channel_status.set_markup (string_compose ("<b>%1</b>: <i>%2>%3</i>", _("Play"), _("all"), PBD::ffs (midi_track()->get_playback_channel_mask())));
1625 MidiTimeAxisView::capture_channel_mode_changed ()
1627 switch (midi_track()->get_capture_channel_mode()) {
1629 _capture_channel_status.set_markup (string_compose ("<b>%1</b>: <i>%2</i>", _("Rec"), _("all")));
1631 case FilterChannels:
1632 _capture_channel_status.set_markup (string_compose ("<b>%1</b>: <i>%2</i>", _("Rec"), _("some")));
1635 _capture_channel_status.set_markup (string_compose ("<b>%1</b>: <i>%2>%3</i>", _("Rec"), _("all"), PBD::ffs (midi_track()->get_capture_channel_mask())));