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 , _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), 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 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());
203 _model_selector.signal_changed().connect(sigc::mem_fun(*this, &MidiTimeAxisView::model_changed));
205 _custom_device_mode_selector.signal_changed().connect(
206 sigc::mem_fun(*this, &MidiTimeAxisView::custom_device_mode_changed));
208 _model_selector.set_active_text (gui_property (X_("midnam-model-name")));
209 _custom_device_mode_selector.set_active_text (gui_property (X_("midnam-custom-device-mode")));
211 ARDOUR_UI::instance()->set_tip (_model_selector, _("External MIDI Device"));
212 ARDOUR_UI::instance()->set_tip (_custom_device_mode_selector, _("External Device Mode"));
214 midi_controls_hbox->pack_start(_channel_selector, true, false);
215 if (!patch_manager.all_models().empty()) {
216 _midi_controls_box.set_border_width (5);
217 _midi_controls_box.pack_start(_model_selector, true, false);
218 _midi_controls_box.pack_start(_custom_device_mode_selector, true, false);
221 _midi_controls_box.pack_start(*midi_controls_hbox, true, true);
223 controls_vbox.pack_start(_midi_controls_box, false, false);
225 // restore channel selector settings
226 _channel_selector.set_channel_mode(midi_track()->get_channel_mode(), midi_track()->get_channel_mask());
227 _channel_selector.mode_changed.connect(
228 sigc::mem_fun(*midi_track(), &MidiTrack::set_channel_mode));
229 _channel_selector.mode_changed.connect(
230 sigc::mem_fun(*this, &MidiTimeAxisView::set_channel_mode));
232 string prop = gui_property ("color-mode");
234 _color_mode = ColorMode (string_2_enum(prop, _color_mode));
235 if (_color_mode == ChannelColors) {
236 _channel_selector.set_channel_colors(CanvasNoteEvent::midi_channel_colors);
240 set_color_mode (_color_mode, true, false);
242 prop = gui_property ("note-mode");
244 _note_mode = NoteMode (string_2_enum (prop, _note_mode));
245 if (_percussion_mode_item) {
246 _percussion_mode_item->set_active (_note_mode == Percussive);
250 /* Look for any GUI object state nodes that represent automation children that should exist, and create
254 list<string> gui_ids = gui_object_state().all_ids ();
255 for (list<string>::const_iterator i = gui_ids.begin(); i != gui_ids.end(); ++i) {
258 Evoral::Parameter parameter (0, 0, 0);
260 bool const p = AutomationTimeAxisView::parse_state_id (*i, route_id, has_parameter, parameter);
261 if (p && route_id == _route->id () && has_parameter) {
262 create_automation_child (parameter, string_is_affirmative (gui_object_state().get_string (*i, X_("visible"))));
268 MidiTimeAxisView::first_idle ()
275 MidiTimeAxisView::~MidiTimeAxisView ()
277 delete _piano_roll_header;
278 _piano_roll_header = 0;
280 delete _range_scroomer;
283 delete controller_menu;
288 MidiTimeAxisView::enter_internal_edit_mode ()
291 midi_view()->enter_internal_edit_mode ();
296 MidiTimeAxisView::leave_internal_edit_mode ()
299 midi_view()->leave_internal_edit_mode ();
304 MidiTimeAxisView::check_step_edit ()
306 ensure_step_editor ();
307 _step_editor->check_step_edit ();
311 MidiTimeAxisView::model_changed()
313 std::list<std::string> device_modes = MIDI::Name::MidiPatchManager::instance()
314 .custom_device_mode_names_by_model(_model_selector.get_active_text());
316 _custom_device_mode_selector.clear_items();
318 for (std::list<std::string>::const_iterator i = device_modes.begin();
319 i != device_modes.end(); ++i) {
320 _custom_device_mode_selector.append_text(*i);
323 _custom_device_mode_selector.set_active(0);
325 _route->instrument_info().set_external_instrument (_model_selector.get_active_text(), _custom_device_mode_selector.get_active_text());
329 MidiTimeAxisView::custom_device_mode_changed()
331 _route->instrument_info().set_external_instrument (_model_selector.get_active_text(), _custom_device_mode_selector.get_active_text());
335 MidiTimeAxisView::midi_view()
337 return dynamic_cast<MidiStreamView*>(_view);
341 MidiTimeAxisView::set_height (uint32_t h)
343 if (h >= MIDI_CONTROLS_BOX_MIN_HEIGHT) {
344 _midi_controls_box.show_all ();
346 _midi_controls_box.hide();
349 if (h >= KEYBOARD_MIN_HEIGHT) {
350 if (is_track() && _range_scroomer) {
351 _range_scroomer->show();
353 if (is_track() && _piano_roll_header) {
354 _piano_roll_header->show();
357 if (is_track() && _range_scroomer) {
358 _range_scroomer->hide();
360 if (is_track() && _piano_roll_header) {
361 _piano_roll_header->hide();
365 /* We need to do this after changing visibility of our stuff, as it will
366 eventually trigger a call to Editor::reset_controls_layout_width(),
367 which needs to know if we have just shown or hidden a scroomer /
370 RouteTimeAxisView::set_height (h);
374 MidiTimeAxisView::append_extra_display_menu_items ()
376 using namespace Menu_Helpers;
378 MenuList& items = display_menu->items();
381 Menu *range_menu = manage(new Menu);
382 MenuList& range_items = range_menu->items();
383 range_menu->set_name ("ArdourContextMenu");
385 range_items.push_back (MenuElem (_("Show Full Range"), sigc::bind (
386 sigc::mem_fun(*this, &MidiTimeAxisView::set_note_range),
387 MidiStreamView::FullRange)));
389 range_items.push_back (MenuElem (_("Fit Contents"), sigc::bind (
390 sigc::mem_fun(*this, &MidiTimeAxisView::set_note_range),
391 MidiStreamView::ContentsRange)));
393 items.push_back (MenuElem (_("Note Range"), *range_menu));
394 items.push_back (MenuElem (_("Note Mode"), *build_note_mode_menu()));
396 items.push_back (CheckMenuElem (_("MIDI Thru"), sigc::mem_fun(*this, &MidiTimeAxisView::toggle_midi_thru)));
397 _midi_thru_item = dynamic_cast<CheckMenuItem*>(&items.back());
399 items.push_back (SeparatorElem ());
403 MidiTimeAxisView::toggle_midi_thru ()
405 if (!_midi_thru_item) {
409 bool view_yn = _midi_thru_item->get_active();
410 if (view_yn != midi_track()->midi_thru()) {
411 midi_track()->set_midi_thru (view_yn);
416 MidiTimeAxisView::build_automation_action_menu (bool for_selection)
418 using namespace Menu_Helpers;
420 /* If we have a controller menu, we need to detach it before
421 RouteTimeAxis::build_automation_action_menu destroys the
422 menu it is attached to. Otherwise GTK destroys
423 controller_menu's gobj, meaning that it can't be reattached
424 below. See bug #3134.
427 if (controller_menu) {
428 detach_menu (*controller_menu);
431 _channel_command_menu_map.clear ();
432 RouteTimeAxisView::build_automation_action_menu (for_selection);
434 MenuList& automation_items = automation_action_menu->items();
436 uint16_t selected_channels = _channel_selector.get_selected_channels();
438 if (selected_channels != 0) {
440 automation_items.push_back (SeparatorElem());
442 /* these 2 MIDI "command" types are semantically more like automation than note data,
443 but they are not MIDI controllers. We give them special status in this menu, since
444 they will not show up in the controller list and anyone who actually knows
445 something about MIDI (!) would not expect to find them there.
448 add_channel_command_menu_item (automation_items, _("Bender"), MidiPitchBenderAutomation, 0);
449 automation_items.back().set_sensitive (!for_selection || _editor.get_selection().tracks.size() == 1);
450 add_channel_command_menu_item (automation_items, _("Pressure"), MidiChannelPressureAutomation, 0);
451 automation_items.back().set_sensitive (!for_selection || _editor.get_selection().tracks.size() == 1);
453 /* now all MIDI controllers. Always offer the possibility that we will rebuild the controllers menu
454 since it might need to be updated after a channel mode change or other change. Also detach it
455 first in case it has been used anywhere else.
458 build_controller_menu ();
460 automation_items.push_back (SeparatorElem());
461 automation_items.push_back (MenuElem (_("Controllers"), *controller_menu));
462 automation_items.back().set_sensitive (!for_selection || _editor.get_selection().tracks.size() == 1);
464 automation_items.push_back (MenuElem (string_compose ("<i>%1</i>", _("No MIDI Channels selected"))));
465 dynamic_cast<Label*> (automation_items.back().get_child())->set_use_markup (true);
471 MidiTimeAxisView::change_all_channel_tracks_visibility (bool yn, Evoral::Parameter param)
473 uint16_t selected_channels = _channel_selector.get_selected_channels();
475 for (uint8_t chn = 0; chn < 16; chn++) {
476 if (selected_channels & (0x0001 << chn)) {
478 Evoral::Parameter fully_qualified_param (param.type(), chn, param.id());
479 Gtk::CheckMenuItem* menu = automation_child_menu_item (fully_qualified_param);
482 menu->set_active (yn);
489 MidiTimeAxisView::add_channel_command_menu_item (Menu_Helpers::MenuList& items, const string& label, AutomationType auto_type, uint8_t cmd)
491 using namespace Menu_Helpers;
493 /* count the number of selected channels because we will build a different menu structure if there is more than 1 selected.
496 uint16_t selected_channels = _channel_selector.get_selected_channels();
499 for (uint8_t chn = 0; chn < 16; chn++) {
500 if (selected_channels & (0x0001 << chn)) {
509 /* multiple channels - create a submenu, with 1 item per channel */
511 Menu* chn_menu = manage (new Menu);
512 MenuList& chn_items (chn_menu->items());
513 Evoral::Parameter param_without_channel (auto_type, 0, cmd);
515 /* add a couple of items to hide/show all of them */
517 chn_items.push_back (MenuElem (_("Hide all channels"),
518 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
519 false, param_without_channel)));
520 chn_items.push_back (MenuElem (_("Show all channels"),
521 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
522 true, param_without_channel)));
524 for (uint8_t chn = 0; chn < 16; chn++) {
525 if (selected_channels & (0x0001 << chn)) {
527 /* for each selected channel, add a menu item for this controller */
529 Evoral::Parameter fully_qualified_param (auto_type, chn, cmd);
530 chn_items.push_back (CheckMenuElem (string_compose (_("Channel %1"), chn+1),
531 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
532 fully_qualified_param)));
534 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
535 bool visible = false;
538 if (track->marked_for_display()) {
543 CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&chn_items.back());
544 _channel_command_menu_map[fully_qualified_param] = cmi;
545 cmi->set_active (visible);
549 /* now create an item in the parent menu that has the per-channel list as a submenu */
551 items.push_back (MenuElem (label, *chn_menu));
555 /* just one channel - create a single menu item for this command+channel combination*/
557 for (uint8_t chn = 0; chn < 16; chn++) {
558 if (selected_channels & (0x0001 << chn)) {
560 Evoral::Parameter fully_qualified_param (auto_type, chn, cmd);
561 items.push_back (CheckMenuElem (label,
562 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
563 fully_qualified_param)));
565 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
566 bool visible = false;
569 if (track->marked_for_display()) {
574 CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&items.back());
575 _channel_command_menu_map[fully_qualified_param] = cmi;
576 cmi->set_active (visible);
578 /* one channel only */
586 MidiTimeAxisView::build_controller_menu ()
588 using namespace Menu_Helpers;
590 if (controller_menu) {
591 /* it exists and has not been invalidated by a channel mode change, so just return it */
595 controller_menu = new Menu; // explicitly managed by us
596 MenuList& items (controller_menu->items());
598 /* create several "top level" menu items for sets of controllers (16 at a time), and populate each one with a submenu
599 for each controller+channel combination covering the currently selected channels for this track
602 uint16_t selected_channels = _channel_selector.get_selected_channels();
604 /* count the number of selected channels because we will build a different menu structure if there is more than 1 selected.
609 for (uint8_t chn = 0; chn < 16; chn++) {
610 if (selected_channels & (0x0001 << chn)) {
617 /* loop over all 127 MIDI controllers, in groups of 16; except don't offer
618 bank select controllers, as they are handled by the `patch' code */
620 for (int i = 0; i < 127; i += 16) {
622 Menu* ctl_menu = manage (new Menu);
623 MenuList& ctl_items (ctl_menu->items());
626 /* for each controller, consider whether to create a submenu or a single item */
628 for (int ctl = i; ctl < i+16; ++ctl) {
630 if (ctl == MIDI_CTL_MSB_BANK || ctl == MIDI_CTL_LSB_BANK) {
636 /* multiple channels - create a submenu, with 1 item per channel */
638 Menu* chn_menu = manage (new Menu);
639 MenuList& chn_items (chn_menu->items());
641 /* add a couple of items to hide/show this controller on all channels */
643 Evoral::Parameter param_without_channel (MidiCCAutomation, 0, ctl);
644 chn_items.push_back (MenuElem (_("Hide all channels"),
645 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
646 false, param_without_channel)));
647 chn_items.push_back (MenuElem (_("Show all channels"),
648 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
649 true, param_without_channel)));
651 for (uint8_t chn = 0; chn < 16; chn++) {
652 if (selected_channels & (0x0001 << chn)) {
654 /* for each selected channel, add a menu item for this controller */
656 Evoral::Parameter fully_qualified_param (MidiCCAutomation, chn, ctl);
657 chn_items.push_back (CheckMenuElem (string_compose (_("Channel %1"), chn+1),
658 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
659 fully_qualified_param)));
661 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
662 bool visible = false;
665 if (track->marked_for_display()) {
670 CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&chn_items.back());
671 _controller_menu_map[fully_qualified_param] = cmi;
672 cmi->set_active (visible);
676 /* add the per-channel menu to the list of controllers, with the name of the controller */
677 ctl_items.push_back (MenuElem (string_compose ("<b>%1</b>: %2", ctl, midi_name (ctl)), *chn_menu));
678 dynamic_cast<Label*> (ctl_items.back().get_child())->set_use_markup (true);
682 /* just one channel - create a single menu item for this ctl+channel combination*/
684 for (uint8_t chn = 0; chn < 16; chn++) {
685 if (selected_channels & (0x0001 << chn)) {
687 Evoral::Parameter fully_qualified_param (MidiCCAutomation, chn, ctl);
688 ctl_items.push_back (
690 string_compose ("<b>%1</b>: %2 [%3]", ctl, midi_name (ctl), int (chn)),
691 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
692 fully_qualified_param)
695 dynamic_cast<Label*> (ctl_items.back().get_child())->set_use_markup (true);
697 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
698 bool visible = false;
701 if (track->marked_for_display()) {
706 CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&ctl_items.back());
707 _controller_menu_map[fully_qualified_param] = cmi;
708 cmi->set_active (visible);
710 /* one channel only */
717 /* add the menu for this block of controllers to the overall controller menu */
719 items.push_back (MenuElem (string_compose (_("Controllers %1-%2"), i, i+15), *ctl_menu));
724 MidiTimeAxisView::build_note_mode_menu()
726 using namespace Menu_Helpers;
728 Menu* mode_menu = manage (new Menu);
729 MenuList& items = mode_menu->items();
730 mode_menu->set_name ("ArdourContextMenu");
732 RadioMenuItem::Group mode_group;
733 items.push_back (RadioMenuElem (mode_group, _("Sustained"),
734 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_mode), Sustained)));
735 _note_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
736 _note_mode_item->set_active(_note_mode == Sustained);
738 items.push_back (RadioMenuElem (mode_group, _("Percussive"),
739 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_mode), Percussive)));
740 _percussion_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
741 _percussion_mode_item->set_active(_note_mode == Percussive);
747 MidiTimeAxisView::build_color_mode_menu()
749 using namespace Menu_Helpers;
751 Menu* mode_menu = manage (new Menu);
752 MenuList& items = mode_menu->items();
753 mode_menu->set_name ("ArdourContextMenu");
755 RadioMenuItem::Group mode_group;
756 items.push_back (RadioMenuElem (mode_group, _("Meter Colors"),
757 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode),
758 MeterColors, false, true)));
759 _meter_color_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
760 _meter_color_mode_item->set_active(_color_mode == MeterColors);
762 items.push_back (RadioMenuElem (mode_group, _("Channel Colors"),
763 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode),
764 ChannelColors, false, true)));
765 _channel_color_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
766 _channel_color_mode_item->set_active(_color_mode == ChannelColors);
768 items.push_back (RadioMenuElem (mode_group, _("Track Color"),
769 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode),
770 TrackColor, false, true)));
771 _channel_color_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
772 _channel_color_mode_item->set_active(_color_mode == TrackColor);
778 MidiTimeAxisView::set_note_mode(NoteMode mode)
780 if (_note_mode != mode || midi_track()->note_mode() != mode) {
782 midi_track()->set_note_mode(mode);
783 set_gui_property ("note-mode", enum_2_string(_note_mode));
784 _view->redisplay_track();
789 MidiTimeAxisView::set_color_mode (ColorMode mode, bool force, bool redisplay)
791 if (_color_mode == mode && !force) {
795 if (mode == ChannelColors) {
796 _channel_selector.set_channel_colors(CanvasNoteEvent::midi_channel_colors);
798 _channel_selector.set_default_channel_color();
802 set_gui_property ("color-mode", enum_2_string(_color_mode));
804 _view->redisplay_track();
809 MidiTimeAxisView::set_note_range(MidiStreamView::VisibleNoteRange range)
811 if (!_ignore_signals)
812 midi_view()->set_note_range(range);
817 MidiTimeAxisView::update_range()
819 MidiGhostRegion* mgr;
821 for(list<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
822 if ((mgr = dynamic_cast<MidiGhostRegion*>(*i)) != 0) {
829 MidiTimeAxisView::show_all_automation (bool apply_to_selection)
831 if (apply_to_selection) {
832 _editor.get_selection().tracks.foreach_midi_time_axis (boost::bind (&MidiTimeAxisView::show_all_automation, _1, false));
835 const set<Evoral::Parameter> params = midi_track()->midi_playlist()->contained_automation();
837 for (set<Evoral::Parameter>::const_iterator i = params.begin(); i != params.end(); ++i) {
838 create_automation_child(*i, true);
842 RouteTimeAxisView::show_all_automation ();
847 MidiTimeAxisView::show_existing_automation (bool apply_to_selection)
849 if (apply_to_selection) {
850 _editor.get_selection().tracks.foreach_midi_time_axis (boost::bind (&MidiTimeAxisView::show_existing_automation, _1, false));
853 const set<Evoral::Parameter> params = midi_track()->midi_playlist()->contained_automation();
855 for (set<Evoral::Parameter>::const_iterator i = params.begin(); i != params.end(); ++i) {
856 create_automation_child (*i, true);
860 RouteTimeAxisView::show_existing_automation ();
864 /** Create an automation track for the given parameter (pitch bend, channel pressure).
867 MidiTimeAxisView::create_automation_child (const Evoral::Parameter& param, bool show)
869 if (param.type() == NullAutomation) {
873 AutomationTracks::iterator existing = _automation_tracks.find (param);
875 if (existing != _automation_tracks.end()) {
877 /* automation track created because we had existing data for
878 * the processor, but visibility may need to be controlled
879 * since it will have been set visible by default.
882 if (existing->second->set_marked_for_display (show) && !no_redraw) {
889 boost::shared_ptr<AutomationTimeAxisView> track;
891 switch (param.type()) {
894 create_gain_automation_child (param, show);
897 case PluginAutomation:
898 /* handled elsewhere */
901 case MidiCCAutomation:
902 case MidiPgmChangeAutomation:
903 case MidiPitchBenderAutomation:
904 case MidiChannelPressureAutomation:
905 case MidiSystemExclusiveAutomation:
906 /* These controllers are region "automation" - they are owned
907 * by regions (and their MidiModels), not by the track. As a
908 * result we do not create an AutomationList/Line for the track
909 * ... except here we are doing something!! XXX
912 track.reset (new AutomationTimeAxisView (
915 boost::shared_ptr<Automatable> (),
916 boost::shared_ptr<AutomationControl> (),
922 _route->describe_parameter(param)
926 _view->foreach_regionview (sigc::mem_fun (*track.get(), &TimeAxisView::add_ghost));
929 add_automation_child (param, track, show);
933 error << "MidiTimeAxisView: unknown automation child " << EventTypeMap::instance().to_symbol(param) << endmsg;
938 MidiTimeAxisView::route_active_changed ()
940 RouteUI::route_active_changed ();
943 if (_route->active()) {
944 controls_ebox.set_name ("MidiTrackControlsBaseUnselected");
945 controls_base_selected_name = "MidiTrackControlsBaseSelected";
946 controls_base_unselected_name = "MidiTrackControlsBaseUnselected";
948 controls_ebox.set_name ("MidiTrackControlsBaseInactiveUnselected");
949 controls_base_selected_name = "MidiTrackControlsBaseInactiveSelected";
950 controls_base_unselected_name = "MidiTrackControlsBaseInactiveUnselected";
956 if (_route->active()) {
957 controls_ebox.set_name ("BusControlsBaseUnselected");
958 controls_base_selected_name = "BusControlsBaseSelected";
959 controls_base_unselected_name = "BusControlsBaseUnselected";
961 controls_ebox.set_name ("BusControlsBaseInactiveUnselected");
962 controls_base_selected_name = "BusControlsBaseInactiveSelected";
963 controls_base_unselected_name = "BusControlsBaseInactiveUnselected";
969 MidiTimeAxisView::set_note_selection (uint8_t note)
971 if (!_editor.internal_editing()) {
975 uint16_t chn_mask = _channel_selector.get_selected_channels();
977 if (_view->num_selected_regionviews() == 0) {
978 _view->foreach_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_selection_region_view), note, chn_mask));
980 _view->foreach_selected_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_selection_region_view), note, chn_mask));
985 MidiTimeAxisView::add_note_selection (uint8_t note)
987 if (!_editor.internal_editing()) {
991 uint16_t chn_mask = _channel_selector.get_selected_channels();
993 if (_view->num_selected_regionviews() == 0) {
994 _view->foreach_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection_region_view), note, chn_mask));
996 _view->foreach_selected_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection_region_view), note, chn_mask));
1001 MidiTimeAxisView::extend_note_selection (uint8_t note)
1003 if (!_editor.internal_editing()) {
1007 uint16_t chn_mask = _channel_selector.get_selected_channels();
1009 if (_view->num_selected_regionviews() == 0) {
1010 _view->foreach_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::extend_note_selection_region_view), note, chn_mask));
1012 _view->foreach_selected_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::extend_note_selection_region_view), note, chn_mask));
1017 MidiTimeAxisView::toggle_note_selection (uint8_t note)
1019 if (!_editor.internal_editing()) {
1023 uint16_t chn_mask = _channel_selector.get_selected_channels();
1025 if (_view->num_selected_regionviews() == 0) {
1026 _view->foreach_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection_region_view), note, chn_mask));
1028 _view->foreach_selected_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection_region_view), note, chn_mask));
1033 MidiTimeAxisView::set_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1035 dynamic_cast<MidiRegionView*>(rv)->select_matching_notes (note, chn_mask, false, false);
1039 MidiTimeAxisView::add_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1041 dynamic_cast<MidiRegionView*>(rv)->select_matching_notes (note, chn_mask, true, false);
1045 MidiTimeAxisView::extend_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1047 dynamic_cast<MidiRegionView*>(rv)->select_matching_notes (note, chn_mask, true, true);
1051 MidiTimeAxisView::toggle_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1053 dynamic_cast<MidiRegionView*>(rv)->toggle_matching_notes (note, chn_mask);
1057 MidiTimeAxisView::set_channel_mode (ChannelMode, uint16_t)
1059 /* hide all automation tracks that use the wrong channel(s) and show all those that use
1063 uint16_t selected_channels = _channel_selector.get_selected_channels();
1064 bool changed = false;
1068 for (uint32_t ctl = 0; ctl < 127; ++ctl) {
1070 for (uint32_t chn = 0; chn < 16; ++chn) {
1071 Evoral::Parameter fully_qualified_param (MidiCCAutomation, chn, ctl);
1072 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
1078 if ((selected_channels & (0x0001 << chn)) == 0) {
1079 /* channel not in use. hiding it will trigger RouteTimeAxisView::automation_track_hidden()
1080 which will cause a redraw. We don't want one per channel, so block that with no_redraw.
1082 changed = track->set_marked_for_display (false) || changed;
1084 changed = track->set_marked_for_display (true) || changed;
1091 /* TODO: Bender, Pressure */
1093 /* invalidate the controller menu, so that we rebuild it next time */
1094 _controller_menu_map.clear ();
1095 delete controller_menu;
1096 controller_menu = 0;
1104 MidiTimeAxisView::automation_child_menu_item (Evoral::Parameter param)
1106 Gtk::CheckMenuItem* m = RouteTimeAxisView::automation_child_menu_item (param);
1111 ParameterMenuMap::iterator i = _controller_menu_map.find (param);
1112 if (i != _controller_menu_map.end()) {
1116 i = _channel_command_menu_map.find (param);
1117 if (i != _channel_command_menu_map.end()) {
1124 boost::shared_ptr<MidiRegion>
1125 MidiTimeAxisView::add_region (framepos_t pos, framecnt_t length, bool commit)
1127 Editor* real_editor = dynamic_cast<Editor*> (&_editor);
1129 real_editor->begin_reversible_command (Operations::create_region);
1130 playlist()->clear_changes ();
1132 real_editor->snap_to (pos, 0);
1134 boost::shared_ptr<Source> src = _session->create_midi_source_for_session (view()->trackview().track().get(),
1135 view()->trackview().track()->name());
1138 plist.add (ARDOUR::Properties::start, 0);
1139 plist.add (ARDOUR::Properties::length, length);
1140 plist.add (ARDOUR::Properties::name, PBD::basename_nosuffix(src->name()));
1142 boost::shared_ptr<Region> region = (RegionFactory::create (src, plist));
1144 playlist()->add_region (region, pos);
1145 _session->add_command (new StatefulDiffCommand (playlist()));
1148 real_editor->commit_reversible_command ();
1151 return boost::dynamic_pointer_cast<MidiRegion>(region);
1155 MidiTimeAxisView::ensure_step_editor ()
1157 if (!_step_editor) {
1158 _step_editor = new StepEditor (_editor, midi_track(), *this);
1163 MidiTimeAxisView::start_step_editing ()
1165 ensure_step_editor ();
1166 _step_editor->start_step_editing ();
1170 MidiTimeAxisView::stop_step_editing ()
1173 _step_editor->stop_step_editing ();
1178 /** @return channel (counted from 0) to add an event to, based on the current setting
1179 * of the channel selector.
1182 MidiTimeAxisView::get_channel_for_add () const
1184 uint16_t const chn_mask = _channel_selector.get_selected_channels ();
1186 uint8_t channel = 0;
1188 /* pick the highest selected channel, unless all channels are selected,
1189 which is interpreted to mean channel 1 (zero)
1192 for (uint16_t i = 0; i < 16; ++i) {
1193 if (chn_mask & (1<<i)) {
1199 if (chn_cnt == 16) {
1207 MidiTimeAxisView::note_range_changed ()
1209 set_gui_property ("note-range-min", (int) midi_view()->lowest_note ());
1210 set_gui_property ("note-range-max", (int) midi_view()->highest_note ());