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"
29 #include "pbd/stl_delete.h"
30 #include "pbd/whitespace.h"
31 #include "pbd/basename.h"
32 #include "pbd/enumwriter.h"
33 #include "pbd/memento_command.h"
34 #include "pbd/stateful_diff_command.h"
36 #include "gtkmm2ext/gtk_ui.h"
37 #include "gtkmm2ext/selector.h"
38 #include "gtkmm2ext/bindable_button.h"
39 #include "gtkmm2ext/utils.h"
41 #include "ardour/event_type_map.h"
42 #include "ardour/midi_patch_manager.h"
43 #include "ardour/midi_playlist.h"
44 #include "ardour/midi_region.h"
45 #include "ardour/midi_source.h"
46 #include "ardour/midi_track.h"
47 #include "ardour/operations.h"
48 #include "ardour/playlist.h"
49 #include "ardour/region.h"
50 #include "ardour/region_factory.h"
51 #include "ardour/route.h"
52 #include "ardour/session.h"
53 #include "ardour/session_object.h"
54 #include "ardour/source.h"
55 #include "ardour/track.h"
56 #include "ardour/types.h"
58 #include "midi++/names.h"
60 #include "ardour_ui.h"
61 #include "ardour_button.h"
62 #include "automation_line.h"
63 #include "automation_time_axis.h"
64 #include "canvas-note-event.h"
65 #include "canvas_impl.h"
68 #include "ghostregion.h"
69 #include "gui_thread.h"
71 #include "midi_scroomer.h"
72 #include "midi_streamview.h"
73 #include "midi_region_view.h"
74 #include "midi_time_axis.h"
75 #include "piano_roll_header.h"
76 #include "playlist_selector.h"
77 #include "plugin_selector.h"
78 #include "plugin_ui.h"
79 #include "point_selection.h"
81 #include "region_view.h"
82 #include "rgb_macros.h"
83 #include "selection.h"
84 #include "step_editor.h"
85 #include "simplerect.h"
88 #include "ardour/midi_track.h"
92 using namespace ARDOUR;
95 using namespace Gtkmm2ext;
96 using namespace Editing;
98 // Minimum height at which a control is displayed
99 static const uint32_t MIDI_CONTROLS_BOX_MIN_HEIGHT = 162;
100 static const uint32_t KEYBOARD_MIN_HEIGHT = 140;
102 MidiTimeAxisView::MidiTimeAxisView (PublicEditor& ed, Session* sess, Canvas& canvas)
103 : AxisView(sess) // virtually inherited
104 , RouteTimeAxisView(ed, sess, canvas)
105 , _ignore_signals(false)
107 , _piano_roll_header(0)
108 , _note_mode(Sustained)
110 , _percussion_mode_item(0)
111 , _color_mode(MeterColors)
112 , _meter_color_mode_item(0)
113 , _channel_color_mode_item(0)
114 , _track_color_mode_item(0)
115 , _step_edit_item (0)
116 , controller_menu (0)
122 MidiTimeAxisView::set_route (boost::shared_ptr<Route> rt)
126 _view = new MidiStreamView (*this);
129 _piano_roll_header = new PianoRollHeader(*midi_view());
130 _range_scroomer = new MidiScroomer(midi_view()->note_range_adjustment);
131 _range_scroomer->DoubleClicked.connect (sigc::bind (
132 sigc::mem_fun(*this, &MidiTimeAxisView::set_note_range),
133 MidiStreamView::ContentsRange, false));
136 /* This next call will result in our height being set up, so it must come after
137 the creation of the piano roll / range scroomer as their visibility is set up
140 RouteTimeAxisView::set_route (rt);
142 _view->apply_color (_color, StreamView::RegionColor);
144 subplugin_menu.set_name ("ArdourContextMenu");
146 if (!gui_property ("note-range-min").empty ()) {
147 midi_view()->apply_note_range (atoi (gui_property ("note-range-min").c_str()), atoi (gui_property ("note-range-max").c_str()), true);
149 midi_view()->NoteRangeChanged.connect (sigc::mem_fun (*this, &MidiTimeAxisView::note_range_changed));
150 _view->ContentsHeightChanged.connect (sigc::mem_fun (*this, &MidiTimeAxisView::contents_height_changed));
152 ignore_toggle = false;
154 if (is_midi_track()) {
155 controls_ebox.set_name ("MidiTimeAxisViewControlsBaseUnselected");
156 _note_mode = midi_track()->note_mode();
157 } else { // MIDI bus (which doesn't exist yet..)
158 controls_ebox.set_name ("MidiBusControlsBaseUnselected");
161 /* map current state of the route */
163 processors_changed (RouteProcessorChange ());
165 _route->processors_changed.connect (*this, invalidator (*this), boost::bind (&MidiTimeAxisView::processors_changed, this, _1), gui_context());
168 _piano_roll_header->SetNoteSelection.connect (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_selection));
169 _piano_roll_header->AddNoteSelection.connect (sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection));
170 _piano_roll_header->ExtendNoteSelection.connect (sigc::mem_fun (*this, &MidiTimeAxisView::extend_note_selection));
171 _piano_roll_header->ToggleNoteSelection.connect (sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection));
173 /* Suspend updates of the StreamView during scroomer drags to speed things up */
174 _range_scroomer->DragStarting.connect (sigc::mem_fun (*midi_view(), &MidiStreamView::suspend_updates));
175 _range_scroomer->DragFinishing.connect (sigc::mem_fun (*midi_view(), &MidiStreamView::resume_updates));
177 /* Put the scroomer and the keyboard in a VBox with a padding
178 label so that they can be reduced in height for stacked-view
181 VBox* v = manage (new VBox);
182 HBox* h = manage (new HBox);
183 h->pack_start (*_range_scroomer);
184 h->pack_start (*_piano_roll_header);
185 v->pack_start (*h, false, false);
186 v->pack_start (*manage (new Label ("")), true, true);
189 controls_hbox.pack_start(*v);
191 controls_ebox.set_name ("MidiTrackControlsBaseUnselected");
192 controls_base_selected_name = "MidiTrackControlsBaseSelected";
193 controls_base_unselected_name = "MidiTrackControlsBaseUnselected";
195 midi_view()->NoteRangeChanged.connect (sigc::mem_fun(*this, &MidiTimeAxisView::update_range));
197 /* ask for notifications of any new RegionViews */
198 _view->RegionViewAdded.connect (sigc::mem_fun(*this, &MidiTimeAxisView::region_view_added));
200 if (!_editor.have_idled()) {
201 /* first idle will do what we need */
208 MIDI::Name::MidiPatchManager& patch_manager = MIDI::Name::MidiPatchManager::instance();
210 MIDI::Name::MasterDeviceNames::Models::const_iterator m = patch_manager.all_models().begin();
211 for (; m != patch_manager.all_models().end(); ++m) {
212 _midnam_model_selector.append_text(m->c_str());
215 _midnam_model_selector.set_active_text (gui_property (X_("midnam-model-name")));
217 _midnam_custom_device_mode_selector.set_active_text (gui_property (X_("midnam-custom-device-mode")));
219 _midnam_model_selector.signal_changed().connect(sigc::mem_fun(*this, &MidiTimeAxisView::model_changed));
220 _midnam_custom_device_mode_selector.signal_changed().connect(
221 sigc::mem_fun(*this, &MidiTimeAxisView::custom_device_mode_changed));
223 ARDOUR_UI::instance()->set_tip (_midnam_model_selector, _("External MIDI Device"));
224 ARDOUR_UI::instance()->set_tip (_midnam_custom_device_mode_selector, _("External Device Mode"));
226 _midi_controls_box.set_homogeneous(false);
227 _midi_controls_box.set_border_width (10);
228 if (!patch_manager.all_models().empty()) {
229 _channel_selector.set_border_width(2);
230 _midi_controls_box.resize(3, 2);
231 _midi_controls_box.attach(_channel_selector, 0, 2, 0, 1);
233 _midi_controls_box.attach(*manage(new HSeparator()), 0, 2, 1, 2);
235 _midnam_model_selector.set_size_request(22, 30);
236 _midnam_model_selector.set_border_width(2);
237 _midi_controls_box.attach(_midnam_model_selector, 0, 1, 2, 3);
239 _midnam_custom_device_mode_selector.set_size_request(10, 30);
240 _midnam_custom_device_mode_selector.set_border_width(2);
241 _midi_controls_box.attach(_midnam_custom_device_mode_selector, 0, 1, 3, 4);
243 _midi_controls_box.attach(_channel_selector, 0, 1, 0, 1);
246 controls_vbox.pack_start(_midi_controls_box, false, false);
248 // restore channel selector settings
249 _channel_selector.set_channel_mode(midi_track()->get_channel_mode(), midi_track()->get_channel_mask());
250 _channel_selector.mode_changed.connect(
251 sigc::mem_fun(*midi_track(), &MidiTrack::set_channel_mode));
252 _channel_selector.mode_changed.connect(
253 sigc::mem_fun(*this, &MidiTimeAxisView::set_channel_mode));
255 string prop = gui_property ("color-mode");
257 _color_mode = ColorMode (string_2_enum(prop, _color_mode));
258 if (_color_mode == ChannelColors) {
259 _channel_selector.set_channel_colors(CanvasNoteEvent::midi_channel_colors);
263 set_color_mode (_color_mode, true, false);
265 prop = gui_property ("note-mode");
267 _note_mode = NoteMode (string_2_enum (prop, _note_mode));
268 if (_percussion_mode_item) {
269 _percussion_mode_item->set_active (_note_mode == Percussive);
273 /* Look for any GUI object state nodes that represent automation children that should exist, and create
277 list<string> gui_ids = gui_object_state().all_ids ();
278 for (list<string>::const_iterator i = gui_ids.begin(); i != gui_ids.end(); ++i) {
281 Evoral::Parameter parameter (0, 0, 0);
283 bool const p = AutomationTimeAxisView::parse_state_id (*i, route_id, has_parameter, parameter);
284 if (p && route_id == _route->id () && has_parameter) {
285 create_automation_child (parameter, string_is_affirmative (gui_object_state().get_string (*i, X_("visible"))));
291 MidiTimeAxisView::first_idle ()
298 MidiTimeAxisView::~MidiTimeAxisView ()
300 delete _piano_roll_header;
301 _piano_roll_header = 0;
303 delete _range_scroomer;
306 delete controller_menu;
311 MidiTimeAxisView::enter_internal_edit_mode ()
314 midi_view()->enter_internal_edit_mode ();
319 MidiTimeAxisView::leave_internal_edit_mode ()
322 midi_view()->leave_internal_edit_mode ();
327 MidiTimeAxisView::check_step_edit ()
329 ensure_step_editor ();
330 _step_editor->check_step_edit ();
334 MidiTimeAxisView::model_changed()
336 string model = _midnam_model_selector.get_active_text();
337 set_gui_property (X_("midnam-model-name"), model);
339 std::list<std::string> device_modes = MIDI::Name::MidiPatchManager::instance()
340 .custom_device_mode_names_by_model(model);
342 _midnam_custom_device_mode_selector.clear_items();
344 if (device_modes.size() < 2) {
345 _midnam_custom_device_mode_selector.hide();
347 _midnam_custom_device_mode_selector.show();
348 for (std::list<std::string>::const_iterator i = device_modes.begin();
349 i != device_modes.end(); ++i) {
350 _midnam_custom_device_mode_selector.append_text(*i);
354 _midnam_custom_device_mode_selector.set_active(0);
356 _route->instrument_info().set_external_instrument (_midnam_model_selector.get_active_text(), _midnam_custom_device_mode_selector.get_active_text());
360 MidiTimeAxisView::custom_device_mode_changed()
362 string mode = _midnam_custom_device_mode_selector.get_active_text();
363 set_gui_property (X_("midnam-custom-device-mode"), mode);
364 _route->instrument_info().set_external_instrument (_midnam_model_selector.get_active_text(), mode);
368 MidiTimeAxisView::midi_view()
370 return dynamic_cast<MidiStreamView*>(_view);
374 MidiTimeAxisView::set_height (uint32_t h)
376 if (h >= MIDI_CONTROLS_BOX_MIN_HEIGHT) {
377 _midi_controls_box.show ();
379 _midi_controls_box.hide();
382 if (h >= KEYBOARD_MIN_HEIGHT) {
383 if (is_track() && _range_scroomer) {
384 _range_scroomer->show();
386 if (is_track() && _piano_roll_header) {
387 _piano_roll_header->show();
390 if (is_track() && _range_scroomer) {
391 _range_scroomer->hide();
393 if (is_track() && _piano_roll_header) {
394 _piano_roll_header->hide();
398 /* We need to do this after changing visibility of our stuff, as it will
399 eventually trigger a call to Editor::reset_controls_layout_width(),
400 which needs to know if we have just shown or hidden a scroomer /
403 RouteTimeAxisView::set_height (h);
407 MidiTimeAxisView::append_extra_display_menu_items ()
409 using namespace Menu_Helpers;
411 MenuList& items = display_menu->items();
414 Menu *range_menu = manage(new Menu);
415 MenuList& range_items = range_menu->items();
416 range_menu->set_name ("ArdourContextMenu");
418 range_items.push_back (MenuElem (_("Show Full Range"), sigc::bind (
419 sigc::mem_fun(*this, &MidiTimeAxisView::set_note_range),
420 MidiStreamView::FullRange, true)));
422 range_items.push_back (MenuElem (_("Fit Contents"), sigc::bind (
423 sigc::mem_fun(*this, &MidiTimeAxisView::set_note_range),
424 MidiStreamView::ContentsRange, true)));
426 items.push_back (MenuElem (_("Note Range"), *range_menu));
427 items.push_back (MenuElem (_("Note Mode"), *build_note_mode_menu()));
429 items.push_back (SeparatorElem ());
433 MidiTimeAxisView::build_automation_action_menu (bool for_selection)
435 using namespace Menu_Helpers;
437 /* If we have a controller menu, we need to detach it before
438 RouteTimeAxis::build_automation_action_menu destroys the
439 menu it is attached to. Otherwise GTK destroys
440 controller_menu's gobj, meaning that it can't be reattached
441 below. See bug #3134.
444 if (controller_menu) {
445 detach_menu (*controller_menu);
448 _channel_command_menu_map.clear ();
449 RouteTimeAxisView::build_automation_action_menu (for_selection);
451 MenuList& automation_items = automation_action_menu->items();
453 uint16_t selected_channels = _channel_selector.get_selected_channels();
455 if (selected_channels != 0) {
457 automation_items.push_back (SeparatorElem());
459 /* these 2 MIDI "command" types are semantically more like automation than note data,
460 but they are not MIDI controllers. We give them special status in this menu, since
461 they will not show up in the controller list and anyone who actually knows
462 something about MIDI (!) would not expect to find them there.
465 add_channel_command_menu_item (automation_items, _("Bender"), MidiPitchBenderAutomation, 0);
466 automation_items.back().set_sensitive (!for_selection || _editor.get_selection().tracks.size() == 1);
467 add_channel_command_menu_item (automation_items, _("Pressure"), MidiChannelPressureAutomation, 0);
468 automation_items.back().set_sensitive (!for_selection || _editor.get_selection().tracks.size() == 1);
470 /* now all MIDI controllers. Always offer the possibility that we will rebuild the controllers menu
471 since it might need to be updated after a channel mode change or other change. Also detach it
472 first in case it has been used anywhere else.
475 build_controller_menu ();
477 automation_items.push_back (SeparatorElem());
478 automation_items.push_back (MenuElem (_("Controllers"), *controller_menu));
479 automation_items.back().set_sensitive (!for_selection || _editor.get_selection().tracks.size() == 1);
481 automation_items.push_back (MenuElem (string_compose ("<i>%1</i>", _("No MIDI Channels selected"))));
482 dynamic_cast<Label*> (automation_items.back().get_child())->set_use_markup (true);
488 MidiTimeAxisView::change_all_channel_tracks_visibility (bool yn, Evoral::Parameter param)
490 uint16_t selected_channels = _channel_selector.get_selected_channels();
492 for (uint8_t chn = 0; chn < 16; chn++) {
493 if (selected_channels & (0x0001 << chn)) {
495 Evoral::Parameter fully_qualified_param (param.type(), chn, param.id());
496 Gtk::CheckMenuItem* menu = automation_child_menu_item (fully_qualified_param);
499 menu->set_active (yn);
506 MidiTimeAxisView::add_channel_command_menu_item (Menu_Helpers::MenuList& items, const string& label, AutomationType auto_type, uint8_t cmd)
508 using namespace Menu_Helpers;
510 /* count the number of selected channels because we will build a different menu structure if there is more than 1 selected.
513 uint16_t selected_channels = _channel_selector.get_selected_channels();
516 for (uint8_t chn = 0; chn < 16; chn++) {
517 if (selected_channels & (0x0001 << chn)) {
526 /* multiple channels - create a submenu, with 1 item per channel */
528 Menu* chn_menu = manage (new Menu);
529 MenuList& chn_items (chn_menu->items());
530 Evoral::Parameter param_without_channel (auto_type, 0, cmd);
532 /* add a couple of items to hide/show all of them */
534 chn_items.push_back (MenuElem (_("Hide all channels"),
535 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
536 false, param_without_channel)));
537 chn_items.push_back (MenuElem (_("Show all channels"),
538 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
539 true, param_without_channel)));
541 for (uint8_t chn = 0; chn < 16; chn++) {
542 if (selected_channels & (0x0001 << chn)) {
544 /* for each selected channel, add a menu item for this controller */
546 Evoral::Parameter fully_qualified_param (auto_type, chn, cmd);
547 chn_items.push_back (CheckMenuElem (string_compose (_("Channel %1"), chn+1),
548 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
549 fully_qualified_param)));
551 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
552 bool visible = false;
555 if (track->marked_for_display()) {
560 CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&chn_items.back());
561 _channel_command_menu_map[fully_qualified_param] = cmi;
562 cmi->set_active (visible);
566 /* now create an item in the parent menu that has the per-channel list as a submenu */
568 items.push_back (MenuElem (label, *chn_menu));
572 /* just one channel - create a single menu item for this command+channel combination*/
574 for (uint8_t chn = 0; chn < 16; chn++) {
575 if (selected_channels & (0x0001 << chn)) {
577 Evoral::Parameter fully_qualified_param (auto_type, chn, cmd);
578 items.push_back (CheckMenuElem (label,
579 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
580 fully_qualified_param)));
582 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
583 bool visible = false;
586 if (track->marked_for_display()) {
591 CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&items.back());
592 _channel_command_menu_map[fully_qualified_param] = cmi;
593 cmi->set_active (visible);
595 /* one channel only */
603 MidiTimeAxisView::build_controller_menu ()
605 using namespace Menu_Helpers;
607 if (controller_menu) {
608 /* it exists and has not been invalidated by a channel mode change, so just return it */
612 controller_menu = new Menu; // explicitly managed by us
613 MenuList& items (controller_menu->items());
615 /* create several "top level" menu items for sets of controllers (16 at a time), and populate each one with a submenu
616 for each controller+channel combination covering the currently selected channels for this track
619 uint16_t selected_channels = _channel_selector.get_selected_channels();
621 /* count the number of selected channels because we will build a different menu structure if there is more than 1 selected.
626 for (uint8_t chn = 0; chn < 16; chn++) {
627 if (selected_channels & (0x0001 << chn)) {
634 using namespace MIDI::Name;
635 const string& model = _midnam_model_selector.get_active_text();
636 boost::shared_ptr<MIDINameDocument> midnam = MidiPatchManager::instance()
637 .document_by_model(model);
638 boost::shared_ptr<MasterDeviceNames> device_names;
639 if (midnam && !midnam->master_device_names_by_model().empty()) {
640 device_names = boost::shared_ptr<MasterDeviceNames>(
641 midnam->master_device_names_by_model().begin()->second);
644 if (device_names && !device_names->controls().empty()) {
645 /* Controllers names available from the midnam file, generate a custom controller menu */
646 for (MasterDeviceNames::ControlNameLists::const_iterator l = device_names->controls().begin();
647 l != device_names->controls().end(); ++l) {
648 boost::shared_ptr<ControlNameList> name_list = *l;
651 Menu* group_menu = manage(new Menu());
652 MenuList& group_items(group_menu->items());
654 for (ControlNameList::Controls::const_iterator c = (*l)->controls().begin();
655 c != (*l)->controls().end(); ++c) {
656 Evoral::Parameter fully_qualified_param(MidiCCAutomation, chn, atoi((*c)->number().c_str()));
657 group_items.push_back(
658 CheckMenuElem(string_compose("<b>%1</b>: %2 [%3]",
659 (*c)->number(), (*c)->name(), int(chn)),
660 sigc::bind(sigc::mem_fun(*this, &RouteTimeAxisView::toggle_automation_track),
661 fully_qualified_param)));
662 dynamic_cast<Label*> (group_items.back().get_child())->set_use_markup (true);
664 items.push_back(MenuElem(name_list->name(), *group_menu));
669 /* loop over all 127 MIDI controllers, in groups of 16; except don't offer
670 bank select controllers, as they are handled by the `patch' code */
672 for (int i = 0; i < 127; i += 16) {
674 Menu* ctl_menu = manage (new Menu);
675 MenuList& ctl_items (ctl_menu->items());
678 /* for each controller, consider whether to create a submenu or a single item */
680 for (int ctl = i; ctl < i+16; ++ctl) {
682 if (ctl == MIDI_CTL_MSB_BANK || ctl == MIDI_CTL_LSB_BANK) {
688 /* multiple channels - create a submenu, with 1 item per channel */
690 Menu* chn_menu = manage (new Menu);
691 MenuList& chn_items (chn_menu->items());
693 /* add a couple of items to hide/show this controller on all channels */
695 Evoral::Parameter param_without_channel (MidiCCAutomation, 0, ctl);
696 chn_items.push_back (MenuElem (_("Hide all channels"),
697 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
698 false, param_without_channel)));
699 chn_items.push_back (MenuElem (_("Show all channels"),
700 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
701 true, param_without_channel)));
703 for (uint8_t chn = 0; chn < 16; chn++) {
704 if (selected_channels & (0x0001 << chn)) {
706 /* for each selected channel, add a menu item for this controller */
708 Evoral::Parameter fully_qualified_param (MidiCCAutomation, chn, ctl);
709 chn_items.push_back (CheckMenuElem (string_compose (_("Channel %1"), chn+1),
710 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
711 fully_qualified_param)));
713 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
714 bool visible = false;
717 if (track->marked_for_display()) {
722 CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&chn_items.back());
723 _controller_menu_map[fully_qualified_param] = cmi;
724 cmi->set_active (visible);
728 /* add the per-channel menu to the list of controllers, with the name of the controller */
729 ctl_items.push_back (MenuElem (string_compose ("<b>%1</b>: %2", ctl, midi_name (ctl)), *chn_menu));
730 dynamic_cast<Label*> (ctl_items.back().get_child())->set_use_markup (true);
734 /* just one channel - create a single menu item for this ctl+channel combination*/
736 for (uint8_t chn = 0; chn < 16; chn++) {
737 if (selected_channels & (0x0001 << chn)) {
739 Evoral::Parameter fully_qualified_param (MidiCCAutomation, chn, ctl);
740 ctl_items.push_back (
742 string_compose ("<b>%1</b>: %2 [%3]", ctl, midi_name (ctl), int (chn)),
743 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
744 fully_qualified_param)
747 dynamic_cast<Label*> (ctl_items.back().get_child())->set_use_markup (true);
749 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
750 bool visible = false;
753 if (track->marked_for_display()) {
758 CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&ctl_items.back());
759 _controller_menu_map[fully_qualified_param] = cmi;
760 cmi->set_active (visible);
762 /* one channel only */
769 /* add the menu for this block of controllers to the overall controller menu */
771 items.push_back (MenuElem (string_compose (_("Controllers %1-%2"), i, i+15), *ctl_menu));
776 MidiTimeAxisView::build_note_mode_menu()
778 using namespace Menu_Helpers;
780 Menu* mode_menu = manage (new Menu);
781 MenuList& items = mode_menu->items();
782 mode_menu->set_name ("ArdourContextMenu");
784 RadioMenuItem::Group mode_group;
785 items.push_back (RadioMenuElem (mode_group,_("Sustained"),
786 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_mode), Sustained, true)));
787 _note_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
788 _note_mode_item->set_active(_note_mode == Sustained);
790 items.push_back (RadioMenuElem (mode_group, _("Percussive"),
791 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_mode), Percussive, true)));
792 _percussion_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
793 _percussion_mode_item->set_active(_note_mode == Percussive);
799 MidiTimeAxisView::build_color_mode_menu()
801 using namespace Menu_Helpers;
803 Menu* mode_menu = manage (new Menu);
804 MenuList& items = mode_menu->items();
805 mode_menu->set_name ("ArdourContextMenu");
807 RadioMenuItem::Group mode_group;
808 items.push_back (RadioMenuElem (mode_group, _("Meter Colors"),
809 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode),
810 MeterColors, false, true, true)));
811 _meter_color_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
812 _meter_color_mode_item->set_active(_color_mode == MeterColors);
814 items.push_back (RadioMenuElem (mode_group, _("Channel Colors"),
815 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode),
816 ChannelColors, false, true, true)));
817 _channel_color_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
818 _channel_color_mode_item->set_active(_color_mode == ChannelColors);
820 items.push_back (RadioMenuElem (mode_group, _("Track Color"),
821 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode),
822 TrackColor, false, true, true)));
823 _channel_color_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
824 _channel_color_mode_item->set_active(_color_mode == TrackColor);
830 MidiTimeAxisView::set_note_mode(NoteMode mode, bool apply_to_selection)
832 if (apply_to_selection) {
833 _editor.get_selection().tracks.foreach_midi_time_axis (boost::bind (&MidiTimeAxisView::set_note_mode, _1, mode, false));
835 if (_note_mode != mode || midi_track()->note_mode() != mode) {
837 midi_track()->set_note_mode(mode);
838 set_gui_property ("note-mode", enum_2_string(_note_mode));
839 _view->redisplay_track();
845 MidiTimeAxisView::set_color_mode (ColorMode mode, bool force, bool redisplay, bool apply_to_selection)
847 if (apply_to_selection) {
848 _editor.get_selection().tracks.foreach_midi_time_axis (
849 boost::bind (&MidiTimeAxisView::set_color_mode, _1, mode, force, redisplay, false)
853 if (_color_mode == mode && !force) {
857 if (mode == ChannelColors) {
858 _channel_selector.set_channel_colors(CanvasNoteEvent::midi_channel_colors);
860 _channel_selector.set_default_channel_color();
864 set_gui_property ("color-mode", enum_2_string(_color_mode));
866 _view->redisplay_track();
872 MidiTimeAxisView::set_note_range (MidiStreamView::VisibleNoteRange range, bool apply_to_selection)
874 if (apply_to_selection) {
875 _editor.get_selection().tracks.foreach_midi_time_axis (
876 boost::bind (&MidiTimeAxisView::set_note_range, _1, range, false)
879 if (!_ignore_signals) {
880 midi_view()->set_note_range(range);
886 MidiTimeAxisView::update_range()
888 MidiGhostRegion* mgr;
890 for(list<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
891 if ((mgr = dynamic_cast<MidiGhostRegion*>(*i)) != 0) {
898 MidiTimeAxisView::show_all_automation (bool apply_to_selection)
900 if (apply_to_selection) {
901 _editor.get_selection().tracks.foreach_midi_time_axis (boost::bind (&MidiTimeAxisView::show_all_automation, _1, false));
904 const set<Evoral::Parameter> params = midi_track()->midi_playlist()->contained_automation();
906 for (set<Evoral::Parameter>::const_iterator i = params.begin(); i != params.end(); ++i) {
907 create_automation_child(*i, true);
911 RouteTimeAxisView::show_all_automation ();
916 MidiTimeAxisView::show_existing_automation (bool apply_to_selection)
918 if (apply_to_selection) {
919 _editor.get_selection().tracks.foreach_midi_time_axis (boost::bind (&MidiTimeAxisView::show_existing_automation, _1, false));
922 const set<Evoral::Parameter> params = midi_track()->midi_playlist()->contained_automation();
924 for (set<Evoral::Parameter>::const_iterator i = params.begin(); i != params.end(); ++i) {
925 create_automation_child (*i, true);
929 RouteTimeAxisView::show_existing_automation ();
933 /** Create an automation track for the given parameter (pitch bend, channel pressure).
936 MidiTimeAxisView::create_automation_child (const Evoral::Parameter& param, bool show)
938 if (param.type() == NullAutomation) {
942 AutomationTracks::iterator existing = _automation_tracks.find (param);
944 if (existing != _automation_tracks.end()) {
946 /* automation track created because we had existing data for
947 * the processor, but visibility may need to be controlled
948 * since it will have been set visible by default.
951 if (existing->second->set_marked_for_display (show) && !no_redraw) {
958 boost::shared_ptr<AutomationTimeAxisView> track;
960 switch (param.type()) {
963 create_gain_automation_child (param, show);
966 case PluginAutomation:
967 /* handled elsewhere */
970 case MidiCCAutomation:
971 case MidiPgmChangeAutomation:
972 case MidiPitchBenderAutomation:
973 case MidiChannelPressureAutomation:
974 case MidiSystemExclusiveAutomation:
975 /* These controllers are region "automation" - they are owned
976 * by regions (and their MidiModels), not by the track. As a
977 * result we do not create an AutomationList/Line for the track
978 * ... except here we are doing something!! XXX
981 track.reset (new AutomationTimeAxisView (
984 boost::shared_ptr<Automatable> (),
985 boost::shared_ptr<AutomationControl> (),
991 _route->describe_parameter(param)
995 _view->foreach_regionview (sigc::mem_fun (*track.get(), &TimeAxisView::add_ghost));
998 add_automation_child (param, track, show);
1002 error << "MidiTimeAxisView: unknown automation child " << EventTypeMap::instance().to_symbol(param) << endmsg;
1007 MidiTimeAxisView::route_active_changed ()
1009 RouteUI::route_active_changed ();
1012 if (_route->active()) {
1013 controls_ebox.set_name ("MidiTrackControlsBaseUnselected");
1014 controls_base_selected_name = "MidiTrackControlsBaseSelected";
1015 controls_base_unselected_name = "MidiTrackControlsBaseUnselected";
1017 controls_ebox.set_name ("MidiTrackControlsBaseInactiveUnselected");
1018 controls_base_selected_name = "MidiTrackControlsBaseInactiveSelected";
1019 controls_base_unselected_name = "MidiTrackControlsBaseInactiveUnselected";
1025 if (_route->active()) {
1026 controls_ebox.set_name ("BusControlsBaseUnselected");
1027 controls_base_selected_name = "BusControlsBaseSelected";
1028 controls_base_unselected_name = "BusControlsBaseUnselected";
1030 controls_ebox.set_name ("BusControlsBaseInactiveUnselected");
1031 controls_base_selected_name = "BusControlsBaseInactiveSelected";
1032 controls_base_unselected_name = "BusControlsBaseInactiveUnselected";
1038 MidiTimeAxisView::set_note_selection (uint8_t note)
1040 if (!_editor.internal_editing()) {
1044 uint16_t chn_mask = _channel_selector.get_selected_channels();
1046 if (_view->num_selected_regionviews() == 0) {
1047 _view->foreach_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_selection_region_view), note, chn_mask));
1049 _view->foreach_selected_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_selection_region_view), note, chn_mask));
1054 MidiTimeAxisView::add_note_selection (uint8_t note)
1056 if (!_editor.internal_editing()) {
1060 uint16_t chn_mask = _channel_selector.get_selected_channels();
1062 if (_view->num_selected_regionviews() == 0) {
1063 _view->foreach_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection_region_view), note, chn_mask));
1065 _view->foreach_selected_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection_region_view), note, chn_mask));
1070 MidiTimeAxisView::extend_note_selection (uint8_t note)
1072 if (!_editor.internal_editing()) {
1076 uint16_t chn_mask = _channel_selector.get_selected_channels();
1078 if (_view->num_selected_regionviews() == 0) {
1079 _view->foreach_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::extend_note_selection_region_view), note, chn_mask));
1081 _view->foreach_selected_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::extend_note_selection_region_view), note, chn_mask));
1086 MidiTimeAxisView::toggle_note_selection (uint8_t note)
1088 if (!_editor.internal_editing()) {
1092 uint16_t chn_mask = _channel_selector.get_selected_channels();
1094 if (_view->num_selected_regionviews() == 0) {
1095 _view->foreach_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection_region_view), note, chn_mask));
1097 _view->foreach_selected_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection_region_view), note, chn_mask));
1102 MidiTimeAxisView::set_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1104 dynamic_cast<MidiRegionView*>(rv)->select_matching_notes (note, chn_mask, false, false);
1108 MidiTimeAxisView::add_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1110 dynamic_cast<MidiRegionView*>(rv)->select_matching_notes (note, chn_mask, true, false);
1114 MidiTimeAxisView::extend_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1116 dynamic_cast<MidiRegionView*>(rv)->select_matching_notes (note, chn_mask, true, true);
1120 MidiTimeAxisView::toggle_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1122 dynamic_cast<MidiRegionView*>(rv)->toggle_matching_notes (note, chn_mask);
1126 MidiTimeAxisView::set_channel_mode (ChannelMode, uint16_t)
1128 /* hide all automation tracks that use the wrong channel(s) and show all those that use
1132 uint16_t selected_channels = _channel_selector.get_selected_channels();
1133 bool changed = false;
1137 for (uint32_t ctl = 0; ctl < 127; ++ctl) {
1139 for (uint32_t chn = 0; chn < 16; ++chn) {
1140 Evoral::Parameter fully_qualified_param (MidiCCAutomation, chn, ctl);
1141 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
1147 if ((selected_channels & (0x0001 << chn)) == 0) {
1148 /* channel not in use. hiding it will trigger RouteTimeAxisView::automation_track_hidden()
1149 which will cause a redraw. We don't want one per channel, so block that with no_redraw.
1151 changed = track->set_marked_for_display (false) || changed;
1153 changed = track->set_marked_for_display (true) || changed;
1160 /* TODO: Bender, Pressure */
1162 /* invalidate the controller menu, so that we rebuild it next time */
1163 _controller_menu_map.clear ();
1164 delete controller_menu;
1165 controller_menu = 0;
1173 MidiTimeAxisView::automation_child_menu_item (Evoral::Parameter param)
1175 Gtk::CheckMenuItem* m = RouteTimeAxisView::automation_child_menu_item (param);
1180 ParameterMenuMap::iterator i = _controller_menu_map.find (param);
1181 if (i != _controller_menu_map.end()) {
1185 i = _channel_command_menu_map.find (param);
1186 if (i != _channel_command_menu_map.end()) {
1193 boost::shared_ptr<MidiRegion>
1194 MidiTimeAxisView::add_region (framepos_t pos, framecnt_t length, bool commit)
1196 Editor* real_editor = dynamic_cast<Editor*> (&_editor);
1198 real_editor->begin_reversible_command (Operations::create_region);
1199 playlist()->clear_changes ();
1201 real_editor->snap_to (pos, 0);
1203 boost::shared_ptr<Source> src = _session->create_midi_source_for_session (view()->trackview().track().get(),
1204 view()->trackview().track()->name());
1207 plist.add (ARDOUR::Properties::start, 0);
1208 plist.add (ARDOUR::Properties::length, length);
1209 plist.add (ARDOUR::Properties::name, PBD::basename_nosuffix(src->name()));
1211 boost::shared_ptr<Region> region = (RegionFactory::create (src, plist));
1213 playlist()->add_region (region, pos);
1214 _session->add_command (new StatefulDiffCommand (playlist()));
1217 real_editor->commit_reversible_command ();
1220 return boost::dynamic_pointer_cast<MidiRegion>(region);
1224 MidiTimeAxisView::ensure_step_editor ()
1226 if (!_step_editor) {
1227 _step_editor = new StepEditor (_editor, midi_track(), *this);
1232 MidiTimeAxisView::start_step_editing ()
1234 ensure_step_editor ();
1235 _step_editor->start_step_editing ();
1239 MidiTimeAxisView::stop_step_editing ()
1242 _step_editor->stop_step_editing ();
1247 /** @return channel (counted from 0) to add an event to, based on the current setting
1248 * of the channel selector.
1251 MidiTimeAxisView::get_channel_for_add () const
1253 uint16_t const chn_mask = _channel_selector.get_selected_channels ();
1255 uint8_t channel = 0;
1257 /* pick the highest selected channel, unless all channels are selected,
1258 which is interpreted to mean channel 1 (zero)
1261 for (uint16_t i = 0; i < 16; ++i) {
1262 if (chn_mask & (1<<i)) {
1268 if (chn_cnt == 16) {
1276 MidiTimeAxisView::note_range_changed ()
1278 set_gui_property ("note-range-min", (int) midi_view()->lowest_note ());
1279 set_gui_property ("note-range-max", (int) midi_view()->highest_note ());
1283 MidiTimeAxisView::contents_height_changed ()
1285 _range_scroomer->set_size_request (-1, _view->child_height ());