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 */
204 HBox* midi_controls_hbox = manage(new HBox());
206 MIDI::Name::MidiPatchManager& patch_manager = MIDI::Name::MidiPatchManager::instance();
208 MIDI::Name::MasterDeviceNames::Models::const_iterator m = patch_manager.all_models().begin();
209 for (; m != patch_manager.all_models().end(); ++m) {
210 _model_selector.append_text(m->c_str());
214 _model_selector.signal_changed().connect(sigc::mem_fun(*this, &MidiTimeAxisView::model_changed));
216 _custom_device_mode_selector.signal_changed().connect(
217 sigc::mem_fun(*this, &MidiTimeAxisView::custom_device_mode_changed));
219 _model_selector.set_active_text (gui_property (X_("midnam-model-name")));
220 _custom_device_mode_selector.set_active_text (gui_property (X_("midnam-custom-device-mode")));
222 ARDOUR_UI::instance()->set_tip (_model_selector, _("External MIDI Device"));
223 ARDOUR_UI::instance()->set_tip (_custom_device_mode_selector, _("External Device Mode"));
225 midi_controls_hbox->pack_start(_channel_selector, true, false);
226 if (!patch_manager.all_models().empty()) {
227 _midi_controls_box.set_border_width (5);
228 _midi_controls_box.pack_start(_model_selector, true, false);
229 _midi_controls_box.pack_start(_custom_device_mode_selector, true, false);
232 _midi_controls_box.pack_start(*midi_controls_hbox, true, true);
234 controls_vbox.pack_start(_midi_controls_box, false, false);
236 // restore channel selector settings
237 _channel_selector.set_channel_mode(midi_track()->get_channel_mode(), midi_track()->get_channel_mask());
238 _channel_selector.mode_changed.connect(
239 sigc::mem_fun(*midi_track(), &MidiTrack::set_channel_mode));
240 _channel_selector.mode_changed.connect(
241 sigc::mem_fun(*this, &MidiTimeAxisView::set_channel_mode));
243 string prop = gui_property ("color-mode");
245 _color_mode = ColorMode (string_2_enum(prop, _color_mode));
246 if (_color_mode == ChannelColors) {
247 _channel_selector.set_channel_colors(CanvasNoteEvent::midi_channel_colors);
251 set_color_mode (_color_mode, true, false);
253 prop = gui_property ("note-mode");
255 _note_mode = NoteMode (string_2_enum (prop, _note_mode));
256 if (_percussion_mode_item) {
257 _percussion_mode_item->set_active (_note_mode == Percussive);
261 /* Look for any GUI object state nodes that represent automation children that should exist, and create
265 list<string> gui_ids = gui_object_state().all_ids ();
266 for (list<string>::const_iterator i = gui_ids.begin(); i != gui_ids.end(); ++i) {
269 Evoral::Parameter parameter (0, 0, 0);
271 bool const p = AutomationTimeAxisView::parse_state_id (*i, route_id, has_parameter, parameter);
272 if (p && route_id == _route->id () && has_parameter) {
273 create_automation_child (parameter, string_is_affirmative (gui_object_state().get_string (*i, X_("visible"))));
279 MidiTimeAxisView::first_idle ()
286 MidiTimeAxisView::~MidiTimeAxisView ()
288 delete _piano_roll_header;
289 _piano_roll_header = 0;
291 delete _range_scroomer;
294 delete controller_menu;
299 MidiTimeAxisView::enter_internal_edit_mode ()
302 midi_view()->enter_internal_edit_mode ();
307 MidiTimeAxisView::leave_internal_edit_mode ()
310 midi_view()->leave_internal_edit_mode ();
315 MidiTimeAxisView::check_step_edit ()
317 ensure_step_editor ();
318 _step_editor->check_step_edit ();
322 MidiTimeAxisView::model_changed()
324 std::list<std::string> device_modes = MIDI::Name::MidiPatchManager::instance()
325 .custom_device_mode_names_by_model(_model_selector.get_active_text());
327 _custom_device_mode_selector.clear_items();
329 for (std::list<std::string>::const_iterator i = device_modes.begin();
330 i != device_modes.end(); ++i) {
331 _custom_device_mode_selector.append_text(*i);
334 _custom_device_mode_selector.set_active(0);
336 _route->instrument_info().set_external_instrument (_model_selector.get_active_text(), _custom_device_mode_selector.get_active_text());
340 MidiTimeAxisView::custom_device_mode_changed()
342 _route->instrument_info().set_external_instrument (_model_selector.get_active_text(), _custom_device_mode_selector.get_active_text());
346 MidiTimeAxisView::midi_view()
348 return dynamic_cast<MidiStreamView*>(_view);
352 MidiTimeAxisView::set_height (uint32_t h)
354 if (h >= MIDI_CONTROLS_BOX_MIN_HEIGHT) {
355 _midi_controls_box.show_all ();
357 _midi_controls_box.hide();
360 if (h >= KEYBOARD_MIN_HEIGHT) {
361 if (is_track() && _range_scroomer) {
362 _range_scroomer->show();
364 if (is_track() && _piano_roll_header) {
365 _piano_roll_header->show();
368 if (is_track() && _range_scroomer) {
369 _range_scroomer->hide();
371 if (is_track() && _piano_roll_header) {
372 _piano_roll_header->hide();
376 /* We need to do this after changing visibility of our stuff, as it will
377 eventually trigger a call to Editor::reset_controls_layout_width(),
378 which needs to know if we have just shown or hidden a scroomer /
381 RouteTimeAxisView::set_height (h);
385 MidiTimeAxisView::append_extra_display_menu_items ()
387 using namespace Menu_Helpers;
389 MenuList& items = display_menu->items();
392 Menu *range_menu = manage(new Menu);
393 MenuList& range_items = range_menu->items();
394 range_menu->set_name ("ArdourContextMenu");
396 range_items.push_back (MenuElem (_("Show Full Range"), sigc::bind (
397 sigc::mem_fun(*this, &MidiTimeAxisView::set_note_range),
398 MidiStreamView::FullRange, true)));
400 range_items.push_back (MenuElem (_("Fit Contents"), sigc::bind (
401 sigc::mem_fun(*this, &MidiTimeAxisView::set_note_range),
402 MidiStreamView::ContentsRange, true)));
404 items.push_back (MenuElem (_("Note Range"), *range_menu));
405 items.push_back (MenuElem (_("Note Mode"), *build_note_mode_menu()));
407 items.push_back (SeparatorElem ());
411 MidiTimeAxisView::build_automation_action_menu (bool for_selection)
413 using namespace Menu_Helpers;
415 /* If we have a controller menu, we need to detach it before
416 RouteTimeAxis::build_automation_action_menu destroys the
417 menu it is attached to. Otherwise GTK destroys
418 controller_menu's gobj, meaning that it can't be reattached
419 below. See bug #3134.
422 if (controller_menu) {
423 detach_menu (*controller_menu);
426 _channel_command_menu_map.clear ();
427 RouteTimeAxisView::build_automation_action_menu (for_selection);
429 MenuList& automation_items = automation_action_menu->items();
431 uint16_t selected_channels = _channel_selector.get_selected_channels();
433 if (selected_channels != 0) {
435 automation_items.push_back (SeparatorElem());
437 /* these 2 MIDI "command" types are semantically more like automation than note data,
438 but they are not MIDI controllers. We give them special status in this menu, since
439 they will not show up in the controller list and anyone who actually knows
440 something about MIDI (!) would not expect to find them there.
443 add_channel_command_menu_item (automation_items, _("Bender"), MidiPitchBenderAutomation, 0);
444 automation_items.back().set_sensitive (!for_selection || _editor.get_selection().tracks.size() == 1);
445 add_channel_command_menu_item (automation_items, _("Pressure"), MidiChannelPressureAutomation, 0);
446 automation_items.back().set_sensitive (!for_selection || _editor.get_selection().tracks.size() == 1);
448 /* now all MIDI controllers. Always offer the possibility that we will rebuild the controllers menu
449 since it might need to be updated after a channel mode change or other change. Also detach it
450 first in case it has been used anywhere else.
453 build_controller_menu ();
455 automation_items.push_back (SeparatorElem());
456 automation_items.push_back (MenuElem (_("Controllers"), *controller_menu));
457 automation_items.back().set_sensitive (!for_selection || _editor.get_selection().tracks.size() == 1);
459 automation_items.push_back (MenuElem (string_compose ("<i>%1</i>", _("No MIDI Channels selected"))));
460 dynamic_cast<Label*> (automation_items.back().get_child())->set_use_markup (true);
466 MidiTimeAxisView::change_all_channel_tracks_visibility (bool yn, Evoral::Parameter param)
468 uint16_t selected_channels = _channel_selector.get_selected_channels();
470 for (uint8_t chn = 0; chn < 16; chn++) {
471 if (selected_channels & (0x0001 << chn)) {
473 Evoral::Parameter fully_qualified_param (param.type(), chn, param.id());
474 Gtk::CheckMenuItem* menu = automation_child_menu_item (fully_qualified_param);
477 menu->set_active (yn);
484 MidiTimeAxisView::add_channel_command_menu_item (Menu_Helpers::MenuList& items, const string& label, AutomationType auto_type, uint8_t cmd)
486 using namespace Menu_Helpers;
488 /* count the number of selected channels because we will build a different menu structure if there is more than 1 selected.
491 uint16_t selected_channels = _channel_selector.get_selected_channels();
494 for (uint8_t chn = 0; chn < 16; chn++) {
495 if (selected_channels & (0x0001 << chn)) {
504 /* multiple channels - create a submenu, with 1 item per channel */
506 Menu* chn_menu = manage (new Menu);
507 MenuList& chn_items (chn_menu->items());
508 Evoral::Parameter param_without_channel (auto_type, 0, cmd);
510 /* add a couple of items to hide/show all of them */
512 chn_items.push_back (MenuElem (_("Hide all channels"),
513 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
514 false, param_without_channel)));
515 chn_items.push_back (MenuElem (_("Show all channels"),
516 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
517 true, param_without_channel)));
519 for (uint8_t chn = 0; chn < 16; chn++) {
520 if (selected_channels & (0x0001 << chn)) {
522 /* for each selected channel, add a menu item for this controller */
524 Evoral::Parameter fully_qualified_param (auto_type, chn, cmd);
525 chn_items.push_back (CheckMenuElem (string_compose (_("Channel %1"), chn+1),
526 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
527 fully_qualified_param)));
529 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
530 bool visible = false;
533 if (track->marked_for_display()) {
538 CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&chn_items.back());
539 _channel_command_menu_map[fully_qualified_param] = cmi;
540 cmi->set_active (visible);
544 /* now create an item in the parent menu that has the per-channel list as a submenu */
546 items.push_back (MenuElem (label, *chn_menu));
550 /* just one channel - create a single menu item for this command+channel combination*/
552 for (uint8_t chn = 0; chn < 16; chn++) {
553 if (selected_channels & (0x0001 << chn)) {
555 Evoral::Parameter fully_qualified_param (auto_type, chn, cmd);
556 items.push_back (CheckMenuElem (label,
557 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
558 fully_qualified_param)));
560 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
561 bool visible = false;
564 if (track->marked_for_display()) {
569 CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&items.back());
570 _channel_command_menu_map[fully_qualified_param] = cmi;
571 cmi->set_active (visible);
573 /* one channel only */
581 MidiTimeAxisView::build_controller_menu ()
583 using namespace Menu_Helpers;
585 if (controller_menu) {
586 /* it exists and has not been invalidated by a channel mode change, so just return it */
590 controller_menu = new Menu; // explicitly managed by us
591 MenuList& items (controller_menu->items());
593 /* create several "top level" menu items for sets of controllers (16 at a time), and populate each one with a submenu
594 for each controller+channel combination covering the currently selected channels for this track
597 uint16_t selected_channels = _channel_selector.get_selected_channels();
599 /* count the number of selected channels because we will build a different menu structure if there is more than 1 selected.
604 for (uint8_t chn = 0; chn < 16; chn++) {
605 if (selected_channels & (0x0001 << chn)) {
612 /* loop over all 127 MIDI controllers, in groups of 16; except don't offer
613 bank select controllers, as they are handled by the `patch' code */
615 for (int i = 0; i < 127; i += 16) {
617 Menu* ctl_menu = manage (new Menu);
618 MenuList& ctl_items (ctl_menu->items());
621 /* for each controller, consider whether to create a submenu or a single item */
623 for (int ctl = i; ctl < i+16; ++ctl) {
625 if (ctl == MIDI_CTL_MSB_BANK || ctl == MIDI_CTL_LSB_BANK) {
631 /* multiple channels - create a submenu, with 1 item per channel */
633 Menu* chn_menu = manage (new Menu);
634 MenuList& chn_items (chn_menu->items());
636 /* add a couple of items to hide/show this controller on all channels */
638 Evoral::Parameter param_without_channel (MidiCCAutomation, 0, ctl);
639 chn_items.push_back (MenuElem (_("Hide all channels"),
640 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
641 false, param_without_channel)));
642 chn_items.push_back (MenuElem (_("Show all channels"),
643 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
644 true, param_without_channel)));
646 for (uint8_t chn = 0; chn < 16; chn++) {
647 if (selected_channels & (0x0001 << chn)) {
649 /* for each selected channel, add a menu item for this controller */
651 Evoral::Parameter fully_qualified_param (MidiCCAutomation, chn, ctl);
652 chn_items.push_back (CheckMenuElem (string_compose (_("Channel %1"), chn+1),
653 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
654 fully_qualified_param)));
656 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
657 bool visible = false;
660 if (track->marked_for_display()) {
665 CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&chn_items.back());
666 _controller_menu_map[fully_qualified_param] = cmi;
667 cmi->set_active (visible);
671 /* add the per-channel menu to the list of controllers, with the name of the controller */
672 ctl_items.push_back (MenuElem (string_compose ("<b>%1</b>: %2", ctl, midi_name (ctl)), *chn_menu));
673 dynamic_cast<Label*> (ctl_items.back().get_child())->set_use_markup (true);
677 /* just one channel - create a single menu item for this ctl+channel combination*/
679 for (uint8_t chn = 0; chn < 16; chn++) {
680 if (selected_channels & (0x0001 << chn)) {
682 Evoral::Parameter fully_qualified_param (MidiCCAutomation, chn, ctl);
683 ctl_items.push_back (
685 string_compose ("<b>%1</b>: %2 [%3]", ctl, midi_name (ctl), int (chn)),
686 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
687 fully_qualified_param)
690 dynamic_cast<Label*> (ctl_items.back().get_child())->set_use_markup (true);
692 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
693 bool visible = false;
696 if (track->marked_for_display()) {
701 CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&ctl_items.back());
702 _controller_menu_map[fully_qualified_param] = cmi;
703 cmi->set_active (visible);
705 /* one channel only */
712 /* add the menu for this block of controllers to the overall controller menu */
714 items.push_back (MenuElem (string_compose (_("Controllers %1-%2"), i, i+15), *ctl_menu));
719 MidiTimeAxisView::build_note_mode_menu()
721 using namespace Menu_Helpers;
723 Menu* mode_menu = manage (new Menu);
724 MenuList& items = mode_menu->items();
725 mode_menu->set_name ("ArdourContextMenu");
727 RadioMenuItem::Group mode_group;
728 items.push_back (RadioMenuElem (mode_group,_("Sustained"),
729 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_mode), Sustained, true)));
730 _note_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
731 _note_mode_item->set_active(_note_mode == Sustained);
733 items.push_back (RadioMenuElem (mode_group, _("Percussive"),
734 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_mode), Percussive, true)));
735 _percussion_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
736 _percussion_mode_item->set_active(_note_mode == Percussive);
742 MidiTimeAxisView::build_color_mode_menu()
744 using namespace Menu_Helpers;
746 Menu* mode_menu = manage (new Menu);
747 MenuList& items = mode_menu->items();
748 mode_menu->set_name ("ArdourContextMenu");
750 RadioMenuItem::Group mode_group;
751 items.push_back (RadioMenuElem (mode_group, _("Meter Colors"),
752 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode),
753 MeterColors, false, true, true)));
754 _meter_color_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
755 _meter_color_mode_item->set_active(_color_mode == MeterColors);
757 items.push_back (RadioMenuElem (mode_group, _("Channel Colors"),
758 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode),
759 ChannelColors, false, true, true)));
760 _channel_color_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
761 _channel_color_mode_item->set_active(_color_mode == ChannelColors);
763 items.push_back (RadioMenuElem (mode_group, _("Track Color"),
764 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode),
765 TrackColor, false, true, true)));
766 _channel_color_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
767 _channel_color_mode_item->set_active(_color_mode == TrackColor);
773 MidiTimeAxisView::set_note_mode(NoteMode mode, bool apply_to_selection)
775 if (apply_to_selection) {
776 _editor.get_selection().tracks.foreach_midi_time_axis (boost::bind (&MidiTimeAxisView::set_note_mode, _1, mode, false));
778 if (_note_mode != mode || midi_track()->note_mode() != mode) {
780 midi_track()->set_note_mode(mode);
781 set_gui_property ("note-mode", enum_2_string(_note_mode));
782 _view->redisplay_track();
788 MidiTimeAxisView::set_color_mode (ColorMode mode, bool force, bool redisplay, bool apply_to_selection)
790 if (apply_to_selection) {
791 _editor.get_selection().tracks.foreach_midi_time_axis (
792 boost::bind (&MidiTimeAxisView::set_color_mode, _1, mode, force, redisplay, false)
796 if (_color_mode == mode && !force) {
800 if (mode == ChannelColors) {
801 _channel_selector.set_channel_colors(CanvasNoteEvent::midi_channel_colors);
803 _channel_selector.set_default_channel_color();
807 set_gui_property ("color-mode", enum_2_string(_color_mode));
809 _view->redisplay_track();
815 MidiTimeAxisView::set_note_range (MidiStreamView::VisibleNoteRange range, bool apply_to_selection)
817 if (apply_to_selection) {
818 _editor.get_selection().tracks.foreach_midi_time_axis (
819 boost::bind (&MidiTimeAxisView::set_note_range, _1, range, false)
822 if (!_ignore_signals) {
823 midi_view()->set_note_range(range);
829 MidiTimeAxisView::update_range()
831 MidiGhostRegion* mgr;
833 for(list<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
834 if ((mgr = dynamic_cast<MidiGhostRegion*>(*i)) != 0) {
841 MidiTimeAxisView::show_all_automation (bool apply_to_selection)
843 if (apply_to_selection) {
844 _editor.get_selection().tracks.foreach_midi_time_axis (boost::bind (&MidiTimeAxisView::show_all_automation, _1, false));
847 const set<Evoral::Parameter> params = midi_track()->midi_playlist()->contained_automation();
849 for (set<Evoral::Parameter>::const_iterator i = params.begin(); i != params.end(); ++i) {
850 create_automation_child(*i, true);
854 RouteTimeAxisView::show_all_automation ();
859 MidiTimeAxisView::show_existing_automation (bool apply_to_selection)
861 if (apply_to_selection) {
862 _editor.get_selection().tracks.foreach_midi_time_axis (boost::bind (&MidiTimeAxisView::show_existing_automation, _1, false));
865 const set<Evoral::Parameter> params = midi_track()->midi_playlist()->contained_automation();
867 for (set<Evoral::Parameter>::const_iterator i = params.begin(); i != params.end(); ++i) {
868 create_automation_child (*i, true);
872 RouteTimeAxisView::show_existing_automation ();
876 /** Create an automation track for the given parameter (pitch bend, channel pressure).
879 MidiTimeAxisView::create_automation_child (const Evoral::Parameter& param, bool show)
881 if (param.type() == NullAutomation) {
885 AutomationTracks::iterator existing = _automation_tracks.find (param);
887 if (existing != _automation_tracks.end()) {
889 /* automation track created because we had existing data for
890 * the processor, but visibility may need to be controlled
891 * since it will have been set visible by default.
894 if (existing->second->set_marked_for_display (show) && !no_redraw) {
901 boost::shared_ptr<AutomationTimeAxisView> track;
903 switch (param.type()) {
906 create_gain_automation_child (param, show);
909 case PluginAutomation:
910 /* handled elsewhere */
913 case MidiCCAutomation:
914 case MidiPgmChangeAutomation:
915 case MidiPitchBenderAutomation:
916 case MidiChannelPressureAutomation:
917 case MidiSystemExclusiveAutomation:
918 /* These controllers are region "automation" - they are owned
919 * by regions (and their MidiModels), not by the track. As a
920 * result we do not create an AutomationList/Line for the track
921 * ... except here we are doing something!! XXX
924 track.reset (new AutomationTimeAxisView (
927 boost::shared_ptr<Automatable> (),
928 boost::shared_ptr<AutomationControl> (),
934 _route->describe_parameter(param)
938 _view->foreach_regionview (sigc::mem_fun (*track.get(), &TimeAxisView::add_ghost));
941 add_automation_child (param, track, show);
945 error << "MidiTimeAxisView: unknown automation child " << EventTypeMap::instance().to_symbol(param) << endmsg;
950 MidiTimeAxisView::route_active_changed ()
952 RouteUI::route_active_changed ();
955 if (_route->active()) {
956 controls_ebox.set_name ("MidiTrackControlsBaseUnselected");
957 controls_base_selected_name = "MidiTrackControlsBaseSelected";
958 controls_base_unselected_name = "MidiTrackControlsBaseUnselected";
960 controls_ebox.set_name ("MidiTrackControlsBaseInactiveUnselected");
961 controls_base_selected_name = "MidiTrackControlsBaseInactiveSelected";
962 controls_base_unselected_name = "MidiTrackControlsBaseInactiveUnselected";
968 if (_route->active()) {
969 controls_ebox.set_name ("BusControlsBaseUnselected");
970 controls_base_selected_name = "BusControlsBaseSelected";
971 controls_base_unselected_name = "BusControlsBaseUnselected";
973 controls_ebox.set_name ("BusControlsBaseInactiveUnselected");
974 controls_base_selected_name = "BusControlsBaseInactiveSelected";
975 controls_base_unselected_name = "BusControlsBaseInactiveUnselected";
981 MidiTimeAxisView::set_note_selection (uint8_t note)
983 if (!_editor.internal_editing()) {
987 uint16_t chn_mask = _channel_selector.get_selected_channels();
989 if (_view->num_selected_regionviews() == 0) {
990 _view->foreach_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_selection_region_view), note, chn_mask));
992 _view->foreach_selected_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_selection_region_view), note, chn_mask));
997 MidiTimeAxisView::add_note_selection (uint8_t note)
999 if (!_editor.internal_editing()) {
1003 uint16_t chn_mask = _channel_selector.get_selected_channels();
1005 if (_view->num_selected_regionviews() == 0) {
1006 _view->foreach_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection_region_view), note, chn_mask));
1008 _view->foreach_selected_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection_region_view), note, chn_mask));
1013 MidiTimeAxisView::extend_note_selection (uint8_t note)
1015 if (!_editor.internal_editing()) {
1019 uint16_t chn_mask = _channel_selector.get_selected_channels();
1021 if (_view->num_selected_regionviews() == 0) {
1022 _view->foreach_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::extend_note_selection_region_view), note, chn_mask));
1024 _view->foreach_selected_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::extend_note_selection_region_view), note, chn_mask));
1029 MidiTimeAxisView::toggle_note_selection (uint8_t note)
1031 if (!_editor.internal_editing()) {
1035 uint16_t chn_mask = _channel_selector.get_selected_channels();
1037 if (_view->num_selected_regionviews() == 0) {
1038 _view->foreach_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection_region_view), note, chn_mask));
1040 _view->foreach_selected_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection_region_view), note, chn_mask));
1045 MidiTimeAxisView::set_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1047 dynamic_cast<MidiRegionView*>(rv)->select_matching_notes (note, chn_mask, false, false);
1051 MidiTimeAxisView::add_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1053 dynamic_cast<MidiRegionView*>(rv)->select_matching_notes (note, chn_mask, true, false);
1057 MidiTimeAxisView::extend_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1059 dynamic_cast<MidiRegionView*>(rv)->select_matching_notes (note, chn_mask, true, true);
1063 MidiTimeAxisView::toggle_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1065 dynamic_cast<MidiRegionView*>(rv)->toggle_matching_notes (note, chn_mask);
1069 MidiTimeAxisView::set_channel_mode (ChannelMode, uint16_t)
1071 /* hide all automation tracks that use the wrong channel(s) and show all those that use
1075 uint16_t selected_channels = _channel_selector.get_selected_channels();
1076 bool changed = false;
1080 for (uint32_t ctl = 0; ctl < 127; ++ctl) {
1082 for (uint32_t chn = 0; chn < 16; ++chn) {
1083 Evoral::Parameter fully_qualified_param (MidiCCAutomation, chn, ctl);
1084 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
1090 if ((selected_channels & (0x0001 << chn)) == 0) {
1091 /* channel not in use. hiding it will trigger RouteTimeAxisView::automation_track_hidden()
1092 which will cause a redraw. We don't want one per channel, so block that with no_redraw.
1094 changed = track->set_marked_for_display (false) || changed;
1096 changed = track->set_marked_for_display (true) || changed;
1103 /* TODO: Bender, Pressure */
1105 /* invalidate the controller menu, so that we rebuild it next time */
1106 _controller_menu_map.clear ();
1107 delete controller_menu;
1108 controller_menu = 0;
1116 MidiTimeAxisView::automation_child_menu_item (Evoral::Parameter param)
1118 Gtk::CheckMenuItem* m = RouteTimeAxisView::automation_child_menu_item (param);
1123 ParameterMenuMap::iterator i = _controller_menu_map.find (param);
1124 if (i != _controller_menu_map.end()) {
1128 i = _channel_command_menu_map.find (param);
1129 if (i != _channel_command_menu_map.end()) {
1136 boost::shared_ptr<MidiRegion>
1137 MidiTimeAxisView::add_region (framepos_t pos, framecnt_t length, bool commit)
1139 Editor* real_editor = dynamic_cast<Editor*> (&_editor);
1141 real_editor->begin_reversible_command (Operations::create_region);
1142 playlist()->clear_changes ();
1144 real_editor->snap_to (pos, 0);
1146 boost::shared_ptr<Source> src = _session->create_midi_source_for_session (view()->trackview().track().get(),
1147 view()->trackview().track()->name());
1150 plist.add (ARDOUR::Properties::start, 0);
1151 plist.add (ARDOUR::Properties::length, length);
1152 plist.add (ARDOUR::Properties::name, PBD::basename_nosuffix(src->name()));
1154 boost::shared_ptr<Region> region = (RegionFactory::create (src, plist));
1156 playlist()->add_region (region, pos);
1157 _session->add_command (new StatefulDiffCommand (playlist()));
1160 real_editor->commit_reversible_command ();
1163 return boost::dynamic_pointer_cast<MidiRegion>(region);
1167 MidiTimeAxisView::ensure_step_editor ()
1169 if (!_step_editor) {
1170 _step_editor = new StepEditor (_editor, midi_track(), *this);
1175 MidiTimeAxisView::start_step_editing ()
1177 ensure_step_editor ();
1178 _step_editor->start_step_editing ();
1182 MidiTimeAxisView::stop_step_editing ()
1185 _step_editor->stop_step_editing ();
1190 /** @return channel (counted from 0) to add an event to, based on the current setting
1191 * of the channel selector.
1194 MidiTimeAxisView::get_channel_for_add () const
1196 uint16_t const chn_mask = _channel_selector.get_selected_channels ();
1198 uint8_t channel = 0;
1200 /* pick the highest selected channel, unless all channels are selected,
1201 which is interpreted to mean channel 1 (zero)
1204 for (uint16_t i = 0; i < 16; ++i) {
1205 if (chn_mask & (1<<i)) {
1211 if (chn_cnt == 16) {
1219 MidiTimeAxisView::note_range_changed ()
1221 set_gui_property ("note-range-min", (int) midi_view()->lowest_note ());
1222 set_gui_property ("note-range-max", (int) midi_view()->highest_note ());
1226 MidiTimeAxisView::contents_height_changed ()
1228 _range_scroomer->set_size_request (-1, _view->child_height ());