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/midi_playlist.h"
43 #include "ardour/midi_diskstream.h"
44 #include "ardour/midi_patch_manager.h"
45 #include "ardour/midi_source.h"
46 #include "ardour/processor.h"
47 #include "ardour/ladspa_plugin.h"
48 #include "ardour/location.h"
49 #include "ardour/playlist.h"
50 #include "ardour/region_factory.h"
51 #include "ardour/session.h"
52 #include "ardour/session_playlist.h"
53 #include "ardour/tempo.h"
54 #include "ardour/utils.h"
55 #include "ardour/operations.h"
57 #include "midi++/names.h"
59 #include "add_midi_cc_track_dialog.h"
60 #include "ardour_ui.h"
61 #include "automation_line.h"
62 #include "automation_time_axis.h"
63 #include "canvas-note-event.h"
64 #include "canvas_impl.h"
65 #include "crossfade_view.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,
103 boost::shared_ptr<Route> rt, Canvas& canvas)
104 : AxisView(sess) // virtually inherited
105 , RouteTimeAxisView(ed, sess, rt, canvas)
106 , _ignore_signals(false)
108 , _piano_roll_header(0)
109 , _note_mode(Sustained)
111 , _percussion_mode_item(0)
112 , _color_mode(MeterColors)
113 , _meter_color_mode_item(0)
114 , _channel_color_mode_item(0)
115 , _track_color_mode_item(0)
116 , _step_edit_item (0)
117 , _midi_thru_item (0)
118 , default_channel_menu (0)
119 , controller_menu (0)
122 subplugin_menu.set_name ("ArdourContextMenu");
124 _view = new MidiStreamView (*this);
126 ignore_toggle = false;
128 mute_button->set_active (false);
129 solo_button->set_active (false);
131 if (is_midi_track()) {
132 controls_ebox.set_name ("MidiTimeAxisViewControlsBaseUnselected");
133 _note_mode = midi_track()->note_mode();
134 } else { // MIDI bus (which doesn't exist yet..)
135 controls_ebox.set_name ("MidiBusControlsBaseUnselected");
138 /* map current state of the route */
140 processors_changed (RouteProcessorChange ());
144 set_state (*xml_node, Stateful::loading_state_version);
146 _route->processors_changed.connect (*this, invalidator (*this), ui_bind (&MidiTimeAxisView::processors_changed, this, _1), gui_context());
149 _piano_roll_header = new PianoRollHeader(*midi_view());
151 _piano_roll_header->AddNoteSelection.connect (sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection));
152 _piano_roll_header->ExtendNoteSelection.connect (sigc::mem_fun (*this, &MidiTimeAxisView::extend_note_selection));
153 _piano_roll_header->ToggleNoteSelection.connect (sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection));
155 _range_scroomer = new MidiScroomer(midi_view()->note_range_adjustment);
157 /* Suspend updates of the StreamView during scroomer drags to speed things up */
158 _range_scroomer->DragStarting.connect (sigc::mem_fun (*midi_view(), &MidiStreamView::suspend_updates));
159 _range_scroomer->DragFinishing.connect (sigc::mem_fun (*midi_view(), &MidiStreamView::resume_updates));
161 controls_hbox.pack_start(*_range_scroomer);
162 controls_hbox.pack_start(*_piano_roll_header);
164 controls_ebox.set_name ("MidiTrackControlsBaseUnselected");
165 controls_base_selected_name = "MidiTrackControlsBaseSelected";
166 controls_base_unselected_name = "MidiTrackControlsBaseUnselected";
168 midi_view()->NoteRangeChanged.connect (sigc::mem_fun(*this, &MidiTimeAxisView::update_range));
170 /* ask for notifications of any new RegionViews */
171 _view->RegionViewAdded.connect (sigc::mem_fun(*this, &MidiTimeAxisView::region_view_added));
173 if (!_editor.have_idled()) {
174 /* first idle will do what we need */
180 HBox* midi_controls_hbox = manage(new HBox());
182 MIDI::Name::MidiPatchManager& patch_manager = MIDI::Name::MidiPatchManager::instance();
184 MIDI::Name::MasterDeviceNames::Models::const_iterator m = patch_manager.all_models().begin();
185 for (; m != patch_manager.all_models().end(); ++m) {
186 _model_selector.append_text(m->c_str());
189 _model_selector.signal_changed().connect(sigc::mem_fun(*this, &MidiTimeAxisView::model_changed));
191 _custom_device_mode_selector.signal_changed().connect(
192 sigc::mem_fun(*this, &MidiTimeAxisView::custom_device_mode_changed));
194 // TODO: persist the choice
195 // this initializes the comboboxes and sends out the signal
196 _model_selector.set_active(0);
198 midi_controls_hbox->pack_start(_channel_selector, true, false);
199 if (!patch_manager.all_models().empty()) {
200 _midi_controls_box.pack_start(_model_selector, true, false);
201 _midi_controls_box.pack_start(_custom_device_mode_selector, true, false);
204 _midi_controls_box.pack_start(*midi_controls_hbox, true, true);
206 controls_vbox.pack_start(_midi_controls_box, false, false);
208 // restore channel selector settings
209 _channel_selector.set_channel_mode(midi_track()->get_channel_mode(), midi_track()->get_channel_mask());
210 _channel_selector.mode_changed.connect(
211 sigc::mem_fun(*midi_track(), &MidiTrack::set_channel_mode));
212 _channel_selector.mode_changed.connect(
213 sigc::mem_fun(*this, &MidiTimeAxisView::set_channel_mode));
216 if ((prop = xml_node->property ("color-mode")) != 0) {
217 _color_mode = ColorMode (string_2_enum(prop->value(), _color_mode));
218 if (_color_mode == ChannelColors) {
219 _channel_selector.set_channel_colors(CanvasNoteEvent::midi_channel_colors);
223 if ((prop = xml_node->property ("note-mode")) != 0) {
224 _note_mode = NoteMode (string_2_enum(prop->value(), _note_mode));
225 if (_percussion_mode_item) {
226 _percussion_mode_item->set_active (_note_mode == Percussive);
232 MidiTimeAxisView::first_idle ()
239 MidiTimeAxisView::~MidiTimeAxisView ()
241 delete _piano_roll_header;
242 _piano_roll_header = 0;
244 delete _range_scroomer;
247 delete controller_menu;
252 MidiTimeAxisView::enter_internal_edit_mode ()
255 midi_view()->enter_internal_edit_mode ();
260 MidiTimeAxisView::leave_internal_edit_mode ()
263 midi_view()->leave_internal_edit_mode ();
268 MidiTimeAxisView::check_step_edit ()
270 ensure_step_editor ();
271 _step_editor->check_step_edit ();
275 MidiTimeAxisView::model_changed()
277 std::list<std::string> device_modes = MIDI::Name::MidiPatchManager::instance()
278 .custom_device_mode_names_by_model(_model_selector.get_active_text());
280 _custom_device_mode_selector.clear_items();
282 for (std::list<std::string>::const_iterator i = device_modes.begin();
283 i != device_modes.end(); ++i) {
284 cerr << "found custom device mode " << *i << " thread_id: " << pthread_self() << endl;
285 _custom_device_mode_selector.append_text(*i);
288 _custom_device_mode_selector.set_active(0);
291 void MidiTimeAxisView::custom_device_mode_changed()
293 _midi_patch_settings_changed.emit(_model_selector.get_active_text(),
294 _custom_device_mode_selector.get_active_text());
298 MidiTimeAxisView::midi_view()
300 return dynamic_cast<MidiStreamView*>(_view);
304 MidiTimeAxisView::show_at (double y, int& nth, Gtk::VBox *parent)
307 xml_node->add_property ("shown-editor", "yes");
309 guint32 ret = TimeAxisView::show_at (y, nth, parent);
314 MidiTimeAxisView::hide ()
317 xml_node->add_property ("shown-editor", "no");
319 TimeAxisView::hide ();
323 MidiTimeAxisView::set_height (uint32_t h)
325 RouteTimeAxisView::set_height (h);
327 if (height >= MIDI_CONTROLS_BOX_MIN_HEIGHT) {
328 _midi_controls_box.show_all ();
330 _midi_controls_box.hide();
333 if (height >= KEYBOARD_MIN_HEIGHT) {
334 if (is_track() && _range_scroomer)
335 _range_scroomer->show();
336 if (is_track() && _piano_roll_header)
337 _piano_roll_header->show();
339 if (is_track() && _range_scroomer)
340 _range_scroomer->hide();
341 if (is_track() && _piano_roll_header)
342 _piano_roll_header->hide();
347 MidiTimeAxisView::append_extra_display_menu_items ()
349 using namespace Menu_Helpers;
351 MenuList& items = display_menu->items();
354 Menu *range_menu = manage(new Menu);
355 MenuList& range_items = range_menu->items();
356 range_menu->set_name ("ArdourContextMenu");
358 range_items.push_back (MenuElem (_("Show Full Range"), sigc::bind (
359 sigc::mem_fun(*this, &MidiTimeAxisView::set_note_range),
360 MidiStreamView::FullRange)));
362 range_items.push_back (MenuElem (_("Fit Contents"), sigc::bind (
363 sigc::mem_fun(*this, &MidiTimeAxisView::set_note_range),
364 MidiStreamView::ContentsRange)));
366 items.push_back (MenuElem (_("Note range"), *range_menu));
367 items.push_back (MenuElem (_("Note mode"), *build_note_mode_menu()));
368 items.push_back (MenuElem (_("Default Channel"), *build_def_channel_menu()));
370 items.push_back (CheckMenuElem (_("MIDI Thru"), sigc::mem_fun(*this, &MidiTimeAxisView::toggle_midi_thru)));
371 _midi_thru_item = dynamic_cast<CheckMenuItem*>(&items.back());
373 items.push_back (SeparatorElem ());
377 MidiTimeAxisView::build_def_channel_menu ()
379 using namespace Menu_Helpers;
381 default_channel_menu = manage (new Menu ());
383 uint8_t defchn = midi_track()->default_channel();
384 MenuList& def_channel_items = default_channel_menu->items();
386 RadioMenuItem::Group dc_group;
388 for (int i = 0; i < 16; ++i) {
390 snprintf (buf, sizeof (buf), "%d", i+1);
392 def_channel_items.push_back (RadioMenuElem (dc_group, buf,
393 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_default_channel), i)));
394 item = dynamic_cast<RadioMenuItem*>(&def_channel_items.back());
395 item->set_active ((i == defchn));
398 return default_channel_menu;
402 MidiTimeAxisView::set_default_channel (int chn)
404 midi_track()->set_default_channel (chn);
408 MidiTimeAxisView::toggle_midi_thru ()
410 if (!_midi_thru_item) {
414 bool view_yn = _midi_thru_item->get_active();
415 if (view_yn != midi_track()->midi_thru()) {
416 midi_track()->set_midi_thru (view_yn);
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)));
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)));
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), MeterColors)));
763 _meter_color_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
764 _meter_color_mode_item->set_active(_color_mode == MeterColors);
766 items.push_back (RadioMenuElem (mode_group, _("Channel Colors"),
767 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode), ChannelColors)));
768 _channel_color_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
769 _channel_color_mode_item->set_active(_color_mode == ChannelColors);
771 items.push_back (RadioMenuElem (mode_group, _("Track Color"),
772 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode), TrackColor)));
773 _channel_color_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
774 _channel_color_mode_item->set_active(_color_mode == TrackColor);
780 MidiTimeAxisView::set_note_mode(NoteMode mode)
782 if (_note_mode != mode || midi_track()->note_mode() != mode) {
784 midi_track()->set_note_mode(mode);
785 xml_node->add_property ("note-mode", enum_2_string(_note_mode));
786 _view->redisplay_track();
791 MidiTimeAxisView::set_color_mode(ColorMode mode)
793 if (_color_mode != mode) {
794 if (mode == ChannelColors) {
795 _channel_selector.set_channel_colors(CanvasNoteEvent::midi_channel_colors);
797 _channel_selector.set_default_channel_color();
801 xml_node->add_property ("color-mode", enum_2_string(_color_mode));
802 _view->redisplay_track();
807 MidiTimeAxisView::set_note_range(MidiStreamView::VisibleNoteRange range)
809 if (!_ignore_signals)
810 midi_view()->set_note_range(range);
815 MidiTimeAxisView::update_range()
817 MidiGhostRegion* mgr;
819 for(list<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
820 if ((mgr = dynamic_cast<MidiGhostRegion*>(*i)) != 0) {
827 MidiTimeAxisView::show_all_automation (bool apply_to_selection)
829 if (apply_to_selection) {
830 _editor.get_selection().tracks.foreach_midi_time_axis (boost::bind (&MidiTimeAxisView::show_all_automation, _1, false));
833 const set<Evoral::Parameter> params = midi_track()->midi_playlist()->contained_automation();
835 for (set<Evoral::Parameter>::const_iterator i = params.begin(); i != params.end(); ++i) {
836 create_automation_child(*i, true);
840 RouteTimeAxisView::show_all_automation ();
845 MidiTimeAxisView::show_existing_automation (bool apply_to_selection)
847 if (apply_to_selection) {
848 _editor.get_selection().tracks.foreach_midi_time_axis (boost::bind (&MidiTimeAxisView::show_existing_automation, _1, false));
851 const set<Evoral::Parameter> params = midi_track()->midi_playlist()->contained_automation();
853 for (set<Evoral::Parameter>::const_iterator i = params.begin(); i != params.end(); ++i) {
854 create_automation_child(*i, true);
858 RouteTimeAxisView::show_existing_automation ();
862 /** Create an automation track for the given parameter (pitch bend, channel pressure).
865 MidiTimeAxisView::create_automation_child (const Evoral::Parameter& param, bool show)
867 if (param.type() == NullAutomation) {
868 cerr << "WARNING: Attempt to create NullAutomation child, ignoring" << endl;
872 AutomationTracks::iterator existing = _automation_tracks.find (param);
873 if (existing != _automation_tracks.end()) {
877 if (param.type() == GainAutomation) {
878 create_gain_automation_child (param, show);
881 /* These controllers are region "automation", so we do not create
882 * an AutomationList/Line for the track */
884 boost::shared_ptr<AutomationTimeAxisView> track (
885 new AutomationTimeAxisView (
888 boost::shared_ptr<Automatable> (),
889 boost::shared_ptr<AutomationControl> (),
895 _route->describe_parameter(param)
900 _view->foreach_regionview (sigc::mem_fun (*track.get(), &TimeAxisView::add_ghost));
903 add_automation_child (param, track, show);
909 MidiTimeAxisView::route_active_changed ()
911 RouteUI::route_active_changed ();
914 if (_route->active()) {
915 controls_ebox.set_name ("MidiTrackControlsBaseUnselected");
916 controls_base_selected_name = "MidiTrackControlsBaseSelected";
917 controls_base_unselected_name = "MidiTrackControlsBaseUnselected";
919 controls_ebox.set_name ("MidiTrackControlsBaseInactiveUnselected");
920 controls_base_selected_name = "MidiTrackControlsBaseInactiveSelected";
921 controls_base_unselected_name = "MidiTrackControlsBaseInactiveUnselected";
927 if (_route->active()) {
928 controls_ebox.set_name ("BusControlsBaseUnselected");
929 controls_base_selected_name = "BusControlsBaseSelected";
930 controls_base_unselected_name = "BusControlsBaseUnselected";
932 controls_ebox.set_name ("BusControlsBaseInactiveUnselected");
933 controls_base_selected_name = "BusControlsBaseInactiveSelected";
934 controls_base_unselected_name = "BusControlsBaseInactiveUnselected";
942 MidiTimeAxisView::add_note_selection (uint8_t note)
944 if (!_editor.internal_editing()) {
948 uint16_t chn_mask = _channel_selector.get_selected_channels();
950 if (_view->num_selected_regionviews() == 0) {
951 _view->foreach_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection_region_view), note, chn_mask));
953 _view->foreach_selected_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection_region_view), note, chn_mask));
958 MidiTimeAxisView::extend_note_selection (uint8_t note)
960 if (!_editor.internal_editing()) {
964 uint16_t chn_mask = _channel_selector.get_selected_channels();
966 if (_view->num_selected_regionviews() == 0) {
967 _view->foreach_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::extend_note_selection_region_view), note, chn_mask));
969 _view->foreach_selected_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::extend_note_selection_region_view), note, chn_mask));
974 MidiTimeAxisView::toggle_note_selection (uint8_t note)
976 if (!_editor.internal_editing()) {
980 uint16_t chn_mask = _channel_selector.get_selected_channels();
982 if (_view->num_selected_regionviews() == 0) {
983 _view->foreach_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection_region_view), note, chn_mask));
985 _view->foreach_selected_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection_region_view), note, chn_mask));
990 MidiTimeAxisView::add_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
992 dynamic_cast<MidiRegionView*>(rv)->select_matching_notes (note, chn_mask, false, false);
996 MidiTimeAxisView::extend_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
998 dynamic_cast<MidiRegionView*>(rv)->select_matching_notes (note, chn_mask, true, true);
1002 MidiTimeAxisView::toggle_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1004 dynamic_cast<MidiRegionView*>(rv)->toggle_matching_notes (note, chn_mask);
1008 MidiTimeAxisView::set_channel_mode (ChannelMode, uint16_t)
1010 /* hide all automation tracks that use the wrong channel(s) and show all those that use
1014 uint16_t selected_channels = _channel_selector.get_selected_channels();
1015 bool changed = false;
1019 for (uint32_t ctl = 0; ctl < 127; ++ctl) {
1021 for (uint32_t chn = 0; chn < 16; ++chn) {
1022 Evoral::Parameter fully_qualified_param (MidiCCAutomation, chn, ctl);
1023 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
1029 if ((selected_channels & (0x0001 << chn)) == 0) {
1030 /* channel not in use. hiding it will trigger RouteTimeAxisView::automation_track_hidden()
1031 which will cause a redraw. We don't want one per channel, so block that with no_redraw.
1033 changed = track->set_visibility (false) || changed;
1035 changed = track->set_visibility (true) || changed;
1042 /* TODO: Bender, Pressure */
1044 /* invalidate the controller menu, so that we rebuild it next time */
1045 _controller_menu_map.clear ();
1046 delete controller_menu;
1047 controller_menu = 0;
1050 _route->gui_changed ("track_height", this);
1055 MidiTimeAxisView::automation_child_menu_item (Evoral::Parameter param)
1057 Gtk::CheckMenuItem* m = RouteTimeAxisView::automation_child_menu_item (param);
1062 ParameterMenuMap::iterator i = _controller_menu_map.find (param);
1063 if (i != _controller_menu_map.end()) {
1067 i = _channel_command_menu_map.find (param);
1068 if (i != _channel_command_menu_map.end()) {
1075 boost::shared_ptr<MidiRegion>
1076 MidiTimeAxisView::add_region (framepos_t pos, framecnt_t length, bool commit)
1078 Editor* real_editor = dynamic_cast<Editor*> (&_editor);
1080 real_editor->begin_reversible_command (Operations::create_region);
1081 playlist()->clear_changes ();
1083 real_editor->snap_to (pos, 0);
1085 boost::shared_ptr<Source> src = _session->create_midi_source_for_session (view()->trackview().track().get(),
1086 view()->trackview().track()->name());
1089 plist.add (ARDOUR::Properties::start, 0);
1090 plist.add (ARDOUR::Properties::length, length);
1091 plist.add (ARDOUR::Properties::name, PBD::basename_nosuffix(src->name()));
1093 boost::shared_ptr<Region> region = (RegionFactory::create (src, plist));
1095 playlist()->add_region (region, pos);
1096 _session->add_command (new StatefulDiffCommand (playlist()));
1099 real_editor->commit_reversible_command ();
1102 return boost::dynamic_pointer_cast<MidiRegion>(region);
1106 MidiTimeAxisView::ensure_step_editor ()
1108 if (!_step_editor) {
1109 _step_editor = new StepEditor (_editor, midi_track(), *this);
1114 MidiTimeAxisView::start_step_editing ()
1116 ensure_step_editor ();
1117 _step_editor->start_step_editing ();
1121 MidiTimeAxisView::stop_step_editing ()
1124 _step_editor->stop_step_editing ();