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 (
132 sigc::bind (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()),
148 atoi (gui_property ("note-range-max").c_str()),
151 midi_view()->NoteRangeChanged.connect (
152 sigc::mem_fun (*this, &MidiTimeAxisView::note_range_changed));
153 _view->ContentsHeightChanged.connect (
154 sigc::mem_fun (*this, &MidiTimeAxisView::contents_height_changed));
156 ignore_toggle = false;
158 if (is_midi_track()) {
159 controls_ebox.set_name ("MidiTimeAxisViewControlsBaseUnselected");
160 _note_mode = midi_track()->note_mode();
161 } else { // MIDI bus (which doesn't exist yet..)
162 controls_ebox.set_name ("MidiBusControlsBaseUnselected");
165 /* map current state of the route */
167 processors_changed (RouteProcessorChange ());
169 _route->processors_changed.connect (*this, invalidator (*this),
170 boost::bind (&MidiTimeAxisView::processors_changed, this, _1),
174 _piano_roll_header->SetNoteSelection.connect (
175 sigc::mem_fun (*this, &MidiTimeAxisView::set_note_selection));
176 _piano_roll_header->AddNoteSelection.connect (
177 sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection));
178 _piano_roll_header->ExtendNoteSelection.connect (
179 sigc::mem_fun (*this, &MidiTimeAxisView::extend_note_selection));
180 _piano_roll_header->ToggleNoteSelection.connect (
181 sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection));
183 /* Suspend updates of the StreamView during scroomer drags to speed things up */
184 _range_scroomer->DragStarting.connect (
185 sigc::mem_fun (*midi_view(), &MidiStreamView::suspend_updates));
186 _range_scroomer->DragFinishing.connect (
187 sigc::mem_fun (*midi_view(), &MidiStreamView::resume_updates));
189 /* Put the scroomer and the keyboard in a VBox with a padding
190 label so that they can be reduced in height for stacked-view
193 VBox* v = manage (new VBox);
194 HBox* h = manage (new HBox);
195 h->pack_start (*_range_scroomer);
196 h->pack_start (*_piano_roll_header);
197 v->pack_start (*h, false, false);
198 v->pack_start (*manage (new Label ("")), true, true);
201 controls_hbox.pack_start(*v);
203 controls_ebox.set_name ("MidiTrackControlsBaseUnselected");
204 controls_base_selected_name = "MidiTrackControlsBaseSelected";
205 controls_base_unselected_name = "MidiTrackControlsBaseUnselected";
207 midi_view()->NoteRangeChanged.connect (
208 sigc::mem_fun(*this, &MidiTimeAxisView::update_range));
210 /* ask for notifications of any new RegionViews */
211 _view->RegionViewAdded.connect (
212 sigc::mem_fun(*this, &MidiTimeAxisView::region_view_added));
214 if (!_editor.have_idled()) {
215 /* first idle will do what we need */
222 MIDI::Name::MidiPatchManager& patch_manager = MIDI::Name::MidiPatchManager::instance();
224 MIDI::Name::MasterDeviceNames::Models::const_iterator m = patch_manager.all_models().begin();
225 for (; m != patch_manager.all_models().end(); ++m) {
226 _midnam_model_selector.append_text(m->c_str());
229 _midnam_model_selector.set_active_text (gui_property (X_("midnam-model-name")));
230 _midnam_custom_device_mode_selector.set_active_text (gui_property (X_("midnam-custom-device-mode")));
232 ARDOUR_UI::instance()->set_tip (_midnam_model_selector, _("External MIDI Device"));
233 ARDOUR_UI::instance()->set_tip (_midnam_custom_device_mode_selector, _("External Device Mode"));
235 _midi_controls_box.set_homogeneous(false);
236 _midi_controls_box.set_border_width (10);
237 if (!patch_manager.all_models().empty()) {
238 _channel_selector.set_border_width(2);
239 _midi_controls_box.resize(3, 2);
240 _midi_controls_box.attach(_channel_selector, 0, 2, 0, 1);
242 _midi_controls_box.attach(*manage(new HSeparator()), 0, 2, 1, 2);
244 _midnam_model_selector.set_size_request(22, 30);
245 _midnam_model_selector.set_border_width(2);
246 _midi_controls_box.attach(_midnam_model_selector, 0, 1, 2, 3);
248 _midnam_custom_device_mode_selector.set_size_request(10, 30);
249 _midnam_custom_device_mode_selector.set_border_width(2);
250 _midi_controls_box.attach(_midnam_custom_device_mode_selector, 0, 1, 3, 4);
252 _midi_controls_box.attach(_channel_selector, 0, 1, 0, 1);
256 custom_device_mode_changed();
258 _midnam_model_selector.signal_changed().connect(
259 sigc::mem_fun(*this, &MidiTimeAxisView::model_changed));
260 _midnam_custom_device_mode_selector.signal_changed().connect(
261 sigc::mem_fun(*this, &MidiTimeAxisView::custom_device_mode_changed));
264 controls_vbox.pack_start(_midi_controls_box, false, false);
266 // restore channel selector settings
267 _channel_selector.set_channel_mode(midi_track()->get_channel_mode(),
268 midi_track()->get_channel_mask());
269 _channel_selector.mode_changed.connect(
270 sigc::mem_fun(*midi_track(), &MidiTrack::set_channel_mode));
271 _channel_selector.mode_changed.connect(
272 sigc::mem_fun(*this, &MidiTimeAxisView::set_channel_mode));
274 const string color_mode = gui_property ("color-mode");
275 if (!color_mode.empty()) {
276 _color_mode = ColorMode (string_2_enum(color_mode, _color_mode));
277 if (_color_mode == ChannelColors) {
278 _channel_selector.set_channel_colors(CanvasNoteEvent::midi_channel_colors);
282 set_color_mode (_color_mode, true, false);
284 const string note_mode = gui_property ("note-mode");
285 if (!note_mode.empty()) {
286 _note_mode = NoteMode (string_2_enum (note_mode, _note_mode));
287 if (_percussion_mode_item) {
288 _percussion_mode_item->set_active (_note_mode == Percussive);
292 /* Look for any GUI object state nodes that represent automation children
293 * that should exist, and create the children.
296 const list<string> gui_ids = gui_object_state().all_ids ();
297 for (list<string>::const_iterator i = gui_ids.begin(); i != gui_ids.end(); ++i) {
300 Evoral::Parameter parameter (0, 0, 0);
302 bool const p = AutomationTimeAxisView::parse_state_id (
303 *i, route_id, has_parameter, parameter);
304 if (p && route_id == _route->id () && has_parameter) {
305 const std::string& visible = gui_object_state().get_string (*i, X_("visible"));
306 create_automation_child (parameter, string_is_affirmative (visible));
312 MidiTimeAxisView::first_idle ()
319 MidiTimeAxisView::~MidiTimeAxisView ()
321 delete _piano_roll_header;
322 _piano_roll_header = 0;
324 delete _range_scroomer;
327 delete controller_menu;
332 MidiTimeAxisView::enter_internal_edit_mode ()
335 midi_view()->enter_internal_edit_mode ();
340 MidiTimeAxisView::leave_internal_edit_mode ()
343 midi_view()->leave_internal_edit_mode ();
348 MidiTimeAxisView::check_step_edit ()
350 ensure_step_editor ();
351 _step_editor->check_step_edit ();
355 MidiTimeAxisView::model_changed()
357 const Glib::ustring model = _midnam_model_selector.get_active_text();
358 set_gui_property (X_("midnam-model-name"), model);
360 const std::list<std::string> device_modes = MIDI::Name::MidiPatchManager::instance()
361 .custom_device_mode_names_by_model(model);
363 _midnam_custom_device_mode_selector.clear_items();
365 if (device_modes.size() < 2) {
366 _midnam_custom_device_mode_selector.hide();
368 _midnam_custom_device_mode_selector.show();
369 for (std::list<std::string>::const_iterator i = device_modes.begin();
370 i != device_modes.end(); ++i) {
371 _midnam_custom_device_mode_selector.append_text(*i);
375 _midnam_custom_device_mode_selector.set_active(0);
377 _route->instrument_info().set_external_instrument (
378 _midnam_model_selector.get_active_text(),
379 _midnam_custom_device_mode_selector.get_active_text());
381 // Rebuild controller menu
382 _controller_menu_map.clear ();
383 delete controller_menu;
385 build_automation_action_menu(false);
389 MidiTimeAxisView::custom_device_mode_changed()
391 const Glib::ustring mode = _midnam_custom_device_mode_selector.get_active_text();
392 set_gui_property (X_("midnam-custom-device-mode"), mode);
393 _route->instrument_info().set_external_instrument (
394 _midnam_model_selector.get_active_text(), mode);
398 MidiTimeAxisView::midi_view()
400 return dynamic_cast<MidiStreamView*>(_view);
404 MidiTimeAxisView::set_height (uint32_t h)
406 if (h >= MIDI_CONTROLS_BOX_MIN_HEIGHT) {
407 _midi_controls_box.show ();
409 _midi_controls_box.hide();
412 if (h >= KEYBOARD_MIN_HEIGHT) {
413 if (is_track() && _range_scroomer) {
414 _range_scroomer->show();
416 if (is_track() && _piano_roll_header) {
417 _piano_roll_header->show();
420 if (is_track() && _range_scroomer) {
421 _range_scroomer->hide();
423 if (is_track() && _piano_roll_header) {
424 _piano_roll_header->hide();
428 /* We need to do this after changing visibility of our stuff, as it will
429 eventually trigger a call to Editor::reset_controls_layout_width(),
430 which needs to know if we have just shown or hidden a scroomer /
433 RouteTimeAxisView::set_height (h);
437 MidiTimeAxisView::append_extra_display_menu_items ()
439 using namespace Menu_Helpers;
441 MenuList& items = display_menu->items();
444 Menu *range_menu = manage(new Menu);
445 MenuList& range_items = range_menu->items();
446 range_menu->set_name ("ArdourContextMenu");
448 range_items.push_back (
449 MenuElem (_("Show Full Range"),
450 sigc::bind (sigc::mem_fun(*this, &MidiTimeAxisView::set_note_range),
451 MidiStreamView::FullRange, true)));
453 range_items.push_back (
454 MenuElem (_("Fit Contents"),
455 sigc::bind (sigc::mem_fun(*this, &MidiTimeAxisView::set_note_range),
456 MidiStreamView::ContentsRange, true)));
458 items.push_back (MenuElem (_("Note Range"), *range_menu));
459 items.push_back (MenuElem (_("Note Mode"), *build_note_mode_menu()));
461 items.push_back (SeparatorElem ());
465 MidiTimeAxisView::build_automation_action_menu (bool for_selection)
467 using namespace Menu_Helpers;
469 /* If we have a controller menu, we need to detach it before
470 RouteTimeAxis::build_automation_action_menu destroys the
471 menu it is attached to. Otherwise GTK destroys
472 controller_menu's gobj, meaning that it can't be reattached
473 below. See bug #3134.
476 if (controller_menu) {
477 detach_menu (*controller_menu);
480 _channel_command_menu_map.clear ();
481 RouteTimeAxisView::build_automation_action_menu (for_selection);
483 MenuList& automation_items = automation_action_menu->items();
485 uint16_t selected_channels = _channel_selector.get_selected_channels();
487 if (selected_channels != 0) {
489 automation_items.push_back (SeparatorElem());
491 /* these 2 MIDI "command" types are semantically more like automation
492 than note data, but they are not MIDI controllers. We give them
493 special status in this menu, since they will not show up in the
494 controller list and anyone who actually knows something about MIDI
495 (!) would not expect to find them there.
498 add_channel_command_menu_item (
499 automation_items, _("Bender"), MidiPitchBenderAutomation, 0);
500 automation_items.back().set_sensitive (
501 !for_selection || _editor.get_selection().tracks.size() == 1);
502 add_channel_command_menu_item (
503 automation_items, _("Pressure"), MidiChannelPressureAutomation, 0);
504 automation_items.back().set_sensitive (
505 !for_selection || _editor.get_selection().tracks.size() == 1);
507 /* now all MIDI controllers. Always offer the possibility that we will
508 rebuild the controllers menu since it might need to be updated after
509 a channel mode change or other change. Also detach it first in case
510 it has been used anywhere else.
513 build_controller_menu ();
515 automation_items.push_back (SeparatorElem());
516 automation_items.push_back (MenuElem (_("Controllers"), *controller_menu));
517 automation_items.back().set_sensitive (
518 !for_selection || _editor.get_selection().tracks.size() == 1);
520 automation_items.push_back (
521 MenuElem (string_compose ("<i>%1</i>", _("No MIDI Channels selected"))));
522 dynamic_cast<Label*> (automation_items.back().get_child())->set_use_markup (true);
527 MidiTimeAxisView::change_all_channel_tracks_visibility (bool yn, Evoral::Parameter param)
529 const uint16_t selected_channels = _channel_selector.get_selected_channels();
531 for (uint8_t chn = 0; chn < 16; chn++) {
532 if (selected_channels & (0x0001 << chn)) {
534 Evoral::Parameter fully_qualified_param (param.type(), chn, param.id());
535 Gtk::CheckMenuItem* menu = automation_child_menu_item (fully_qualified_param);
538 menu->set_active (yn);
545 MidiTimeAxisView::add_channel_command_menu_item (Menu_Helpers::MenuList& items,
547 AutomationType auto_type,
550 using namespace Menu_Helpers;
552 /* count the number of selected channels because we will build a different menu
553 structure if there is more than 1 selected.
556 const uint16_t selected_channels = _channel_selector.get_selected_channels();
559 for (uint8_t chn = 0; chn < 16; chn++) {
560 if (selected_channels & (0x0001 << chn)) {
569 /* multiple channels - create a submenu, with 1 item per channel */
571 Menu* chn_menu = manage (new Menu);
572 MenuList& chn_items (chn_menu->items());
573 Evoral::Parameter param_without_channel (auto_type, 0, cmd);
575 /* add a couple of items to hide/show all of them */
577 chn_items.push_back (
578 MenuElem (_("Hide all channels"),
579 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
580 false, param_without_channel)));
581 chn_items.push_back (
582 MenuElem (_("Show all channels"),
583 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
584 true, param_without_channel)));
586 for (uint8_t chn = 0; chn < 16; chn++) {
587 if (selected_channels & (0x0001 << chn)) {
589 /* for each selected channel, add a menu item for this controller */
591 Evoral::Parameter fully_qualified_param (auto_type, chn, cmd);
592 chn_items.push_back (
593 CheckMenuElem (string_compose (_("Channel %1"), chn+1),
594 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
595 fully_qualified_param)));
597 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
598 bool visible = false;
601 if (track->marked_for_display()) {
606 CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&chn_items.back());
607 _channel_command_menu_map[fully_qualified_param] = cmi;
608 cmi->set_active (visible);
612 /* now create an item in the parent menu that has the per-channel list as a submenu */
614 items.push_back (MenuElem (label, *chn_menu));
618 /* just one channel - create a single menu item for this command+channel combination*/
620 for (uint8_t chn = 0; chn < 16; chn++) {
621 if (selected_channels & (0x0001 << chn)) {
623 Evoral::Parameter fully_qualified_param (auto_type, chn, cmd);
625 CheckMenuElem (label,
626 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
627 fully_qualified_param)));
629 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
630 bool visible = false;
633 if (track->marked_for_display()) {
638 CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&items.back());
639 _channel_command_menu_map[fully_qualified_param] = cmi;
640 cmi->set_active (visible);
642 /* one channel only */
649 /** Add a single menu item for a controller on one channel. */
651 MidiTimeAxisView::add_single_channel_controller_item(Menu_Helpers::MenuList& ctl_items,
653 const std::string& name)
655 using namespace Menu_Helpers;
657 const uint16_t selected_channels = _channel_selector.get_selected_channels();
658 for (uint8_t chn = 0; chn < 16; chn++) {
659 if (selected_channels & (0x0001 << chn)) {
661 Evoral::Parameter fully_qualified_param (MidiCCAutomation, chn, ctl);
662 ctl_items.push_back (
664 string_compose ("<b>%1</b>: %2 [%3]", ctl, name, int (chn)),
666 sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
667 fully_qualified_param)));
668 dynamic_cast<Label*> (ctl_items.back().get_child())->set_use_markup (true);
670 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (
671 fully_qualified_param);
673 bool visible = false;
675 if (track->marked_for_display()) {
680 CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&ctl_items.back());
681 _controller_menu_map[fully_qualified_param] = cmi;
682 cmi->set_active (visible);
684 /* one channel only */
690 /** Add a submenu with 1 item per channel for a controller on many channels. */
692 MidiTimeAxisView::add_multi_channel_controller_item(Menu_Helpers::MenuList& ctl_items,
694 const std::string& name)
696 using namespace Menu_Helpers;
698 const uint16_t selected_channels = _channel_selector.get_selected_channels();
700 Menu* chn_menu = manage (new Menu);
701 MenuList& chn_items (chn_menu->items());
703 /* add a couple of items to hide/show this controller on all channels */
705 Evoral::Parameter param_without_channel (MidiCCAutomation, 0, ctl);
706 chn_items.push_back (
707 MenuElem (_("Hide all channels"),
708 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
709 false, param_without_channel)));
710 chn_items.push_back (
711 MenuElem (_("Show all channels"),
712 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
713 true, param_without_channel)));
715 for (uint8_t chn = 0; chn < 16; chn++) {
716 if (selected_channels & (0x0001 << chn)) {
718 /* for each selected channel, add a menu item for this controller */
720 Evoral::Parameter fully_qualified_param (MidiCCAutomation, chn, ctl);
721 chn_items.push_back (
722 CheckMenuElem (string_compose (_("Channel %1"), chn+1),
723 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
724 fully_qualified_param)));
726 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (
727 fully_qualified_param);
728 bool visible = false;
731 if (track->marked_for_display()) {
736 CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&chn_items.back());
737 _controller_menu_map[fully_qualified_param] = cmi;
738 cmi->set_active (visible);
742 /* add the per-channel menu to the list of controllers, with the name of the controller */
743 ctl_items.push_back (MenuElem (string_compose ("<b>%1</b>: %2", ctl, name),
745 dynamic_cast<Label*> (ctl_items.back().get_child())->set_use_markup (true);
749 MidiTimeAxisView::build_controller_menu ()
751 using namespace Menu_Helpers;
753 if (controller_menu) {
754 /* it exists and has not been invalidated by a channel mode change */
758 controller_menu = new Menu; // explicitly managed by us
759 MenuList& items (controller_menu->items());
761 /* create several "top level" menu items for sets of controllers (16 at a
762 time), and populate each one with a submenu for each controller+channel
763 combination covering the currently selected channels for this track
766 const uint16_t selected_channels = _channel_selector.get_selected_channels();
768 /* count the number of selected channels because we will build a different menu
769 structure if there is more than 1 selected.
773 for (uint8_t chn = 0; chn < 16; chn++) {
774 if (selected_channels & (0x0001 << chn)) {
781 using namespace MIDI::Name;
782 const Glib::ustring model = _midnam_model_selector.get_active_text();
783 boost::shared_ptr<MIDINameDocument> midnam = MidiPatchManager::instance()
784 .document_by_model(model);
785 boost::shared_ptr<MasterDeviceNames> device_names;
788 device_names = midnam->master_device_names(model);
791 if (device_names && !device_names->controls().empty()) {
792 /* Controllers names available in midnam file, generate fancy menu */
793 unsigned n_items = 0;
794 unsigned n_groups = 0;
795 for (MasterDeviceNames::ControlNameLists::const_iterator l = device_names->controls().begin();
796 l != device_names->controls().end(); ++l) {
797 boost::shared_ptr<ControlNameList> name_list = *l;
798 Menu* ctl_menu = NULL;
800 for (ControlNameList::Controls::const_iterator c = (*l)->controls().begin();
801 c != (*l)->controls().end(); ++c) {
802 const int ctl = atoi((*c)->number().c_str());
803 if (ctl == MIDI_CTL_MSB_BANK || ctl == MIDI_CTL_LSB_BANK) {
804 /* Skip bank select controllers since they're handled specially */
809 /* Create a new submenu */
810 ctl_menu = manage (new Menu);
813 MenuList& ctl_items (ctl_menu->items());
815 add_multi_channel_controller_item(ctl_items, ctl, (*c)->name());
817 add_single_channel_controller_item(ctl_items, ctl, (*c)->name());
820 if (++n_items == 16 || c == (*l)->controls().end()) {
821 /* Submenu has 16 items, add it to controller menu and reset */
823 MenuElem(string_compose(_("Controllers %1-%2"),
824 (16 * n_groups), (16 * n_groups) + n_items - 1),
833 /* No controllers names, generate generic numeric menu */
834 for (int i = 0; i < 127; i += 16) {
835 Menu* ctl_menu = manage (new Menu);
836 MenuList& ctl_items (ctl_menu->items());
838 for (int ctl = i; ctl < i+16; ++ctl) {
839 if (ctl == MIDI_CTL_MSB_BANK || ctl == MIDI_CTL_LSB_BANK) {
840 /* Skip bank select controllers since they're handled specially */
845 add_multi_channel_controller_item(
846 ctl_items, ctl, string_compose(_("Controller %1"), ctl));
848 add_single_channel_controller_item(
849 ctl_items, ctl, string_compose(_("Controller %1"), ctl));
853 /* Add submenu for this block of controllers to controller menu */
855 MenuElem (string_compose (_("Controllers %1-%2"), i, i + 15),
862 MidiTimeAxisView::build_note_mode_menu()
864 using namespace Menu_Helpers;
866 Menu* mode_menu = manage (new Menu);
867 MenuList& items = mode_menu->items();
868 mode_menu->set_name ("ArdourContextMenu");
870 RadioMenuItem::Group mode_group;
872 RadioMenuElem (mode_group,_("Sustained"),
873 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_mode),
875 _note_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
876 _note_mode_item->set_active(_note_mode == Sustained);
879 RadioMenuElem (mode_group, _("Percussive"),
880 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_mode),
882 _percussion_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
883 _percussion_mode_item->set_active(_note_mode == Percussive);
889 MidiTimeAxisView::build_color_mode_menu()
891 using namespace Menu_Helpers;
893 Menu* mode_menu = manage (new Menu);
894 MenuList& items = mode_menu->items();
895 mode_menu->set_name ("ArdourContextMenu");
897 RadioMenuItem::Group mode_group;
899 RadioMenuElem (mode_group, _("Meter Colors"),
900 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode),
901 MeterColors, false, true, true)));
902 _meter_color_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
903 _meter_color_mode_item->set_active(_color_mode == MeterColors);
906 RadioMenuElem (mode_group, _("Channel Colors"),
907 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode),
908 ChannelColors, false, true, true)));
909 _channel_color_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
910 _channel_color_mode_item->set_active(_color_mode == ChannelColors);
913 RadioMenuElem (mode_group, _("Track Color"),
914 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode),
915 TrackColor, false, true, true)));
916 _channel_color_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
917 _channel_color_mode_item->set_active(_color_mode == TrackColor);
923 MidiTimeAxisView::set_note_mode(NoteMode mode, bool apply_to_selection)
925 if (apply_to_selection) {
926 _editor.get_selection().tracks.foreach_midi_time_axis (
927 boost::bind (&MidiTimeAxisView::set_note_mode, _1, mode, false));
929 if (_note_mode != mode || midi_track()->note_mode() != mode) {
931 midi_track()->set_note_mode(mode);
932 set_gui_property ("note-mode", enum_2_string(_note_mode));
933 _view->redisplay_track();
939 MidiTimeAxisView::set_color_mode (ColorMode mode, bool force, bool redisplay, bool apply_to_selection)
941 if (apply_to_selection) {
942 _editor.get_selection().tracks.foreach_midi_time_axis (
943 boost::bind (&MidiTimeAxisView::set_color_mode, _1, mode, force, redisplay, false));
945 if (_color_mode == mode && !force) {
949 if (mode == ChannelColors) {
950 _channel_selector.set_channel_colors(CanvasNoteEvent::midi_channel_colors);
952 _channel_selector.set_default_channel_color();
956 set_gui_property ("color-mode", enum_2_string(_color_mode));
958 _view->redisplay_track();
964 MidiTimeAxisView::set_note_range (MidiStreamView::VisibleNoteRange range, bool apply_to_selection)
966 if (apply_to_selection) {
967 _editor.get_selection().tracks.foreach_midi_time_axis (
968 boost::bind (&MidiTimeAxisView::set_note_range, _1, range, false));
970 if (!_ignore_signals) {
971 midi_view()->set_note_range(range);
977 MidiTimeAxisView::update_range()
979 MidiGhostRegion* mgr;
981 for (list<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
982 if ((mgr = dynamic_cast<MidiGhostRegion*>(*i)) != 0) {
989 MidiTimeAxisView::show_all_automation (bool apply_to_selection)
991 if (apply_to_selection) {
992 _editor.get_selection().tracks.foreach_midi_time_axis (
993 boost::bind (&MidiTimeAxisView::show_all_automation, _1, false));
996 const set<Evoral::Parameter> params = midi_track()->midi_playlist()->contained_automation();
998 for (set<Evoral::Parameter>::const_iterator i = params.begin(); i != params.end(); ++i) {
999 create_automation_child(*i, true);
1003 RouteTimeAxisView::show_all_automation ();
1008 MidiTimeAxisView::show_existing_automation (bool apply_to_selection)
1010 if (apply_to_selection) {
1011 _editor.get_selection().tracks.foreach_midi_time_axis (
1012 boost::bind (&MidiTimeAxisView::show_existing_automation, _1, false));
1015 const set<Evoral::Parameter> params = midi_track()->midi_playlist()->contained_automation();
1017 for (set<Evoral::Parameter>::const_iterator i = params.begin(); i != params.end(); ++i) {
1018 create_automation_child (*i, true);
1022 RouteTimeAxisView::show_existing_automation ();
1026 /** Create an automation track for the given parameter (pitch bend, channel pressure).
1029 MidiTimeAxisView::create_automation_child (const Evoral::Parameter& param, bool show)
1031 if (param.type() == NullAutomation) {
1035 AutomationTracks::iterator existing = _automation_tracks.find (param);
1037 if (existing != _automation_tracks.end()) {
1039 /* automation track created because we had existing data for
1040 * the processor, but visibility may need to be controlled
1041 * since it will have been set visible by default.
1044 if (existing->second->set_marked_for_display (show) && !no_redraw) {
1051 boost::shared_ptr<AutomationTimeAxisView> track;
1053 switch (param.type()) {
1055 case GainAutomation:
1056 create_gain_automation_child (param, show);
1059 case PluginAutomation:
1060 /* handled elsewhere */
1063 case MidiCCAutomation:
1064 case MidiPgmChangeAutomation:
1065 case MidiPitchBenderAutomation:
1066 case MidiChannelPressureAutomation:
1067 case MidiSystemExclusiveAutomation:
1068 /* These controllers are region "automation" - they are owned
1069 * by regions (and their MidiModels), not by the track. As a
1070 * result we do not create an AutomationList/Line for the track
1071 * ... except here we are doing something!! XXX
1074 track.reset (new AutomationTimeAxisView (
1077 boost::shared_ptr<Automatable> (),
1078 boost::shared_ptr<AutomationControl> (),
1084 _route->describe_parameter(param)));
1087 _view->foreach_regionview (
1088 sigc::mem_fun (*track.get(), &TimeAxisView::add_ghost));
1091 add_automation_child (param, track, show);
1095 error << "MidiTimeAxisView: unknown automation child "
1096 << EventTypeMap::instance().to_symbol(param) << endmsg;
1101 MidiTimeAxisView::route_active_changed ()
1103 RouteUI::route_active_changed ();
1106 if (_route->active()) {
1107 controls_ebox.set_name ("MidiTrackControlsBaseUnselected");
1108 controls_base_selected_name = "MidiTrackControlsBaseSelected";
1109 controls_base_unselected_name = "MidiTrackControlsBaseUnselected";
1111 controls_ebox.set_name ("MidiTrackControlsBaseInactiveUnselected");
1112 controls_base_selected_name = "MidiTrackControlsBaseInactiveSelected";
1113 controls_base_unselected_name = "MidiTrackControlsBaseInactiveUnselected";
1116 if (_route->active()) {
1117 controls_ebox.set_name ("BusControlsBaseUnselected");
1118 controls_base_selected_name = "BusControlsBaseSelected";
1119 controls_base_unselected_name = "BusControlsBaseUnselected";
1121 controls_ebox.set_name ("BusControlsBaseInactiveUnselected");
1122 controls_base_selected_name = "BusControlsBaseInactiveSelected";
1123 controls_base_unselected_name = "BusControlsBaseInactiveUnselected";
1129 MidiTimeAxisView::set_note_selection (uint8_t note)
1131 if (!_editor.internal_editing()) {
1135 uint16_t chn_mask = _channel_selector.get_selected_channels();
1137 if (_view->num_selected_regionviews() == 0) {
1138 _view->foreach_regionview (
1139 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_selection_region_view),
1142 _view->foreach_selected_regionview (
1143 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_selection_region_view),
1149 MidiTimeAxisView::add_note_selection (uint8_t note)
1151 if (!_editor.internal_editing()) {
1155 const uint16_t chn_mask = _channel_selector.get_selected_channels();
1157 if (_view->num_selected_regionviews() == 0) {
1158 _view->foreach_regionview (
1159 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection_region_view),
1162 _view->foreach_selected_regionview (
1163 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection_region_view),
1169 MidiTimeAxisView::extend_note_selection (uint8_t note)
1171 if (!_editor.internal_editing()) {
1175 const uint16_t chn_mask = _channel_selector.get_selected_channels();
1177 if (_view->num_selected_regionviews() == 0) {
1178 _view->foreach_regionview (
1179 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::extend_note_selection_region_view),
1182 _view->foreach_selected_regionview (
1183 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::extend_note_selection_region_view),
1189 MidiTimeAxisView::toggle_note_selection (uint8_t note)
1191 if (!_editor.internal_editing()) {
1195 const uint16_t chn_mask = _channel_selector.get_selected_channels();
1197 if (_view->num_selected_regionviews() == 0) {
1198 _view->foreach_regionview (
1199 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection_region_view),
1202 _view->foreach_selected_regionview (
1203 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection_region_view),
1209 MidiTimeAxisView::set_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1211 dynamic_cast<MidiRegionView*>(rv)->select_matching_notes (note, chn_mask, false, false);
1215 MidiTimeAxisView::add_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1217 dynamic_cast<MidiRegionView*>(rv)->select_matching_notes (note, chn_mask, true, false);
1221 MidiTimeAxisView::extend_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1223 dynamic_cast<MidiRegionView*>(rv)->select_matching_notes (note, chn_mask, true, true);
1227 MidiTimeAxisView::toggle_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1229 dynamic_cast<MidiRegionView*>(rv)->toggle_matching_notes (note, chn_mask);
1233 MidiTimeAxisView::set_channel_mode (ChannelMode, uint16_t)
1235 /* hide all automation tracks that use the wrong channel(s) and show all those that use
1239 const uint16_t selected_channels = _channel_selector.get_selected_channels();
1240 bool changed = false;
1244 for (uint32_t ctl = 0; ctl < 127; ++ctl) {
1246 for (uint32_t chn = 0; chn < 16; ++chn) {
1247 Evoral::Parameter fully_qualified_param (MidiCCAutomation, chn, ctl);
1248 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
1254 if ((selected_channels & (0x0001 << chn)) == 0) {
1255 /* channel not in use. hiding it will trigger RouteTimeAxisView::automation_track_hidden()
1256 which will cause a redraw. We don't want one per channel, so block that with no_redraw.
1258 changed = track->set_marked_for_display (false) || changed;
1260 changed = track->set_marked_for_display (true) || changed;
1267 /* TODO: Bender, Pressure */
1269 /* invalidate the controller menu, so that we rebuild it next time */
1270 _controller_menu_map.clear ();
1271 delete controller_menu;
1272 controller_menu = 0;
1280 MidiTimeAxisView::automation_child_menu_item (Evoral::Parameter param)
1282 Gtk::CheckMenuItem* m = RouteTimeAxisView::automation_child_menu_item (param);
1287 ParameterMenuMap::iterator i = _controller_menu_map.find (param);
1288 if (i != _controller_menu_map.end()) {
1292 i = _channel_command_menu_map.find (param);
1293 if (i != _channel_command_menu_map.end()) {
1300 boost::shared_ptr<MidiRegion>
1301 MidiTimeAxisView::add_region (framepos_t pos, framecnt_t length, bool commit)
1303 Editor* real_editor = dynamic_cast<Editor*> (&_editor);
1305 real_editor->begin_reversible_command (Operations::create_region);
1306 playlist()->clear_changes ();
1308 real_editor->snap_to (pos, 0);
1310 boost::shared_ptr<Source> src = _session->create_midi_source_for_session (
1311 view()->trackview().track().get(), view()->trackview().track()->name());
1314 plist.add (ARDOUR::Properties::start, 0);
1315 plist.add (ARDOUR::Properties::length, length);
1316 plist.add (ARDOUR::Properties::name, PBD::basename_nosuffix(src->name()));
1318 boost::shared_ptr<Region> region = (RegionFactory::create (src, plist));
1320 playlist()->add_region (region, pos);
1321 _session->add_command (new StatefulDiffCommand (playlist()));
1324 real_editor->commit_reversible_command ();
1327 return boost::dynamic_pointer_cast<MidiRegion>(region);
1331 MidiTimeAxisView::ensure_step_editor ()
1333 if (!_step_editor) {
1334 _step_editor = new StepEditor (_editor, midi_track(), *this);
1339 MidiTimeAxisView::start_step_editing ()
1341 ensure_step_editor ();
1342 _step_editor->start_step_editing ();
1346 MidiTimeAxisView::stop_step_editing ()
1349 _step_editor->stop_step_editing ();
1354 /** @return channel (counted from 0) to add an event to, based on the current setting
1355 * of the channel selector.
1358 MidiTimeAxisView::get_channel_for_add () const
1360 uint16_t const chn_mask = _channel_selector.get_selected_channels ();
1362 uint8_t channel = 0;
1364 /* pick the highest selected channel, unless all channels are selected,
1365 which is interpreted to mean channel 1 (zero)
1368 for (uint16_t i = 0; i < 16; ++i) {
1369 if (chn_mask & (1<<i)) {
1375 if (chn_cnt == 16) {
1383 MidiTimeAxisView::note_range_changed ()
1385 set_gui_property ("note-range-min", (int) midi_view()->lowest_note ());
1386 set_gui_property ("note-range-max", (int) midi_view()->highest_note ());
1390 MidiTimeAxisView::contents_height_changed ()
1392 _range_scroomer->set_size_request (-1, _view->child_height ());