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);
133 /* This next call will result in our height being set up, so it must come after
134 the creation of the piano roll / range scroomer as their visibility is set up
137 RouteTimeAxisView::set_route (rt);
139 _view->apply_color (_color, StreamView::RegionColor);
141 subplugin_menu.set_name ("ArdourContextMenu");
143 if (!gui_property ("note-range-min").empty ()) {
144 midi_view()->apply_note_range (atoi (gui_property ("note-range-min").c_str()), atoi (gui_property ("note-range-max").c_str()), true);
146 midi_view()->NoteRangeChanged.connect (sigc::mem_fun (*this, &MidiTimeAxisView::note_range_changed));
147 _view->ContentsHeightChanged.connect (sigc::mem_fun (*this, &MidiTimeAxisView::contents_height_changed));
149 ignore_toggle = false;
151 if (is_midi_track()) {
152 controls_ebox.set_name ("MidiTimeAxisViewControlsBaseUnselected");
153 _note_mode = midi_track()->note_mode();
154 } else { // MIDI bus (which doesn't exist yet..)
155 controls_ebox.set_name ("MidiBusControlsBaseUnselected");
158 /* map current state of the route */
160 processors_changed (RouteProcessorChange ());
162 _route->processors_changed.connect (*this, invalidator (*this), boost::bind (&MidiTimeAxisView::processors_changed, this, _1), gui_context());
165 _piano_roll_header->SetNoteSelection.connect (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_selection));
166 _piano_roll_header->AddNoteSelection.connect (sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection));
167 _piano_roll_header->ExtendNoteSelection.connect (sigc::mem_fun (*this, &MidiTimeAxisView::extend_note_selection));
168 _piano_roll_header->ToggleNoteSelection.connect (sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection));
170 /* Suspend updates of the StreamView during scroomer drags to speed things up */
171 _range_scroomer->DragStarting.connect (sigc::mem_fun (*midi_view(), &MidiStreamView::suspend_updates));
172 _range_scroomer->DragFinishing.connect (sigc::mem_fun (*midi_view(), &MidiStreamView::resume_updates));
174 /* Put the scroomer and the keyboard in a VBox with a padding
175 label so that they can be reduced in height for stacked-view
178 VBox* v = manage (new VBox);
179 HBox* h = manage (new HBox);
180 h->pack_start (*_range_scroomer);
181 h->pack_start (*_piano_roll_header);
182 v->pack_start (*h, false, false);
183 v->pack_start (*manage (new Label ("")), true, true);
186 controls_hbox.pack_start(*v);
188 controls_ebox.set_name ("MidiTrackControlsBaseUnselected");
189 controls_base_selected_name = "MidiTrackControlsBaseSelected";
190 controls_base_unselected_name = "MidiTrackControlsBaseUnselected";
192 midi_view()->NoteRangeChanged.connect (sigc::mem_fun(*this, &MidiTimeAxisView::update_range));
194 /* ask for notifications of any new RegionViews */
195 _view->RegionViewAdded.connect (sigc::mem_fun(*this, &MidiTimeAxisView::region_view_added));
197 if (!_editor.have_idled()) {
198 /* first idle will do what we need */
205 MIDI::Name::MidiPatchManager& patch_manager = MIDI::Name::MidiPatchManager::instance();
207 MIDI::Name::MasterDeviceNames::Models::const_iterator m = patch_manager.all_models().begin();
208 for (; m != patch_manager.all_models().end(); ++m) {
209 _model_selector.append_text(m->c_str());
213 _model_selector.signal_changed().connect(sigc::mem_fun(*this, &MidiTimeAxisView::model_changed));
215 _custom_device_mode_selector.signal_changed().connect(
216 sigc::mem_fun(*this, &MidiTimeAxisView::custom_device_mode_changed));
218 _model_selector.set_active_text (gui_property (X_("midnam-model-name")));
219 _custom_device_mode_selector.set_active_text (gui_property (X_("midnam-custom-device-mode")));
221 ARDOUR_UI::instance()->set_tip (_model_selector, _("External MIDI Device"));
222 ARDOUR_UI::instance()->set_tip (_custom_device_mode_selector, _("External Device Mode"));
224 _midi_controls_box.set_homogeneous(false);
225 _midi_controls_box.set_border_width (10);
226 if (!patch_manager.all_models().empty()) {
227 _channel_selector.set_border_width(2);
228 _midi_controls_box.resize(3, 2);
229 _midi_controls_box.attach(_channel_selector, 0, 2, 0, 1);
231 _midi_controls_box.attach(*manage(new HSeparator()), 0, 2, 1, 2);
233 _model_selector.set_size_request(22, 30);
234 _model_selector.set_border_width(2);
235 _midi_controls_box.attach(_model_selector, 0, 1, 2, 3);
237 _custom_device_mode_selector.set_size_request(10, 30);
238 _custom_device_mode_selector.set_border_width(2);
239 _midi_controls_box.attach(_custom_device_mode_selector, 1, 2, 2, 3);
241 _midi_controls_box.attach(_channel_selector, 0, 1, 0, 1);
244 controls_vbox.pack_start(_midi_controls_box, false, false);
246 // restore channel selector settings
247 _channel_selector.set_channel_mode(midi_track()->get_channel_mode(), midi_track()->get_channel_mask());
248 _channel_selector.mode_changed.connect(
249 sigc::mem_fun(*midi_track(), &MidiTrack::set_channel_mode));
250 _channel_selector.mode_changed.connect(
251 sigc::mem_fun(*this, &MidiTimeAxisView::set_channel_mode));
253 string prop = gui_property ("color-mode");
255 _color_mode = ColorMode (string_2_enum(prop, _color_mode));
256 if (_color_mode == ChannelColors) {
257 _channel_selector.set_channel_colors(CanvasNoteEvent::midi_channel_colors);
261 set_color_mode (_color_mode, true, false);
263 prop = gui_property ("note-mode");
265 _note_mode = NoteMode (string_2_enum (prop, _note_mode));
266 if (_percussion_mode_item) {
267 _percussion_mode_item->set_active (_note_mode == Percussive);
271 /* Look for any GUI object state nodes that represent automation children that should exist, and create
275 list<string> gui_ids = gui_object_state().all_ids ();
276 for (list<string>::const_iterator i = gui_ids.begin(); i != gui_ids.end(); ++i) {
279 Evoral::Parameter parameter (0, 0, 0);
281 bool const p = AutomationTimeAxisView::parse_state_id (*i, route_id, has_parameter, parameter);
282 if (p && route_id == _route->id () && has_parameter) {
283 create_automation_child (parameter, string_is_affirmative (gui_object_state().get_string (*i, X_("visible"))));
289 MidiTimeAxisView::first_idle ()
296 MidiTimeAxisView::~MidiTimeAxisView ()
298 delete _piano_roll_header;
299 _piano_roll_header = 0;
301 delete _range_scroomer;
304 delete controller_menu;
309 MidiTimeAxisView::enter_internal_edit_mode ()
312 midi_view()->enter_internal_edit_mode ();
317 MidiTimeAxisView::leave_internal_edit_mode ()
320 midi_view()->leave_internal_edit_mode ();
325 MidiTimeAxisView::check_step_edit ()
327 ensure_step_editor ();
328 _step_editor->check_step_edit ();
332 MidiTimeAxisView::model_changed()
334 std::list<std::string> device_modes = MIDI::Name::MidiPatchManager::instance()
335 .custom_device_mode_names_by_model(_model_selector.get_active_text());
337 _custom_device_mode_selector.clear_items();
339 for (std::list<std::string>::const_iterator i = device_modes.begin();
340 i != device_modes.end(); ++i) {
341 _custom_device_mode_selector.append_text(*i);
344 _custom_device_mode_selector.set_active(0);
346 _route->instrument_info().set_external_instrument (_model_selector.get_active_text(), _custom_device_mode_selector.get_active_text());
350 MidiTimeAxisView::custom_device_mode_changed()
352 _route->instrument_info().set_external_instrument (_model_selector.get_active_text(), _custom_device_mode_selector.get_active_text());
356 MidiTimeAxisView::midi_view()
358 return dynamic_cast<MidiStreamView*>(_view);
362 MidiTimeAxisView::set_height (uint32_t h)
364 if (h >= MIDI_CONTROLS_BOX_MIN_HEIGHT) {
365 _midi_controls_box.show_all ();
367 _midi_controls_box.hide();
370 if (h >= KEYBOARD_MIN_HEIGHT) {
371 if (is_track() && _range_scroomer) {
372 _range_scroomer->show();
374 if (is_track() && _piano_roll_header) {
375 _piano_roll_header->show();
378 if (is_track() && _range_scroomer) {
379 _range_scroomer->hide();
381 if (is_track() && _piano_roll_header) {
382 _piano_roll_header->hide();
386 /* We need to do this after changing visibility of our stuff, as it will
387 eventually trigger a call to Editor::reset_controls_layout_width(),
388 which needs to know if we have just shown or hidden a scroomer /
391 RouteTimeAxisView::set_height (h);
395 MidiTimeAxisView::append_extra_display_menu_items ()
397 using namespace Menu_Helpers;
399 MenuList& items = display_menu->items();
402 Menu *range_menu = manage(new Menu);
403 MenuList& range_items = range_menu->items();
404 range_menu->set_name ("ArdourContextMenu");
406 range_items.push_back (MenuElem (_("Show Full Range"), sigc::bind (
407 sigc::mem_fun(*this, &MidiTimeAxisView::set_note_range),
408 MidiStreamView::FullRange, true)));
410 range_items.push_back (MenuElem (_("Fit Contents"), sigc::bind (
411 sigc::mem_fun(*this, &MidiTimeAxisView::set_note_range),
412 MidiStreamView::ContentsRange, true)));
414 items.push_back (MenuElem (_("Note Range"), *range_menu));
415 items.push_back (MenuElem (_("Note Mode"), *build_note_mode_menu()));
417 items.push_back (SeparatorElem ());
421 MidiTimeAxisView::build_automation_action_menu (bool for_selection)
423 using namespace Menu_Helpers;
425 /* If we have a controller menu, we need to detach it before
426 RouteTimeAxis::build_automation_action_menu destroys the
427 menu it is attached to. Otherwise GTK destroys
428 controller_menu's gobj, meaning that it can't be reattached
429 below. See bug #3134.
432 if (controller_menu) {
433 detach_menu (*controller_menu);
436 _channel_command_menu_map.clear ();
437 RouteTimeAxisView::build_automation_action_menu (for_selection);
439 MenuList& automation_items = automation_action_menu->items();
441 uint16_t selected_channels = _channel_selector.get_selected_channels();
443 if (selected_channels != 0) {
445 automation_items.push_back (SeparatorElem());
447 /* these 2 MIDI "command" types are semantically more like automation than note data,
448 but they are not MIDI controllers. We give them special status in this menu, since
449 they will not show up in the controller list and anyone who actually knows
450 something about MIDI (!) would not expect to find them there.
453 add_channel_command_menu_item (automation_items, _("Bender"), MidiPitchBenderAutomation, 0);
454 automation_items.back().set_sensitive (!for_selection || _editor.get_selection().tracks.size() == 1);
455 add_channel_command_menu_item (automation_items, _("Pressure"), MidiChannelPressureAutomation, 0);
456 automation_items.back().set_sensitive (!for_selection || _editor.get_selection().tracks.size() == 1);
458 /* now all MIDI controllers. Always offer the possibility that we will rebuild the controllers menu
459 since it might need to be updated after a channel mode change or other change. Also detach it
460 first in case it has been used anywhere else.
463 build_controller_menu ();
465 automation_items.push_back (SeparatorElem());
466 automation_items.push_back (MenuElem (_("Controllers"), *controller_menu));
467 automation_items.back().set_sensitive (!for_selection || _editor.get_selection().tracks.size() == 1);
469 automation_items.push_back (MenuElem (string_compose ("<i>%1</i>", _("No MIDI Channels selected"))));
470 dynamic_cast<Label*> (automation_items.back().get_child())->set_use_markup (true);
476 MidiTimeAxisView::change_all_channel_tracks_visibility (bool yn, Evoral::Parameter param)
478 uint16_t selected_channels = _channel_selector.get_selected_channels();
480 for (uint8_t chn = 0; chn < 16; chn++) {
481 if (selected_channels & (0x0001 << chn)) {
483 Evoral::Parameter fully_qualified_param (param.type(), chn, param.id());
484 Gtk::CheckMenuItem* menu = automation_child_menu_item (fully_qualified_param);
487 menu->set_active (yn);
494 MidiTimeAxisView::add_channel_command_menu_item (Menu_Helpers::MenuList& items, const string& label, AutomationType auto_type, uint8_t cmd)
496 using namespace Menu_Helpers;
498 /* count the number of selected channels because we will build a different menu structure if there is more than 1 selected.
501 uint16_t selected_channels = _channel_selector.get_selected_channels();
504 for (uint8_t chn = 0; chn < 16; chn++) {
505 if (selected_channels & (0x0001 << chn)) {
514 /* multiple channels - create a submenu, with 1 item per channel */
516 Menu* chn_menu = manage (new Menu);
517 MenuList& chn_items (chn_menu->items());
518 Evoral::Parameter param_without_channel (auto_type, 0, cmd);
520 /* add a couple of items to hide/show all of them */
522 chn_items.push_back (MenuElem (_("Hide all channels"),
523 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
524 false, param_without_channel)));
525 chn_items.push_back (MenuElem (_("Show all channels"),
526 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
527 true, param_without_channel)));
529 for (uint8_t chn = 0; chn < 16; chn++) {
530 if (selected_channels & (0x0001 << chn)) {
532 /* for each selected channel, add a menu item for this controller */
534 Evoral::Parameter fully_qualified_param (auto_type, chn, cmd);
535 chn_items.push_back (CheckMenuElem (string_compose (_("Channel %1"), chn+1),
536 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
537 fully_qualified_param)));
539 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
540 bool visible = false;
543 if (track->marked_for_display()) {
548 CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&chn_items.back());
549 _channel_command_menu_map[fully_qualified_param] = cmi;
550 cmi->set_active (visible);
554 /* now create an item in the parent menu that has the per-channel list as a submenu */
556 items.push_back (MenuElem (label, *chn_menu));
560 /* just one channel - create a single menu item for this command+channel combination*/
562 for (uint8_t chn = 0; chn < 16; chn++) {
563 if (selected_channels & (0x0001 << chn)) {
565 Evoral::Parameter fully_qualified_param (auto_type, chn, cmd);
566 items.push_back (CheckMenuElem (label,
567 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
568 fully_qualified_param)));
570 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
571 bool visible = false;
574 if (track->marked_for_display()) {
579 CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&items.back());
580 _channel_command_menu_map[fully_qualified_param] = cmi;
581 cmi->set_active (visible);
583 /* one channel only */
591 MidiTimeAxisView::build_controller_menu ()
593 using namespace Menu_Helpers;
595 if (controller_menu) {
596 /* it exists and has not been invalidated by a channel mode change, so just return it */
600 controller_menu = new Menu; // explicitly managed by us
601 MenuList& items (controller_menu->items());
603 /* create several "top level" menu items for sets of controllers (16 at a time), and populate each one with a submenu
604 for each controller+channel combination covering the currently selected channels for this track
607 uint16_t selected_channels = _channel_selector.get_selected_channels();
609 /* count the number of selected channels because we will build a different menu structure if there is more than 1 selected.
614 for (uint8_t chn = 0; chn < 16; chn++) {
615 if (selected_channels & (0x0001 << chn)) {
622 /* loop over all 127 MIDI controllers, in groups of 16; except don't offer
623 bank select controllers, as they are handled by the `patch' code */
625 for (int i = 0; i < 127; i += 16) {
627 Menu* ctl_menu = manage (new Menu);
628 MenuList& ctl_items (ctl_menu->items());
631 /* for each controller, consider whether to create a submenu or a single item */
633 for (int ctl = i; ctl < i+16; ++ctl) {
635 if (ctl == MIDI_CTL_MSB_BANK || ctl == MIDI_CTL_LSB_BANK) {
641 /* multiple channels - create a submenu, with 1 item per channel */
643 Menu* chn_menu = manage (new Menu);
644 MenuList& chn_items (chn_menu->items());
646 /* add a couple of items to hide/show this controller on all channels */
648 Evoral::Parameter param_without_channel (MidiCCAutomation, 0, ctl);
649 chn_items.push_back (MenuElem (_("Hide all channels"),
650 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
651 false, param_without_channel)));
652 chn_items.push_back (MenuElem (_("Show all channels"),
653 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
654 true, param_without_channel)));
656 for (uint8_t chn = 0; chn < 16; chn++) {
657 if (selected_channels & (0x0001 << chn)) {
659 /* for each selected channel, add a menu item for this controller */
661 Evoral::Parameter fully_qualified_param (MidiCCAutomation, chn, ctl);
662 chn_items.push_back (CheckMenuElem (string_compose (_("Channel %1"), chn+1),
663 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
664 fully_qualified_param)));
666 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
667 bool visible = false;
670 if (track->marked_for_display()) {
675 CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&chn_items.back());
676 _controller_menu_map[fully_qualified_param] = cmi;
677 cmi->set_active (visible);
681 /* add the per-channel menu to the list of controllers, with the name of the controller */
682 ctl_items.push_back (MenuElem (string_compose ("<b>%1</b>: %2", ctl, midi_name (ctl)), *chn_menu));
683 dynamic_cast<Label*> (ctl_items.back().get_child())->set_use_markup (true);
687 /* just one channel - create a single menu item for this ctl+channel combination*/
689 for (uint8_t chn = 0; chn < 16; chn++) {
690 if (selected_channels & (0x0001 << chn)) {
692 Evoral::Parameter fully_qualified_param (MidiCCAutomation, chn, ctl);
693 ctl_items.push_back (
695 string_compose ("<b>%1</b>: %2 [%3]", ctl, midi_name (ctl), int (chn)),
696 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
697 fully_qualified_param)
700 dynamic_cast<Label*> (ctl_items.back().get_child())->set_use_markup (true);
702 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
703 bool visible = false;
706 if (track->marked_for_display()) {
711 CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&ctl_items.back());
712 _controller_menu_map[fully_qualified_param] = cmi;
713 cmi->set_active (visible);
715 /* one channel only */
722 /* add the menu for this block of controllers to the overall controller menu */
724 items.push_back (MenuElem (string_compose (_("Controllers %1-%2"), i, i+15), *ctl_menu));
729 MidiTimeAxisView::build_note_mode_menu()
731 using namespace Menu_Helpers;
733 Menu* mode_menu = manage (new Menu);
734 MenuList& items = mode_menu->items();
735 mode_menu->set_name ("ArdourContextMenu");
737 RadioMenuItem::Group mode_group;
738 items.push_back (RadioMenuElem (mode_group,_("Sustained"),
739 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_mode), Sustained, true)));
740 _note_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
741 _note_mode_item->set_active(_note_mode == Sustained);
743 items.push_back (RadioMenuElem (mode_group, _("Percussive"),
744 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_mode), Percussive, true)));
745 _percussion_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
746 _percussion_mode_item->set_active(_note_mode == Percussive);
752 MidiTimeAxisView::build_color_mode_menu()
754 using namespace Menu_Helpers;
756 Menu* mode_menu = manage (new Menu);
757 MenuList& items = mode_menu->items();
758 mode_menu->set_name ("ArdourContextMenu");
760 RadioMenuItem::Group mode_group;
761 items.push_back (RadioMenuElem (mode_group, _("Meter Colors"),
762 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode),
763 MeterColors, false, true, true)));
764 _meter_color_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
765 _meter_color_mode_item->set_active(_color_mode == MeterColors);
767 items.push_back (RadioMenuElem (mode_group, _("Channel Colors"),
768 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode),
769 ChannelColors, false, true, true)));
770 _channel_color_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
771 _channel_color_mode_item->set_active(_color_mode == ChannelColors);
773 items.push_back (RadioMenuElem (mode_group, _("Track Color"),
774 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode),
775 TrackColor, false, true, true)));
776 _channel_color_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
777 _channel_color_mode_item->set_active(_color_mode == TrackColor);
783 MidiTimeAxisView::set_note_mode(NoteMode mode, bool apply_to_selection)
785 if (apply_to_selection) {
786 _editor.get_selection().tracks.foreach_midi_time_axis (boost::bind (&MidiTimeAxisView::set_note_mode, _1, mode, false));
788 if (_note_mode != mode || midi_track()->note_mode() != mode) {
790 midi_track()->set_note_mode(mode);
791 set_gui_property ("note-mode", enum_2_string(_note_mode));
792 _view->redisplay_track();
798 MidiTimeAxisView::set_color_mode (ColorMode mode, bool force, bool redisplay, bool apply_to_selection)
800 if (apply_to_selection) {
801 _editor.get_selection().tracks.foreach_midi_time_axis (
802 boost::bind (&MidiTimeAxisView::set_color_mode, _1, mode, force, redisplay, false)
806 if (_color_mode == mode && !force) {
810 if (mode == ChannelColors) {
811 _channel_selector.set_channel_colors(CanvasNoteEvent::midi_channel_colors);
813 _channel_selector.set_default_channel_color();
817 set_gui_property ("color-mode", enum_2_string(_color_mode));
819 _view->redisplay_track();
825 MidiTimeAxisView::set_note_range (MidiStreamView::VisibleNoteRange range, bool apply_to_selection)
827 if (apply_to_selection) {
828 _editor.get_selection().tracks.foreach_midi_time_axis (
829 boost::bind (&MidiTimeAxisView::set_note_range, _1, range, false)
832 if (!_ignore_signals) {
833 midi_view()->set_note_range(range);
839 MidiTimeAxisView::update_range()
841 MidiGhostRegion* mgr;
843 for(list<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
844 if ((mgr = dynamic_cast<MidiGhostRegion*>(*i)) != 0) {
851 MidiTimeAxisView::show_all_automation (bool apply_to_selection)
853 if (apply_to_selection) {
854 _editor.get_selection().tracks.foreach_midi_time_axis (boost::bind (&MidiTimeAxisView::show_all_automation, _1, false));
857 const set<Evoral::Parameter> params = midi_track()->midi_playlist()->contained_automation();
859 for (set<Evoral::Parameter>::const_iterator i = params.begin(); i != params.end(); ++i) {
860 create_automation_child(*i, true);
864 RouteTimeAxisView::show_all_automation ();
869 MidiTimeAxisView::show_existing_automation (bool apply_to_selection)
871 if (apply_to_selection) {
872 _editor.get_selection().tracks.foreach_midi_time_axis (boost::bind (&MidiTimeAxisView::show_existing_automation, _1, false));
875 const set<Evoral::Parameter> params = midi_track()->midi_playlist()->contained_automation();
877 for (set<Evoral::Parameter>::const_iterator i = params.begin(); i != params.end(); ++i) {
878 create_automation_child (*i, true);
882 RouteTimeAxisView::show_existing_automation ();
886 /** Create an automation track for the given parameter (pitch bend, channel pressure).
889 MidiTimeAxisView::create_automation_child (const Evoral::Parameter& param, bool show)
891 if (param.type() == NullAutomation) {
895 AutomationTracks::iterator existing = _automation_tracks.find (param);
897 if (existing != _automation_tracks.end()) {
899 /* automation track created because we had existing data for
900 * the processor, but visibility may need to be controlled
901 * since it will have been set visible by default.
904 if (existing->second->set_marked_for_display (show) && !no_redraw) {
911 boost::shared_ptr<AutomationTimeAxisView> track;
913 switch (param.type()) {
916 create_gain_automation_child (param, show);
919 case PluginAutomation:
920 /* handled elsewhere */
923 case MidiCCAutomation:
924 case MidiPgmChangeAutomation:
925 case MidiPitchBenderAutomation:
926 case MidiChannelPressureAutomation:
927 case MidiSystemExclusiveAutomation:
928 /* These controllers are region "automation" - they are owned
929 * by regions (and their MidiModels), not by the track. As a
930 * result we do not create an AutomationList/Line for the track
931 * ... except here we are doing something!! XXX
934 track.reset (new AutomationTimeAxisView (
937 boost::shared_ptr<Automatable> (),
938 boost::shared_ptr<AutomationControl> (),
944 _route->describe_parameter(param)
948 _view->foreach_regionview (sigc::mem_fun (*track.get(), &TimeAxisView::add_ghost));
951 add_automation_child (param, track, show);
955 error << "MidiTimeAxisView: unknown automation child " << EventTypeMap::instance().to_symbol(param) << endmsg;
960 MidiTimeAxisView::route_active_changed ()
962 RouteUI::route_active_changed ();
965 if (_route->active()) {
966 controls_ebox.set_name ("MidiTrackControlsBaseUnselected");
967 controls_base_selected_name = "MidiTrackControlsBaseSelected";
968 controls_base_unselected_name = "MidiTrackControlsBaseUnselected";
970 controls_ebox.set_name ("MidiTrackControlsBaseInactiveUnselected");
971 controls_base_selected_name = "MidiTrackControlsBaseInactiveSelected";
972 controls_base_unselected_name = "MidiTrackControlsBaseInactiveUnselected";
978 if (_route->active()) {
979 controls_ebox.set_name ("BusControlsBaseUnselected");
980 controls_base_selected_name = "BusControlsBaseSelected";
981 controls_base_unselected_name = "BusControlsBaseUnselected";
983 controls_ebox.set_name ("BusControlsBaseInactiveUnselected");
984 controls_base_selected_name = "BusControlsBaseInactiveSelected";
985 controls_base_unselected_name = "BusControlsBaseInactiveUnselected";
991 MidiTimeAxisView::set_note_selection (uint8_t note)
993 if (!_editor.internal_editing()) {
997 uint16_t chn_mask = _channel_selector.get_selected_channels();
999 if (_view->num_selected_regionviews() == 0) {
1000 _view->foreach_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_selection_region_view), note, chn_mask));
1002 _view->foreach_selected_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_selection_region_view), note, chn_mask));
1007 MidiTimeAxisView::add_note_selection (uint8_t note)
1009 if (!_editor.internal_editing()) {
1013 uint16_t chn_mask = _channel_selector.get_selected_channels();
1015 if (_view->num_selected_regionviews() == 0) {
1016 _view->foreach_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection_region_view), note, chn_mask));
1018 _view->foreach_selected_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection_region_view), note, chn_mask));
1023 MidiTimeAxisView::extend_note_selection (uint8_t note)
1025 if (!_editor.internal_editing()) {
1029 uint16_t chn_mask = _channel_selector.get_selected_channels();
1031 if (_view->num_selected_regionviews() == 0) {
1032 _view->foreach_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::extend_note_selection_region_view), note, chn_mask));
1034 _view->foreach_selected_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::extend_note_selection_region_view), note, chn_mask));
1039 MidiTimeAxisView::toggle_note_selection (uint8_t note)
1041 if (!_editor.internal_editing()) {
1045 uint16_t chn_mask = _channel_selector.get_selected_channels();
1047 if (_view->num_selected_regionviews() == 0) {
1048 _view->foreach_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection_region_view), note, chn_mask));
1050 _view->foreach_selected_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection_region_view), note, chn_mask));
1055 MidiTimeAxisView::set_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1057 dynamic_cast<MidiRegionView*>(rv)->select_matching_notes (note, chn_mask, false, false);
1061 MidiTimeAxisView::add_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1063 dynamic_cast<MidiRegionView*>(rv)->select_matching_notes (note, chn_mask, true, false);
1067 MidiTimeAxisView::extend_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1069 dynamic_cast<MidiRegionView*>(rv)->select_matching_notes (note, chn_mask, true, true);
1073 MidiTimeAxisView::toggle_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1075 dynamic_cast<MidiRegionView*>(rv)->toggle_matching_notes (note, chn_mask);
1079 MidiTimeAxisView::set_channel_mode (ChannelMode, uint16_t)
1081 /* hide all automation tracks that use the wrong channel(s) and show all those that use
1085 uint16_t selected_channels = _channel_selector.get_selected_channels();
1086 bool changed = false;
1090 for (uint32_t ctl = 0; ctl < 127; ++ctl) {
1092 for (uint32_t chn = 0; chn < 16; ++chn) {
1093 Evoral::Parameter fully_qualified_param (MidiCCAutomation, chn, ctl);
1094 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
1100 if ((selected_channels & (0x0001 << chn)) == 0) {
1101 /* channel not in use. hiding it will trigger RouteTimeAxisView::automation_track_hidden()
1102 which will cause a redraw. We don't want one per channel, so block that with no_redraw.
1104 changed = track->set_marked_for_display (false) || changed;
1106 changed = track->set_marked_for_display (true) || changed;
1113 /* TODO: Bender, Pressure */
1115 /* invalidate the controller menu, so that we rebuild it next time */
1116 _controller_menu_map.clear ();
1117 delete controller_menu;
1118 controller_menu = 0;
1126 MidiTimeAxisView::automation_child_menu_item (Evoral::Parameter param)
1128 Gtk::CheckMenuItem* m = RouteTimeAxisView::automation_child_menu_item (param);
1133 ParameterMenuMap::iterator i = _controller_menu_map.find (param);
1134 if (i != _controller_menu_map.end()) {
1138 i = _channel_command_menu_map.find (param);
1139 if (i != _channel_command_menu_map.end()) {
1146 boost::shared_ptr<MidiRegion>
1147 MidiTimeAxisView::add_region (framepos_t pos, framecnt_t length, bool commit)
1149 Editor* real_editor = dynamic_cast<Editor*> (&_editor);
1151 real_editor->begin_reversible_command (Operations::create_region);
1152 playlist()->clear_changes ();
1154 real_editor->snap_to (pos, 0);
1156 boost::shared_ptr<Source> src = _session->create_midi_source_for_session (view()->trackview().track().get(),
1157 view()->trackview().track()->name());
1160 plist.add (ARDOUR::Properties::start, 0);
1161 plist.add (ARDOUR::Properties::length, length);
1162 plist.add (ARDOUR::Properties::name, PBD::basename_nosuffix(src->name()));
1164 boost::shared_ptr<Region> region = (RegionFactory::create (src, plist));
1166 playlist()->add_region (region, pos);
1167 _session->add_command (new StatefulDiffCommand (playlist()));
1170 real_editor->commit_reversible_command ();
1173 return boost::dynamic_pointer_cast<MidiRegion>(region);
1177 MidiTimeAxisView::ensure_step_editor ()
1179 if (!_step_editor) {
1180 _step_editor = new StepEditor (_editor, midi_track(), *this);
1185 MidiTimeAxisView::start_step_editing ()
1187 ensure_step_editor ();
1188 _step_editor->start_step_editing ();
1192 MidiTimeAxisView::stop_step_editing ()
1195 _step_editor->stop_step_editing ();
1200 /** @return channel (counted from 0) to add an event to, based on the current setting
1201 * of the channel selector.
1204 MidiTimeAxisView::get_channel_for_add () const
1206 uint16_t const chn_mask = _channel_selector.get_selected_channels ();
1208 uint8_t channel = 0;
1210 /* pick the highest selected channel, unless all channels are selected,
1211 which is interpreted to mean channel 1 (zero)
1214 for (uint16_t i = 0; i < 16; ++i) {
1215 if (chn_mask & (1<<i)) {
1221 if (chn_cnt == 16) {
1229 MidiTimeAxisView::note_range_changed ()
1231 set_gui_property ("note-range-min", (int) midi_view()->lowest_note ());
1232 set_gui_property ("note-range-max", (int) midi_view()->highest_note ());
1236 MidiTimeAxisView::contents_height_changed ()
1238 _range_scroomer->set_size_request (-1, _view->child_height ());