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/file_source.h"
42 #include "ardour/ladspa_plugin.h"
43 #include "ardour/location.h"
44 #include "ardour/midi_diskstream.h"
45 #include "ardour/midi_patch_manager.h"
46 #include "ardour/midi_playlist.h"
47 #include "ardour/midi_region.h"
48 #include "ardour/midi_source.h"
49 #include "ardour/operations.h"
50 #include "ardour/playlist.h"
51 #include "ardour/processor.h"
52 #include "ardour/region_factory.h"
53 #include "ardour/session.h"
54 #include "ardour/session_playlist.h"
55 #include "ardour/tempo.h"
56 #include "ardour/utils.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 , _midi_thru_item (0)
117 , controller_menu (0)
123 MidiTimeAxisView::set_route (boost::shared_ptr<Route> rt)
127 _view = new MidiStreamView (*this);
130 _piano_roll_header = new PianoRollHeader(*midi_view());
131 _range_scroomer = new MidiScroomer(midi_view()->note_range_adjustment);
134 /* This next call will result in our height being set up, so it must come after
135 the creation of the piano roll / range scroomer as their visibility is set up
138 RouteTimeAxisView::set_route (rt);
140 _view->apply_color (_color, StreamView::RegionColor);
142 subplugin_menu.set_name ("ArdourContextMenu");
144 if (!gui_property ("note-range-min").empty ()) {
145 midi_view()->apply_note_range (atoi (gui_property ("note-range-min").c_str()), atoi (gui_property ("note-range-max").c_str()), true);
147 midi_view()->NoteRangeChanged.connect (sigc::mem_fun (*this, &MidiTimeAxisView::note_range_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), ui_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 controls_hbox.pack_start(*_range_scroomer);
175 controls_hbox.pack_start(*_piano_roll_header);
177 controls_ebox.set_name ("MidiTrackControlsBaseUnselected");
178 controls_base_selected_name = "MidiTrackControlsBaseSelected";
179 controls_base_unselected_name = "MidiTrackControlsBaseUnselected";
181 midi_view()->NoteRangeChanged.connect (sigc::mem_fun(*this, &MidiTimeAxisView::update_range));
183 /* ask for notifications of any new RegionViews */
184 _view->RegionViewAdded.connect (sigc::mem_fun(*this, &MidiTimeAxisView::region_view_added));
186 if (!_editor.have_idled()) {
187 /* first idle will do what we need */
193 HBox* midi_controls_hbox = manage(new HBox());
195 MIDI::Name::MidiPatchManager& patch_manager = MIDI::Name::MidiPatchManager::instance();
197 MIDI::Name::MasterDeviceNames::Models::const_iterator m = patch_manager.all_models().begin();
198 for (; m != patch_manager.all_models().end(); ++m) {
199 _model_selector.append_text(m->c_str());
202 _model_selector.signal_changed().connect(sigc::mem_fun(*this, &MidiTimeAxisView::model_changed));
204 _custom_device_mode_selector.signal_changed().connect(
205 sigc::mem_fun(*this, &MidiTimeAxisView::custom_device_mode_changed));
207 // TODO: persist the choice
208 // this initializes the comboboxes and sends out the signal
209 _model_selector.set_active(0);
211 midi_controls_hbox->pack_start(_channel_selector, true, false);
212 if (!patch_manager.all_models().empty()) {
213 _midi_controls_box.pack_start(_model_selector, true, false);
214 _midi_controls_box.pack_start(_custom_device_mode_selector, true, false);
217 _midi_controls_box.pack_start(*midi_controls_hbox, true, true);
219 controls_vbox.pack_start(_midi_controls_box, false, false);
221 // restore channel selector settings
222 _channel_selector.set_channel_mode(midi_track()->get_channel_mode(), midi_track()->get_channel_mask());
223 _channel_selector.mode_changed.connect(
224 sigc::mem_fun(*midi_track(), &MidiTrack::set_channel_mode));
225 _channel_selector.mode_changed.connect(
226 sigc::mem_fun(*this, &MidiTimeAxisView::set_channel_mode));
228 string prop = gui_property ("color-mode");
230 _color_mode = ColorMode (string_2_enum(prop, _color_mode));
231 if (_color_mode == ChannelColors) {
232 _channel_selector.set_channel_colors(CanvasNoteEvent::midi_channel_colors);
236 set_color_mode (_color_mode, true, false);
238 prop = gui_property ("note-mode");
240 _note_mode = NoteMode (string_2_enum (prop, _note_mode));
241 if (_percussion_mode_item) {
242 _percussion_mode_item->set_active (_note_mode == Percussive);
246 /* Look for any GUI object state nodes that represent automation children that should exist, and create
250 list<string> gui_ids = gui_object_state().all_ids ();
251 for (list<string>::const_iterator i = gui_ids.begin(); i != gui_ids.end(); ++i) {
254 Evoral::Parameter parameter (0, 0, 0);
256 bool const p = AutomationTimeAxisView::parse_state_id (*i, route_id, has_parameter, parameter);
257 if (p && route_id == _route->id () && has_parameter) {
258 create_automation_child (parameter, string_is_affirmative (gui_object_state().get_string (*i, X_("visible"))));
264 MidiTimeAxisView::first_idle ()
271 MidiTimeAxisView::~MidiTimeAxisView ()
273 delete _piano_roll_header;
274 _piano_roll_header = 0;
276 delete _range_scroomer;
279 delete controller_menu;
284 MidiTimeAxisView::enter_internal_edit_mode ()
287 midi_view()->enter_internal_edit_mode ();
292 MidiTimeAxisView::leave_internal_edit_mode ()
295 midi_view()->leave_internal_edit_mode ();
300 MidiTimeAxisView::check_step_edit ()
302 ensure_step_editor ();
303 _step_editor->check_step_edit ();
307 MidiTimeAxisView::model_changed()
309 std::list<std::string> device_modes = MIDI::Name::MidiPatchManager::instance()
310 .custom_device_mode_names_by_model(_model_selector.get_active_text());
312 _custom_device_mode_selector.clear_items();
314 for (std::list<std::string>::const_iterator i = device_modes.begin();
315 i != device_modes.end(); ++i) {
316 _custom_device_mode_selector.append_text(*i);
319 _custom_device_mode_selector.set_active(0);
322 void MidiTimeAxisView::custom_device_mode_changed()
324 _midi_patch_settings_changed.emit(_model_selector.get_active_text(),
325 _custom_device_mode_selector.get_active_text());
329 MidiTimeAxisView::midi_view()
331 return dynamic_cast<MidiStreamView*>(_view);
335 MidiTimeAxisView::set_height (uint32_t h)
337 if (h >= MIDI_CONTROLS_BOX_MIN_HEIGHT) {
338 _midi_controls_box.show_all ();
340 _midi_controls_box.hide();
343 if (h >= KEYBOARD_MIN_HEIGHT) {
344 if (is_track() && _range_scroomer) {
345 _range_scroomer->show();
347 if (is_track() && _piano_roll_header) {
348 _piano_roll_header->show();
351 if (is_track() && _range_scroomer) {
352 _range_scroomer->hide();
354 if (is_track() && _piano_roll_header) {
355 _piano_roll_header->hide();
359 /* We need to do this after changing visibility of our stuff, as it will
360 eventually trigger a call to Editor::reset_controls_layout_width(),
361 which needs to know if we have just shown or hidden a scroomer /
364 RouteTimeAxisView::set_height (h);
368 MidiTimeAxisView::append_extra_display_menu_items ()
370 using namespace Menu_Helpers;
372 MenuList& items = display_menu->items();
375 Menu *range_menu = manage(new Menu);
376 MenuList& range_items = range_menu->items();
377 range_menu->set_name ("ArdourContextMenu");
379 range_items.push_back (MenuElem (_("Show Full Range"), sigc::bind (
380 sigc::mem_fun(*this, &MidiTimeAxisView::set_note_range),
381 MidiStreamView::FullRange)));
383 range_items.push_back (MenuElem (_("Fit Contents"), sigc::bind (
384 sigc::mem_fun(*this, &MidiTimeAxisView::set_note_range),
385 MidiStreamView::ContentsRange)));
387 items.push_back (MenuElem (_("Note Range"), *range_menu));
388 items.push_back (MenuElem (_("Note Mode"), *build_note_mode_menu()));
390 items.push_back (CheckMenuElem (_("MIDI Thru"), sigc::mem_fun(*this, &MidiTimeAxisView::toggle_midi_thru)));
391 _midi_thru_item = dynamic_cast<CheckMenuItem*>(&items.back());
393 items.push_back (SeparatorElem ());
397 MidiTimeAxisView::toggle_midi_thru ()
399 if (!_midi_thru_item) {
403 bool view_yn = _midi_thru_item->get_active();
404 if (view_yn != midi_track()->midi_thru()) {
405 midi_track()->set_midi_thru (view_yn);
410 MidiTimeAxisView::build_automation_action_menu (bool for_selection)
412 using namespace Menu_Helpers;
414 /* If we have a controller menu, we need to detach it before
415 RouteTimeAxis::build_automation_action_menu destroys the
416 menu it is attached to. Otherwise GTK destroys
417 controller_menu's gobj, meaning that it can't be reattached
418 below. See bug #3134.
421 if (controller_menu) {
422 detach_menu (*controller_menu);
425 _channel_command_menu_map.clear ();
426 RouteTimeAxisView::build_automation_action_menu (for_selection);
428 MenuList& automation_items = automation_action_menu->items();
430 uint16_t selected_channels = _channel_selector.get_selected_channels();
432 if (selected_channels != 0) {
434 automation_items.push_back (SeparatorElem());
436 /* these 2 MIDI "command" types are semantically more like automation than note data,
437 but they are not MIDI controllers. We give them special status in this menu, since
438 they will not show up in the controller list and anyone who actually knows
439 something about MIDI (!) would not expect to find them there.
442 add_channel_command_menu_item (automation_items, _("Bender"), MidiPitchBenderAutomation, 0);
443 automation_items.back().set_sensitive (!for_selection || _editor.get_selection().tracks.size() == 1);
444 add_channel_command_menu_item (automation_items, _("Pressure"), MidiChannelPressureAutomation, 0);
445 automation_items.back().set_sensitive (!for_selection || _editor.get_selection().tracks.size() == 1);
447 /* now all MIDI controllers. Always offer the possibility that we will rebuild the controllers menu
448 since it might need to be updated after a channel mode change or other change. Also detach it
449 first in case it has been used anywhere else.
452 build_controller_menu ();
454 automation_items.push_back (SeparatorElem());
455 automation_items.push_back (MenuElem (_("Controllers"), *controller_menu));
456 automation_items.back().set_sensitive (!for_selection || _editor.get_selection().tracks.size() == 1);
458 automation_items.push_back (MenuElem (string_compose ("<i>%1</i>", _("No MIDI Channels selected"))));
459 dynamic_cast<Label*> (automation_items.back().get_child())->set_use_markup (true);
465 MidiTimeAxisView::change_all_channel_tracks_visibility (bool yn, Evoral::Parameter param)
467 uint16_t selected_channels = _channel_selector.get_selected_channels();
469 for (uint8_t chn = 0; chn < 16; chn++) {
470 if (selected_channels & (0x0001 << chn)) {
472 Evoral::Parameter fully_qualified_param (param.type(), chn, param.id());
473 Gtk::CheckMenuItem* menu = automation_child_menu_item (fully_qualified_param);
476 menu->set_active (yn);
483 MidiTimeAxisView::add_channel_command_menu_item (Menu_Helpers::MenuList& items, const string& label, AutomationType auto_type, uint8_t cmd)
485 using namespace Menu_Helpers;
487 /* count the number of selected channels because we will build a different menu structure if there is more than 1 selected.
490 uint16_t selected_channels = _channel_selector.get_selected_channels();
493 for (uint8_t chn = 0; chn < 16; chn++) {
494 if (selected_channels & (0x0001 << chn)) {
503 /* multiple channels - create a submenu, with 1 item per channel */
505 Menu* chn_menu = manage (new Menu);
506 MenuList& chn_items (chn_menu->items());
507 Evoral::Parameter param_without_channel (auto_type, 0, cmd);
509 /* add a couple of items to hide/show all of them */
511 chn_items.push_back (MenuElem (_("Hide all channels"),
512 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
513 false, param_without_channel)));
514 chn_items.push_back (MenuElem (_("Show all channels"),
515 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
516 true, param_without_channel)));
518 for (uint8_t chn = 0; chn < 16; chn++) {
519 if (selected_channels & (0x0001 << chn)) {
521 /* for each selected channel, add a menu item for this controller */
523 Evoral::Parameter fully_qualified_param (auto_type, chn, cmd);
524 chn_items.push_back (CheckMenuElem (string_compose (_("Channel %1"), chn+1),
525 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
526 fully_qualified_param)));
528 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
529 bool visible = false;
532 if (track->marked_for_display()) {
537 CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&chn_items.back());
538 _channel_command_menu_map[fully_qualified_param] = cmi;
539 cmi->set_active (visible);
543 /* now create an item in the parent menu that has the per-channel list as a submenu */
545 items.push_back (MenuElem (label, *chn_menu));
549 /* just one channel - create a single menu item for this command+channel combination*/
551 for (uint8_t chn = 0; chn < 16; chn++) {
552 if (selected_channels & (0x0001 << chn)) {
554 Evoral::Parameter fully_qualified_param (auto_type, chn, cmd);
555 items.push_back (CheckMenuElem (label,
556 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
557 fully_qualified_param)));
559 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
560 bool visible = false;
563 if (track->marked_for_display()) {
568 CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&items.back());
569 _channel_command_menu_map[fully_qualified_param] = cmi;
570 cmi->set_active (visible);
572 /* one channel only */
580 MidiTimeAxisView::build_controller_menu ()
582 using namespace Menu_Helpers;
584 if (controller_menu) {
585 /* it exists and has not been invalidated by a channel mode change, so just return it */
589 controller_menu = new Menu; // explicitly managed by us
590 MenuList& items (controller_menu->items());
592 /* create several "top level" menu items for sets of controllers (16 at a time), and populate each one with a submenu
593 for each controller+channel combination covering the currently selected channels for this track
596 uint16_t selected_channels = _channel_selector.get_selected_channels();
598 /* count the number of selected channels because we will build a different menu structure if there is more than 1 selected.
603 for (uint8_t chn = 0; chn < 16; chn++) {
604 if (selected_channels & (0x0001 << chn)) {
611 /* loop over all 127 MIDI controllers, in groups of 16; except don't offer
612 bank select controllers, as they are handled by the `patch' code */
614 for (int i = 0; i < 127; i += 16) {
616 Menu* ctl_menu = manage (new Menu);
617 MenuList& ctl_items (ctl_menu->items());
620 /* for each controller, consider whether to create a submenu or a single item */
622 for (int ctl = i; ctl < i+16; ++ctl) {
624 if (ctl == MIDI_CTL_MSB_BANK || ctl == MIDI_CTL_LSB_BANK) {
630 /* multiple channels - create a submenu, with 1 item per channel */
632 Menu* chn_menu = manage (new Menu);
633 MenuList& chn_items (chn_menu->items());
635 /* add a couple of items to hide/show this controller on all channels */
637 Evoral::Parameter param_without_channel (MidiCCAutomation, 0, ctl);
638 chn_items.push_back (MenuElem (_("Hide all channels"),
639 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
640 false, param_without_channel)));
641 chn_items.push_back (MenuElem (_("Show all channels"),
642 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
643 true, param_without_channel)));
645 for (uint8_t chn = 0; chn < 16; chn++) {
646 if (selected_channels & (0x0001 << chn)) {
648 /* for each selected channel, add a menu item for this controller */
650 Evoral::Parameter fully_qualified_param (MidiCCAutomation, chn, ctl);
651 chn_items.push_back (CheckMenuElem (string_compose (_("Channel %1"), chn+1),
652 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
653 fully_qualified_param)));
655 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
656 bool visible = false;
659 if (track->marked_for_display()) {
664 CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&chn_items.back());
665 _controller_menu_map[fully_qualified_param] = cmi;
666 cmi->set_active (visible);
670 /* add the per-channel menu to the list of controllers, with the name of the controller */
671 ctl_items.push_back (MenuElem (string_compose ("<b>%1</b>: %2", ctl, midi_name (ctl)), *chn_menu));
672 dynamic_cast<Label*> (ctl_items.back().get_child())->set_use_markup (true);
676 /* just one channel - create a single menu item for this ctl+channel combination*/
678 for (uint8_t chn = 0; chn < 16; chn++) {
679 if (selected_channels & (0x0001 << chn)) {
681 Evoral::Parameter fully_qualified_param (MidiCCAutomation, chn, ctl);
682 ctl_items.push_back (
684 string_compose ("<b>%1</b>: %2 [%3]", ctl, midi_name (ctl), int (chn)),
685 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
686 fully_qualified_param)
689 dynamic_cast<Label*> (ctl_items.back().get_child())->set_use_markup (true);
691 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
692 bool visible = false;
695 if (track->marked_for_display()) {
700 CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&ctl_items.back());
701 _controller_menu_map[fully_qualified_param] = cmi;
702 cmi->set_active (visible);
704 /* one channel only */
711 /* add the menu for this block of controllers to the overall controller menu */
713 items.push_back (MenuElem (string_compose (_("Controllers %1-%2"), i, i+15), *ctl_menu));
718 MidiTimeAxisView::build_note_mode_menu()
720 using namespace Menu_Helpers;
722 Menu* mode_menu = manage (new Menu);
723 MenuList& items = mode_menu->items();
724 mode_menu->set_name ("ArdourContextMenu");
726 RadioMenuItem::Group mode_group;
727 items.push_back (RadioMenuElem (mode_group, _("Sustained"),
728 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_mode), Sustained)));
729 _note_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
730 _note_mode_item->set_active(_note_mode == Sustained);
732 items.push_back (RadioMenuElem (mode_group, _("Percussive"),
733 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_mode), Percussive)));
734 _percussion_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
735 _percussion_mode_item->set_active(_note_mode == Percussive);
741 MidiTimeAxisView::build_color_mode_menu()
743 using namespace Menu_Helpers;
745 Menu* mode_menu = manage (new Menu);
746 MenuList& items = mode_menu->items();
747 mode_menu->set_name ("ArdourContextMenu");
749 RadioMenuItem::Group mode_group;
750 items.push_back (RadioMenuElem (mode_group, _("Meter Colors"),
751 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode),
752 MeterColors, false, true)));
753 _meter_color_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
754 _meter_color_mode_item->set_active(_color_mode == MeterColors);
756 items.push_back (RadioMenuElem (mode_group, _("Channel Colors"),
757 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode),
758 ChannelColors, false, true)));
759 _channel_color_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
760 _channel_color_mode_item->set_active(_color_mode == ChannelColors);
762 items.push_back (RadioMenuElem (mode_group, _("Track Color"),
763 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode),
764 TrackColor, false, true)));
765 _channel_color_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
766 _channel_color_mode_item->set_active(_color_mode == TrackColor);
772 MidiTimeAxisView::set_note_mode(NoteMode mode)
774 if (_note_mode != mode || midi_track()->note_mode() != mode) {
776 midi_track()->set_note_mode(mode);
777 set_gui_property ("note-mode", enum_2_string(_note_mode));
778 _view->redisplay_track();
783 MidiTimeAxisView::set_color_mode (ColorMode mode, bool force, bool redisplay)
785 if (_color_mode == mode && !force) {
789 if (mode == ChannelColors) {
790 _channel_selector.set_channel_colors(CanvasNoteEvent::midi_channel_colors);
792 _channel_selector.set_default_channel_color();
796 set_gui_property ("color-mode", enum_2_string(_color_mode));
798 _view->redisplay_track();
803 MidiTimeAxisView::set_note_range(MidiStreamView::VisibleNoteRange range)
805 if (!_ignore_signals)
806 midi_view()->set_note_range(range);
811 MidiTimeAxisView::update_range()
813 MidiGhostRegion* mgr;
815 for(list<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
816 if ((mgr = dynamic_cast<MidiGhostRegion*>(*i)) != 0) {
823 MidiTimeAxisView::show_all_automation (bool apply_to_selection)
825 if (apply_to_selection) {
826 _editor.get_selection().tracks.foreach_midi_time_axis (boost::bind (&MidiTimeAxisView::show_all_automation, _1, false));
829 const set<Evoral::Parameter> params = midi_track()->midi_playlist()->contained_automation();
831 for (set<Evoral::Parameter>::const_iterator i = params.begin(); i != params.end(); ++i) {
832 create_automation_child(*i, true);
836 RouteTimeAxisView::show_all_automation ();
841 MidiTimeAxisView::show_existing_automation (bool apply_to_selection)
843 if (apply_to_selection) {
844 _editor.get_selection().tracks.foreach_midi_time_axis (boost::bind (&MidiTimeAxisView::show_existing_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_existing_automation ();
858 /** Create an automation track for the given parameter (pitch bend, channel pressure).
861 MidiTimeAxisView::create_automation_child (const Evoral::Parameter& param, bool show)
863 if (param.type() == NullAutomation) {
864 cerr << "WARNING: Attempt to create NullAutomation child, ignoring" << endl;
868 AutomationTracks::iterator existing = _automation_tracks.find (param);
870 if (existing != _automation_tracks.end()) {
872 /* automation track created because we had existing data for
873 * the processor, but visibility may need to be controlled
874 * since it will have been set visible by default.
877 cerr << "show existing auto track: " << show << " noredraw " << no_redraw << endl;
879 if (existing->second->set_marked_for_display (show) && !no_redraw) {
886 boost::shared_ptr<AutomationTimeAxisView> track;
888 switch (param.type()) {
891 create_gain_automation_child (param, show);
894 case PluginAutomation:
895 /* handled elsewhere */
898 case MidiCCAutomation:
899 case MidiPgmChangeAutomation:
900 case MidiPitchBenderAutomation:
901 case MidiChannelPressureAutomation:
902 case MidiSystemExclusiveAutomation:
903 /* These controllers are region "automation" - they are owned
904 * by regions (and their MidiModels), not by the track. As a
905 * result we do not create an AutomationList/Line for the track
906 * ... except here we are doing something!! XXX
909 track.reset (new AutomationTimeAxisView (
912 boost::shared_ptr<Automatable> (),
913 boost::shared_ptr<AutomationControl> (),
919 _route->describe_parameter(param)
923 _view->foreach_regionview (sigc::mem_fun (*track.get(), &TimeAxisView::add_ghost));
926 add_automation_child (param, track, show);
930 error << "MidiTimeAxisView: unknown automation child " << EventTypeMap::instance().to_symbol(param) << endmsg;
935 MidiTimeAxisView::route_active_changed ()
937 RouteUI::route_active_changed ();
940 if (_route->active()) {
941 controls_ebox.set_name ("MidiTrackControlsBaseUnselected");
942 controls_base_selected_name = "MidiTrackControlsBaseSelected";
943 controls_base_unselected_name = "MidiTrackControlsBaseUnselected";
945 controls_ebox.set_name ("MidiTrackControlsBaseInactiveUnselected");
946 controls_base_selected_name = "MidiTrackControlsBaseInactiveSelected";
947 controls_base_unselected_name = "MidiTrackControlsBaseInactiveUnselected";
953 if (_route->active()) {
954 controls_ebox.set_name ("BusControlsBaseUnselected");
955 controls_base_selected_name = "BusControlsBaseSelected";
956 controls_base_unselected_name = "BusControlsBaseUnselected";
958 controls_ebox.set_name ("BusControlsBaseInactiveUnselected");
959 controls_base_selected_name = "BusControlsBaseInactiveSelected";
960 controls_base_unselected_name = "BusControlsBaseInactiveUnselected";
966 MidiTimeAxisView::set_note_selection (uint8_t note)
968 if (!_editor.internal_editing()) {
972 uint16_t chn_mask = _channel_selector.get_selected_channels();
974 if (_view->num_selected_regionviews() == 0) {
975 _view->foreach_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_selection_region_view), note, chn_mask));
977 _view->foreach_selected_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_selection_region_view), note, chn_mask));
982 MidiTimeAxisView::add_note_selection (uint8_t note)
984 if (!_editor.internal_editing()) {
988 uint16_t chn_mask = _channel_selector.get_selected_channels();
990 if (_view->num_selected_regionviews() == 0) {
991 _view->foreach_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection_region_view), note, chn_mask));
993 _view->foreach_selected_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection_region_view), note, chn_mask));
998 MidiTimeAxisView::extend_note_selection (uint8_t note)
1000 if (!_editor.internal_editing()) {
1004 uint16_t chn_mask = _channel_selector.get_selected_channels();
1006 if (_view->num_selected_regionviews() == 0) {
1007 _view->foreach_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::extend_note_selection_region_view), note, chn_mask));
1009 _view->foreach_selected_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::extend_note_selection_region_view), note, chn_mask));
1014 MidiTimeAxisView::toggle_note_selection (uint8_t note)
1016 if (!_editor.internal_editing()) {
1020 uint16_t chn_mask = _channel_selector.get_selected_channels();
1022 if (_view->num_selected_regionviews() == 0) {
1023 _view->foreach_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection_region_view), note, chn_mask));
1025 _view->foreach_selected_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection_region_view), note, chn_mask));
1030 MidiTimeAxisView::set_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1032 dynamic_cast<MidiRegionView*>(rv)->select_matching_notes (note, chn_mask, false, false);
1036 MidiTimeAxisView::add_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1038 dynamic_cast<MidiRegionView*>(rv)->select_matching_notes (note, chn_mask, true, false);
1042 MidiTimeAxisView::extend_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1044 dynamic_cast<MidiRegionView*>(rv)->select_matching_notes (note, chn_mask, true, true);
1048 MidiTimeAxisView::toggle_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1050 dynamic_cast<MidiRegionView*>(rv)->toggle_matching_notes (note, chn_mask);
1054 MidiTimeAxisView::set_channel_mode (ChannelMode, uint16_t)
1056 /* hide all automation tracks that use the wrong channel(s) and show all those that use
1060 uint16_t selected_channels = _channel_selector.get_selected_channels();
1061 bool changed = false;
1065 for (uint32_t ctl = 0; ctl < 127; ++ctl) {
1067 for (uint32_t chn = 0; chn < 16; ++chn) {
1068 Evoral::Parameter fully_qualified_param (MidiCCAutomation, chn, ctl);
1069 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
1075 if ((selected_channels & (0x0001 << chn)) == 0) {
1076 /* channel not in use. hiding it will trigger RouteTimeAxisView::automation_track_hidden()
1077 which will cause a redraw. We don't want one per channel, so block that with no_redraw.
1079 changed = track->set_marked_for_display (false) || changed;
1081 changed = track->set_marked_for_display (true) || changed;
1088 /* TODO: Bender, Pressure */
1090 /* invalidate the controller menu, so that we rebuild it next time */
1091 _controller_menu_map.clear ();
1092 delete controller_menu;
1093 controller_menu = 0;
1101 MidiTimeAxisView::automation_child_menu_item (Evoral::Parameter param)
1103 Gtk::CheckMenuItem* m = RouteTimeAxisView::automation_child_menu_item (param);
1108 ParameterMenuMap::iterator i = _controller_menu_map.find (param);
1109 if (i != _controller_menu_map.end()) {
1113 i = _channel_command_menu_map.find (param);
1114 if (i != _channel_command_menu_map.end()) {
1121 boost::shared_ptr<MidiRegion>
1122 MidiTimeAxisView::add_region (framepos_t pos, framecnt_t length, bool commit)
1124 Editor* real_editor = dynamic_cast<Editor*> (&_editor);
1126 real_editor->begin_reversible_command (Operations::create_region);
1127 playlist()->clear_changes ();
1129 real_editor->snap_to (pos, 0);
1131 boost::shared_ptr<Source> src = _session->create_midi_source_for_session (view()->trackview().track().get(),
1132 view()->trackview().track()->name());
1135 plist.add (ARDOUR::Properties::start, 0);
1136 plist.add (ARDOUR::Properties::length, length);
1137 plist.add (ARDOUR::Properties::name, PBD::basename_nosuffix(src->name()));
1139 boost::shared_ptr<Region> region = (RegionFactory::create (src, plist));
1141 playlist()->add_region (region, pos);
1142 _session->add_command (new StatefulDiffCommand (playlist()));
1145 real_editor->commit_reversible_command ();
1148 return boost::dynamic_pointer_cast<MidiRegion>(region);
1152 MidiTimeAxisView::ensure_step_editor ()
1154 if (!_step_editor) {
1155 _step_editor = new StepEditor (_editor, midi_track(), *this);
1160 MidiTimeAxisView::start_step_editing ()
1162 ensure_step_editor ();
1163 _step_editor->start_step_editing ();
1167 MidiTimeAxisView::stop_step_editing ()
1170 _step_editor->stop_step_editing ();
1175 /** @return channel (counted from 0) to add an event to, based on the current setting
1176 * of the channel selector.
1179 MidiTimeAxisView::get_channel_for_add () const
1181 uint16_t const chn_mask = _channel_selector.get_selected_channels ();
1183 uint8_t channel = 0;
1185 /* pick the highest selected channel, unless all channels are selected,
1186 which is interpreted to mean channel 1 (zero)
1189 for (uint16_t i = 0; i < 16; ++i) {
1190 if (chn_mask & (1<<i)) {
1196 if (chn_cnt == 16) {
1204 MidiTimeAxisView::note_range_changed ()
1206 set_gui_property ("note-range-min", (int) midi_view()->lowest_note ());
1207 set_gui_property ("note-range-max", (int) midi_view()->highest_note ());