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 = midnam->master_device_names(
788 if (device_names && !device_names->controls().empty()) {
789 /* Controllers names available in midnam file, generate fancy menu */
790 unsigned n_items = 0;
791 unsigned n_groups = 0;
792 for (MasterDeviceNames::ControlNameLists::const_iterator l = device_names->controls().begin();
793 l != device_names->controls().end(); ++l) {
794 boost::shared_ptr<ControlNameList> name_list = *l;
795 Menu* ctl_menu = NULL;
797 for (ControlNameList::Controls::const_iterator c = (*l)->controls().begin();
798 c != (*l)->controls().end(); ++c) {
799 const int ctl = atoi((*c)->number().c_str());
800 if (ctl == MIDI_CTL_MSB_BANK || ctl == MIDI_CTL_LSB_BANK) {
801 /* Skip bank select controllers since they're handled specially */
806 /* Create a new submenu */
807 ctl_menu = manage (new Menu);
810 MenuList& ctl_items (ctl_menu->items());
812 add_multi_channel_controller_item(ctl_items, ctl, (*c)->name());
814 add_single_channel_controller_item(ctl_items, ctl, (*c)->name());
817 if (++n_items == 16 || c == (*l)->controls().end()) {
818 /* Submenu has 16 items, add it to controller menu and reset */
820 MenuElem(string_compose(_("Controllers %1-%2"),
821 (16 * n_groups), (16 * n_groups) + n_items - 1),
830 /* No controllers names, generate generic numeric menu */
831 for (int i = 0; i < 127; i += 16) {
832 Menu* ctl_menu = manage (new Menu);
833 MenuList& ctl_items (ctl_menu->items());
835 for (int ctl = i; ctl < i+16; ++ctl) {
836 if (ctl == MIDI_CTL_MSB_BANK || ctl == MIDI_CTL_LSB_BANK) {
837 /* Skip bank select controllers since they're handled specially */
842 add_multi_channel_controller_item(
843 ctl_items, ctl, string_compose(_("Controller %1"), ctl));
845 add_single_channel_controller_item(
846 ctl_items, ctl, string_compose(_("Controller %1"), ctl));
850 /* Add submenu for this block of controllers to controller menu */
852 MenuElem (string_compose (_("Controllers %1-%2"), i, i + 15),
859 MidiTimeAxisView::build_note_mode_menu()
861 using namespace Menu_Helpers;
863 Menu* mode_menu = manage (new Menu);
864 MenuList& items = mode_menu->items();
865 mode_menu->set_name ("ArdourContextMenu");
867 RadioMenuItem::Group mode_group;
869 RadioMenuElem (mode_group,_("Sustained"),
870 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_mode),
872 _note_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
873 _note_mode_item->set_active(_note_mode == Sustained);
876 RadioMenuElem (mode_group, _("Percussive"),
877 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_mode),
879 _percussion_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
880 _percussion_mode_item->set_active(_note_mode == Percussive);
886 MidiTimeAxisView::build_color_mode_menu()
888 using namespace Menu_Helpers;
890 Menu* mode_menu = manage (new Menu);
891 MenuList& items = mode_menu->items();
892 mode_menu->set_name ("ArdourContextMenu");
894 RadioMenuItem::Group mode_group;
896 RadioMenuElem (mode_group, _("Meter Colors"),
897 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode),
898 MeterColors, false, true, true)));
899 _meter_color_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
900 _meter_color_mode_item->set_active(_color_mode == MeterColors);
903 RadioMenuElem (mode_group, _("Channel Colors"),
904 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode),
905 ChannelColors, false, true, true)));
906 _channel_color_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
907 _channel_color_mode_item->set_active(_color_mode == ChannelColors);
910 RadioMenuElem (mode_group, _("Track Color"),
911 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode),
912 TrackColor, false, true, true)));
913 _channel_color_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
914 _channel_color_mode_item->set_active(_color_mode == TrackColor);
920 MidiTimeAxisView::set_note_mode(NoteMode mode, bool apply_to_selection)
922 if (apply_to_selection) {
923 _editor.get_selection().tracks.foreach_midi_time_axis (
924 boost::bind (&MidiTimeAxisView::set_note_mode, _1, mode, false));
926 if (_note_mode != mode || midi_track()->note_mode() != mode) {
928 midi_track()->set_note_mode(mode);
929 set_gui_property ("note-mode", enum_2_string(_note_mode));
930 _view->redisplay_track();
936 MidiTimeAxisView::set_color_mode (ColorMode mode, bool force, bool redisplay, bool apply_to_selection)
938 if (apply_to_selection) {
939 _editor.get_selection().tracks.foreach_midi_time_axis (
940 boost::bind (&MidiTimeAxisView::set_color_mode, _1, mode, force, redisplay, false));
942 if (_color_mode == mode && !force) {
946 if (mode == ChannelColors) {
947 _channel_selector.set_channel_colors(CanvasNoteEvent::midi_channel_colors);
949 _channel_selector.set_default_channel_color();
953 set_gui_property ("color-mode", enum_2_string(_color_mode));
955 _view->redisplay_track();
961 MidiTimeAxisView::set_note_range (MidiStreamView::VisibleNoteRange range, bool apply_to_selection)
963 if (apply_to_selection) {
964 _editor.get_selection().tracks.foreach_midi_time_axis (
965 boost::bind (&MidiTimeAxisView::set_note_range, _1, range, false));
967 if (!_ignore_signals) {
968 midi_view()->set_note_range(range);
974 MidiTimeAxisView::update_range()
976 MidiGhostRegion* mgr;
978 for (list<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
979 if ((mgr = dynamic_cast<MidiGhostRegion*>(*i)) != 0) {
986 MidiTimeAxisView::show_all_automation (bool apply_to_selection)
988 if (apply_to_selection) {
989 _editor.get_selection().tracks.foreach_midi_time_axis (
990 boost::bind (&MidiTimeAxisView::show_all_automation, _1, false));
993 const set<Evoral::Parameter> params = midi_track()->midi_playlist()->contained_automation();
995 for (set<Evoral::Parameter>::const_iterator i = params.begin(); i != params.end(); ++i) {
996 create_automation_child(*i, true);
1000 RouteTimeAxisView::show_all_automation ();
1005 MidiTimeAxisView::show_existing_automation (bool apply_to_selection)
1007 if (apply_to_selection) {
1008 _editor.get_selection().tracks.foreach_midi_time_axis (
1009 boost::bind (&MidiTimeAxisView::show_existing_automation, _1, false));
1012 const set<Evoral::Parameter> params = midi_track()->midi_playlist()->contained_automation();
1014 for (set<Evoral::Parameter>::const_iterator i = params.begin(); i != params.end(); ++i) {
1015 create_automation_child (*i, true);
1019 RouteTimeAxisView::show_existing_automation ();
1023 /** Create an automation track for the given parameter (pitch bend, channel pressure).
1026 MidiTimeAxisView::create_automation_child (const Evoral::Parameter& param, bool show)
1028 if (param.type() == NullAutomation) {
1032 AutomationTracks::iterator existing = _automation_tracks.find (param);
1034 if (existing != _automation_tracks.end()) {
1036 /* automation track created because we had existing data for
1037 * the processor, but visibility may need to be controlled
1038 * since it will have been set visible by default.
1041 if (existing->second->set_marked_for_display (show) && !no_redraw) {
1048 boost::shared_ptr<AutomationTimeAxisView> track;
1050 switch (param.type()) {
1052 case GainAutomation:
1053 create_gain_automation_child (param, show);
1056 case PluginAutomation:
1057 /* handled elsewhere */
1060 case MidiCCAutomation:
1061 case MidiPgmChangeAutomation:
1062 case MidiPitchBenderAutomation:
1063 case MidiChannelPressureAutomation:
1064 case MidiSystemExclusiveAutomation:
1065 /* These controllers are region "automation" - they are owned
1066 * by regions (and their MidiModels), not by the track. As a
1067 * result we do not create an AutomationList/Line for the track
1068 * ... except here we are doing something!! XXX
1071 track.reset (new AutomationTimeAxisView (
1074 boost::shared_ptr<Automatable> (),
1075 boost::shared_ptr<AutomationControl> (),
1081 _route->describe_parameter(param)));
1084 _view->foreach_regionview (
1085 sigc::mem_fun (*track.get(), &TimeAxisView::add_ghost));
1088 add_automation_child (param, track, show);
1092 error << "MidiTimeAxisView: unknown automation child "
1093 << EventTypeMap::instance().to_symbol(param) << endmsg;
1098 MidiTimeAxisView::route_active_changed ()
1100 RouteUI::route_active_changed ();
1103 if (_route->active()) {
1104 controls_ebox.set_name ("MidiTrackControlsBaseUnselected");
1105 controls_base_selected_name = "MidiTrackControlsBaseSelected";
1106 controls_base_unselected_name = "MidiTrackControlsBaseUnselected";
1108 controls_ebox.set_name ("MidiTrackControlsBaseInactiveUnselected");
1109 controls_base_selected_name = "MidiTrackControlsBaseInactiveSelected";
1110 controls_base_unselected_name = "MidiTrackControlsBaseInactiveUnselected";
1113 if (_route->active()) {
1114 controls_ebox.set_name ("BusControlsBaseUnselected");
1115 controls_base_selected_name = "BusControlsBaseSelected";
1116 controls_base_unselected_name = "BusControlsBaseUnselected";
1118 controls_ebox.set_name ("BusControlsBaseInactiveUnselected");
1119 controls_base_selected_name = "BusControlsBaseInactiveSelected";
1120 controls_base_unselected_name = "BusControlsBaseInactiveUnselected";
1126 MidiTimeAxisView::set_note_selection (uint8_t note)
1128 if (!_editor.internal_editing()) {
1132 uint16_t chn_mask = _channel_selector.get_selected_channels();
1134 if (_view->num_selected_regionviews() == 0) {
1135 _view->foreach_regionview (
1136 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_selection_region_view),
1139 _view->foreach_selected_regionview (
1140 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_selection_region_view),
1146 MidiTimeAxisView::add_note_selection (uint8_t note)
1148 if (!_editor.internal_editing()) {
1152 const uint16_t chn_mask = _channel_selector.get_selected_channels();
1154 if (_view->num_selected_regionviews() == 0) {
1155 _view->foreach_regionview (
1156 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection_region_view),
1159 _view->foreach_selected_regionview (
1160 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection_region_view),
1166 MidiTimeAxisView::extend_note_selection (uint8_t note)
1168 if (!_editor.internal_editing()) {
1172 const uint16_t chn_mask = _channel_selector.get_selected_channels();
1174 if (_view->num_selected_regionviews() == 0) {
1175 _view->foreach_regionview (
1176 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::extend_note_selection_region_view),
1179 _view->foreach_selected_regionview (
1180 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::extend_note_selection_region_view),
1186 MidiTimeAxisView::toggle_note_selection (uint8_t note)
1188 if (!_editor.internal_editing()) {
1192 const uint16_t chn_mask = _channel_selector.get_selected_channels();
1194 if (_view->num_selected_regionviews() == 0) {
1195 _view->foreach_regionview (
1196 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection_region_view),
1199 _view->foreach_selected_regionview (
1200 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection_region_view),
1206 MidiTimeAxisView::set_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1208 dynamic_cast<MidiRegionView*>(rv)->select_matching_notes (note, chn_mask, false, false);
1212 MidiTimeAxisView::add_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1214 dynamic_cast<MidiRegionView*>(rv)->select_matching_notes (note, chn_mask, true, false);
1218 MidiTimeAxisView::extend_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1220 dynamic_cast<MidiRegionView*>(rv)->select_matching_notes (note, chn_mask, true, true);
1224 MidiTimeAxisView::toggle_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1226 dynamic_cast<MidiRegionView*>(rv)->toggle_matching_notes (note, chn_mask);
1230 MidiTimeAxisView::set_channel_mode (ChannelMode, uint16_t)
1232 /* hide all automation tracks that use the wrong channel(s) and show all those that use
1236 const uint16_t selected_channels = _channel_selector.get_selected_channels();
1237 bool changed = false;
1241 for (uint32_t ctl = 0; ctl < 127; ++ctl) {
1243 for (uint32_t chn = 0; chn < 16; ++chn) {
1244 Evoral::Parameter fully_qualified_param (MidiCCAutomation, chn, ctl);
1245 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
1251 if ((selected_channels & (0x0001 << chn)) == 0) {
1252 /* channel not in use. hiding it will trigger RouteTimeAxisView::automation_track_hidden()
1253 which will cause a redraw. We don't want one per channel, so block that with no_redraw.
1255 changed = track->set_marked_for_display (false) || changed;
1257 changed = track->set_marked_for_display (true) || changed;
1264 /* TODO: Bender, Pressure */
1266 /* invalidate the controller menu, so that we rebuild it next time */
1267 _controller_menu_map.clear ();
1268 delete controller_menu;
1269 controller_menu = 0;
1277 MidiTimeAxisView::automation_child_menu_item (Evoral::Parameter param)
1279 Gtk::CheckMenuItem* m = RouteTimeAxisView::automation_child_menu_item (param);
1284 ParameterMenuMap::iterator i = _controller_menu_map.find (param);
1285 if (i != _controller_menu_map.end()) {
1289 i = _channel_command_menu_map.find (param);
1290 if (i != _channel_command_menu_map.end()) {
1297 boost::shared_ptr<MidiRegion>
1298 MidiTimeAxisView::add_region (framepos_t pos, framecnt_t length, bool commit)
1300 Editor* real_editor = dynamic_cast<Editor*> (&_editor);
1302 real_editor->begin_reversible_command (Operations::create_region);
1303 playlist()->clear_changes ();
1305 real_editor->snap_to (pos, 0);
1307 boost::shared_ptr<Source> src = _session->create_midi_source_for_session (
1308 view()->trackview().track().get(), view()->trackview().track()->name());
1311 plist.add (ARDOUR::Properties::start, 0);
1312 plist.add (ARDOUR::Properties::length, length);
1313 plist.add (ARDOUR::Properties::name, PBD::basename_nosuffix(src->name()));
1315 boost::shared_ptr<Region> region = (RegionFactory::create (src, plist));
1317 playlist()->add_region (region, pos);
1318 _session->add_command (new StatefulDiffCommand (playlist()));
1321 real_editor->commit_reversible_command ();
1324 return boost::dynamic_pointer_cast<MidiRegion>(region);
1328 MidiTimeAxisView::ensure_step_editor ()
1330 if (!_step_editor) {
1331 _step_editor = new StepEditor (_editor, midi_track(), *this);
1336 MidiTimeAxisView::start_step_editing ()
1338 ensure_step_editor ();
1339 _step_editor->start_step_editing ();
1343 MidiTimeAxisView::stop_step_editing ()
1346 _step_editor->stop_step_editing ();
1351 /** @return channel (counted from 0) to add an event to, based on the current setting
1352 * of the channel selector.
1355 MidiTimeAxisView::get_channel_for_add () const
1357 uint16_t const chn_mask = _channel_selector.get_selected_channels ();
1359 uint8_t channel = 0;
1361 /* pick the highest selected channel, unless all channels are selected,
1362 which is interpreted to mean channel 1 (zero)
1365 for (uint16_t i = 0; i < 16; ++i) {
1366 if (chn_mask & (1<<i)) {
1372 if (chn_cnt == 16) {
1380 MidiTimeAxisView::note_range_changed ()
1382 set_gui_property ("note-range-min", (int) midi_view()->lowest_note ());
1383 set_gui_property ("note-range-max", (int) midi_view()->highest_note ());
1387 MidiTimeAxisView::contents_height_changed ()
1389 _range_scroomer->set_size_request (-1, _view->child_height ());