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_for_selection),
387 MidiStreamView::FullRange)));
389 range_items.push_back (MenuElem (_("Fit Contents"), sigc::bind (
390 sigc::mem_fun(*this, &MidiTimeAxisView::set_note_range_for_selection),
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);
816 /** Set the note range for all selected MIDI tracks */
818 MidiTimeAxisView::set_note_range_for_selection (MidiStreamView::VisibleNoteRange range)
820 TrackSelection& ts = _editor.get_selection().tracks;
822 for (TrackSelection::iterator i = ts.begin(); i != ts.end(); ++i) {
823 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*> (*i);
825 mtv->set_note_range (range);
832 MidiTimeAxisView::update_range()
834 MidiGhostRegion* mgr;
836 for(list<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
837 if ((mgr = dynamic_cast<MidiGhostRegion*>(*i)) != 0) {
844 MidiTimeAxisView::show_all_automation (bool apply_to_selection)
846 if (apply_to_selection) {
847 _editor.get_selection().tracks.foreach_midi_time_axis (boost::bind (&MidiTimeAxisView::show_all_automation, _1, false));
850 const set<Evoral::Parameter> params = midi_track()->midi_playlist()->contained_automation();
852 for (set<Evoral::Parameter>::const_iterator i = params.begin(); i != params.end(); ++i) {
853 create_automation_child(*i, true);
857 RouteTimeAxisView::show_all_automation ();
862 MidiTimeAxisView::show_existing_automation (bool apply_to_selection)
864 if (apply_to_selection) {
865 _editor.get_selection().tracks.foreach_midi_time_axis (boost::bind (&MidiTimeAxisView::show_existing_automation, _1, false));
868 const set<Evoral::Parameter> params = midi_track()->midi_playlist()->contained_automation();
870 for (set<Evoral::Parameter>::const_iterator i = params.begin(); i != params.end(); ++i) {
871 create_automation_child (*i, true);
875 RouteTimeAxisView::show_existing_automation ();
879 /** Create an automation track for the given parameter (pitch bend, channel pressure).
882 MidiTimeAxisView::create_automation_child (const Evoral::Parameter& param, bool show)
884 if (param.type() == NullAutomation) {
888 AutomationTracks::iterator existing = _automation_tracks.find (param);
890 if (existing != _automation_tracks.end()) {
892 /* automation track created because we had existing data for
893 * the processor, but visibility may need to be controlled
894 * since it will have been set visible by default.
897 if (existing->second->set_marked_for_display (show) && !no_redraw) {
904 boost::shared_ptr<AutomationTimeAxisView> track;
906 switch (param.type()) {
909 create_gain_automation_child (param, show);
912 case PluginAutomation:
913 /* handled elsewhere */
916 case MidiCCAutomation:
917 case MidiPgmChangeAutomation:
918 case MidiPitchBenderAutomation:
919 case MidiChannelPressureAutomation:
920 case MidiSystemExclusiveAutomation:
921 /* These controllers are region "automation" - they are owned
922 * by regions (and their MidiModels), not by the track. As a
923 * result we do not create an AutomationList/Line for the track
924 * ... except here we are doing something!! XXX
927 track.reset (new AutomationTimeAxisView (
930 boost::shared_ptr<Automatable> (),
931 boost::shared_ptr<AutomationControl> (),
937 _route->describe_parameter(param)
941 _view->foreach_regionview (sigc::mem_fun (*track.get(), &TimeAxisView::add_ghost));
944 add_automation_child (param, track, show);
948 error << "MidiTimeAxisView: unknown automation child " << EventTypeMap::instance().to_symbol(param) << endmsg;
953 MidiTimeAxisView::route_active_changed ()
955 RouteUI::route_active_changed ();
958 if (_route->active()) {
959 controls_ebox.set_name ("MidiTrackControlsBaseUnselected");
960 controls_base_selected_name = "MidiTrackControlsBaseSelected";
961 controls_base_unselected_name = "MidiTrackControlsBaseUnselected";
963 controls_ebox.set_name ("MidiTrackControlsBaseInactiveUnselected");
964 controls_base_selected_name = "MidiTrackControlsBaseInactiveSelected";
965 controls_base_unselected_name = "MidiTrackControlsBaseInactiveUnselected";
971 if (_route->active()) {
972 controls_ebox.set_name ("BusControlsBaseUnselected");
973 controls_base_selected_name = "BusControlsBaseSelected";
974 controls_base_unselected_name = "BusControlsBaseUnselected";
976 controls_ebox.set_name ("BusControlsBaseInactiveUnselected");
977 controls_base_selected_name = "BusControlsBaseInactiveSelected";
978 controls_base_unselected_name = "BusControlsBaseInactiveUnselected";
984 MidiTimeAxisView::set_note_selection (uint8_t note)
986 if (!_editor.internal_editing()) {
990 uint16_t chn_mask = _channel_selector.get_selected_channels();
992 if (_view->num_selected_regionviews() == 0) {
993 _view->foreach_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_selection_region_view), note, chn_mask));
995 _view->foreach_selected_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_selection_region_view), note, chn_mask));
1000 MidiTimeAxisView::add_note_selection (uint8_t note)
1002 if (!_editor.internal_editing()) {
1006 uint16_t chn_mask = _channel_selector.get_selected_channels();
1008 if (_view->num_selected_regionviews() == 0) {
1009 _view->foreach_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection_region_view), note, chn_mask));
1011 _view->foreach_selected_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection_region_view), note, chn_mask));
1016 MidiTimeAxisView::extend_note_selection (uint8_t note)
1018 if (!_editor.internal_editing()) {
1022 uint16_t chn_mask = _channel_selector.get_selected_channels();
1024 if (_view->num_selected_regionviews() == 0) {
1025 _view->foreach_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::extend_note_selection_region_view), note, chn_mask));
1027 _view->foreach_selected_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::extend_note_selection_region_view), note, chn_mask));
1032 MidiTimeAxisView::toggle_note_selection (uint8_t note)
1034 if (!_editor.internal_editing()) {
1038 uint16_t chn_mask = _channel_selector.get_selected_channels();
1040 if (_view->num_selected_regionviews() == 0) {
1041 _view->foreach_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection_region_view), note, chn_mask));
1043 _view->foreach_selected_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection_region_view), note, chn_mask));
1048 MidiTimeAxisView::set_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1050 dynamic_cast<MidiRegionView*>(rv)->select_matching_notes (note, chn_mask, false, false);
1054 MidiTimeAxisView::add_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1056 dynamic_cast<MidiRegionView*>(rv)->select_matching_notes (note, chn_mask, true, false);
1060 MidiTimeAxisView::extend_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1062 dynamic_cast<MidiRegionView*>(rv)->select_matching_notes (note, chn_mask, true, true);
1066 MidiTimeAxisView::toggle_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1068 dynamic_cast<MidiRegionView*>(rv)->toggle_matching_notes (note, chn_mask);
1072 MidiTimeAxisView::set_channel_mode (ChannelMode, uint16_t)
1074 /* hide all automation tracks that use the wrong channel(s) and show all those that use
1078 uint16_t selected_channels = _channel_selector.get_selected_channels();
1079 bool changed = false;
1083 for (uint32_t ctl = 0; ctl < 127; ++ctl) {
1085 for (uint32_t chn = 0; chn < 16; ++chn) {
1086 Evoral::Parameter fully_qualified_param (MidiCCAutomation, chn, ctl);
1087 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
1093 if ((selected_channels & (0x0001 << chn)) == 0) {
1094 /* channel not in use. hiding it will trigger RouteTimeAxisView::automation_track_hidden()
1095 which will cause a redraw. We don't want one per channel, so block that with no_redraw.
1097 changed = track->set_marked_for_display (false) || changed;
1099 changed = track->set_marked_for_display (true) || changed;
1106 /* TODO: Bender, Pressure */
1108 /* invalidate the controller menu, so that we rebuild it next time */
1109 _controller_menu_map.clear ();
1110 delete controller_menu;
1111 controller_menu = 0;
1119 MidiTimeAxisView::automation_child_menu_item (Evoral::Parameter param)
1121 Gtk::CheckMenuItem* m = RouteTimeAxisView::automation_child_menu_item (param);
1126 ParameterMenuMap::iterator i = _controller_menu_map.find (param);
1127 if (i != _controller_menu_map.end()) {
1131 i = _channel_command_menu_map.find (param);
1132 if (i != _channel_command_menu_map.end()) {
1139 boost::shared_ptr<MidiRegion>
1140 MidiTimeAxisView::add_region (framepos_t pos, framecnt_t length, bool commit)
1142 Editor* real_editor = dynamic_cast<Editor*> (&_editor);
1144 real_editor->begin_reversible_command (Operations::create_region);
1145 playlist()->clear_changes ();
1147 real_editor->snap_to (pos, 0);
1149 boost::shared_ptr<Source> src = _session->create_midi_source_for_session (view()->trackview().track().get(),
1150 view()->trackview().track()->name());
1153 plist.add (ARDOUR::Properties::start, 0);
1154 plist.add (ARDOUR::Properties::length, length);
1155 plist.add (ARDOUR::Properties::name, PBD::basename_nosuffix(src->name()));
1157 boost::shared_ptr<Region> region = (RegionFactory::create (src, plist));
1159 playlist()->add_region (region, pos);
1160 _session->add_command (new StatefulDiffCommand (playlist()));
1163 real_editor->commit_reversible_command ();
1166 return boost::dynamic_pointer_cast<MidiRegion>(region);
1170 MidiTimeAxisView::ensure_step_editor ()
1172 if (!_step_editor) {
1173 _step_editor = new StepEditor (_editor, midi_track(), *this);
1178 MidiTimeAxisView::start_step_editing ()
1180 ensure_step_editor ();
1181 _step_editor->start_step_editing ();
1185 MidiTimeAxisView::stop_step_editing ()
1188 _step_editor->stop_step_editing ();
1193 /** @return channel (counted from 0) to add an event to, based on the current setting
1194 * of the channel selector.
1197 MidiTimeAxisView::get_channel_for_add () const
1199 uint16_t const chn_mask = _channel_selector.get_selected_channels ();
1201 uint8_t channel = 0;
1203 /* pick the highest selected channel, unless all channels are selected,
1204 which is interpreted to mean channel 1 (zero)
1207 for (uint16_t i = 0; i < 16; ++i) {
1208 if (chn_mask & (1<<i)) {
1214 if (chn_cnt == 16) {
1222 MidiTimeAxisView::note_range_changed ()
1224 set_gui_property ("note-range-min", (int) midi_view()->lowest_note ());
1225 set_gui_property ("note-range-max", (int) midi_view()->highest_note ());