2 Copyright (C) 2000 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
26 #include <sigc++/bind.h>
28 #include "pbd/error.h"
29 #include "pbd/stl_delete.h"
30 #include "pbd/whitespace.h"
31 #include "pbd/basename.h"
32 #include "pbd/enumwriter.h"
33 #include "pbd/memento_command.h"
34 #include "pbd/stateful_diff_command.h"
36 #include "gtkmm2ext/gtk_ui.h"
37 #include "gtkmm2ext/selector.h"
38 #include "gtkmm2ext/bindable_button.h"
39 #include "gtkmm2ext/utils.h"
41 #include "ardour/file_source.h"
42 #include "ardour/ladspa_plugin.h"
43 #include "ardour/location.h"
44 #include "ardour/midi_diskstream.h"
45 #include "ardour/midi_patch_manager.h"
46 #include "ardour/midi_playlist.h"
47 #include "ardour/midi_region.h"
48 #include "ardour/midi_source.h"
49 #include "ardour/operations.h"
50 #include "ardour/playlist.h"
51 #include "ardour/processor.h"
52 #include "ardour/region_factory.h"
53 #include "ardour/session.h"
54 #include "ardour/session_playlist.h"
55 #include "ardour/tempo.h"
56 #include "ardour/utils.h"
58 #include "midi++/names.h"
60 #include "ardour_ui.h"
61 #include "ardour_button.h"
62 #include "automation_line.h"
63 #include "automation_time_axis.h"
64 #include "canvas-note-event.h"
65 #include "canvas_impl.h"
68 #include "ghostregion.h"
69 #include "gui_thread.h"
71 #include "midi_scroomer.h"
72 #include "midi_streamview.h"
73 #include "midi_region_view.h"
74 #include "midi_time_axis.h"
75 #include "piano_roll_header.h"
76 #include "playlist_selector.h"
77 #include "plugin_selector.h"
78 #include "plugin_ui.h"
79 #include "point_selection.h"
81 #include "region_view.h"
82 #include "rgb_macros.h"
83 #include "selection.h"
84 #include "step_editor.h"
85 #include "simplerect.h"
88 #include "ardour/midi_track.h"
92 using namespace ARDOUR;
95 using namespace Gtkmm2ext;
96 using namespace Editing;
98 // Minimum height at which a control is displayed
99 static const uint32_t MIDI_CONTROLS_BOX_MIN_HEIGHT = 162;
100 static const uint32_t KEYBOARD_MIN_HEIGHT = 140;
102 MidiTimeAxisView::MidiTimeAxisView (PublicEditor& ed, Session* sess, Canvas& canvas)
103 : AxisView(sess) // virtually inherited
104 , RouteTimeAxisView(ed, sess, canvas)
105 , _ignore_signals(false)
107 , _piano_roll_header(0)
108 , _note_mode(Sustained)
110 , _percussion_mode_item(0)
111 , _color_mode(MeterColors)
112 , _meter_color_mode_item(0)
113 , _channel_color_mode_item(0)
114 , _track_color_mode_item(0)
115 , _step_edit_item (0)
116 , _midi_thru_item (0)
117 , controller_menu (0)
123 MidiTimeAxisView::set_route (boost::shared_ptr<Route> rt)
127 _view = new MidiStreamView (*this);
130 _piano_roll_header = new PianoRollHeader(*midi_view());
131 _range_scroomer = new MidiScroomer(midi_view()->note_range_adjustment);
134 /* This next call will result in our height being set up, so it must come after
135 the creation of the piano roll / range scroomer as their visibility is set up
138 RouteTimeAxisView::set_route (rt);
140 _view->apply_color (_color, StreamView::RegionColor);
142 subplugin_menu.set_name ("ArdourContextMenu");
144 if (!gui_property ("note-range-min").empty ()) {
145 midi_view()->apply_note_range (atoi (gui_property ("note-range-min").c_str()), atoi (gui_property ("note-range-max").c_str()), true);
147 midi_view()->NoteRangeChanged.connect (sigc::mem_fun (*this, &MidiTimeAxisView::note_range_changed));
149 ignore_toggle = false;
151 if (is_midi_track()) {
152 controls_ebox.set_name ("MidiTimeAxisViewControlsBaseUnselected");
153 _note_mode = midi_track()->note_mode();
154 } else { // MIDI bus (which doesn't exist yet..)
155 controls_ebox.set_name ("MidiBusControlsBaseUnselected");
158 /* map current state of the route */
160 processors_changed (RouteProcessorChange ());
162 _route->processors_changed.connect (*this, invalidator (*this), ui_bind (&MidiTimeAxisView::processors_changed, this, _1), gui_context());
165 _piano_roll_header->SetNoteSelection.connect (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_selection));
166 _piano_roll_header->AddNoteSelection.connect (sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection));
167 _piano_roll_header->ExtendNoteSelection.connect (sigc::mem_fun (*this, &MidiTimeAxisView::extend_note_selection));
168 _piano_roll_header->ToggleNoteSelection.connect (sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection));
170 /* Suspend updates of the StreamView during scroomer drags to speed things up */
171 _range_scroomer->DragStarting.connect (sigc::mem_fun (*midi_view(), &MidiStreamView::suspend_updates));
172 _range_scroomer->DragFinishing.connect (sigc::mem_fun (*midi_view(), &MidiStreamView::resume_updates));
174 controls_hbox.pack_start(*_range_scroomer);
175 controls_hbox.pack_start(*_piano_roll_header);
177 controls_ebox.set_name ("MidiTrackControlsBaseUnselected");
178 controls_base_selected_name = "MidiTrackControlsBaseSelected";
179 controls_base_unselected_name = "MidiTrackControlsBaseUnselected";
181 midi_view()->NoteRangeChanged.connect (sigc::mem_fun(*this, &MidiTimeAxisView::update_range));
183 /* ask for notifications of any new RegionViews */
184 _view->RegionViewAdded.connect (sigc::mem_fun(*this, &MidiTimeAxisView::region_view_added));
186 if (!_editor.have_idled()) {
187 /* first idle will do what we need */
193 HBox* midi_controls_hbox = manage(new HBox());
195 MIDI::Name::MidiPatchManager& patch_manager = MIDI::Name::MidiPatchManager::instance();
197 MIDI::Name::MasterDeviceNames::Models::const_iterator m = patch_manager.all_models().begin();
198 for (; m != patch_manager.all_models().end(); ++m) {
199 _model_selector.append_text(m->c_str());
202 _model_selector.signal_changed().connect(sigc::mem_fun(*this, &MidiTimeAxisView::model_changed));
204 _custom_device_mode_selector.signal_changed().connect(
205 sigc::mem_fun(*this, &MidiTimeAxisView::custom_device_mode_changed));
207 // TODO: persist the choice
208 // this initializes the comboboxes and sends out the signal
209 _model_selector.set_active(0);
211 midi_controls_hbox->pack_start(_channel_selector, true, false);
212 if (!patch_manager.all_models().empty()) {
213 _midi_controls_box.pack_start(_model_selector, true, false);
214 _midi_controls_box.pack_start(_custom_device_mode_selector, true, false);
217 _midi_controls_box.pack_start(*midi_controls_hbox, true, true);
219 controls_vbox.pack_start(_midi_controls_box, false, false);
221 // restore channel selector settings
222 _channel_selector.set_channel_mode(midi_track()->get_channel_mode(), midi_track()->get_channel_mask());
223 _channel_selector.mode_changed.connect(
224 sigc::mem_fun(*midi_track(), &MidiTrack::set_channel_mode));
225 _channel_selector.mode_changed.connect(
226 sigc::mem_fun(*this, &MidiTimeAxisView::set_channel_mode));
228 string prop = gui_property ("color-mode");
230 _color_mode = ColorMode (string_2_enum(prop, _color_mode));
231 if (_color_mode == ChannelColors) {
232 _channel_selector.set_channel_colors(CanvasNoteEvent::midi_channel_colors);
236 set_color_mode (_color_mode, true, false);
238 prop = gui_property ("note-mode");
240 _note_mode = NoteMode (string_2_enum (prop, _note_mode));
241 if (_percussion_mode_item) {
242 _percussion_mode_item->set_active (_note_mode == Percussive);
246 /* Look for any GUI object state nodes that represent automation children that should exist, and create
250 list<string> gui_ids = gui_object_state().all_ids ();
251 for (list<string>::const_iterator i = gui_ids.begin(); i != gui_ids.end(); ++i) {
254 Evoral::Parameter parameter (0, 0, 0);
256 bool const p = AutomationTimeAxisView::parse_state_id (*i, route_id, has_parameter, parameter);
257 if (p && route_id == _route->id () && has_parameter) {
258 create_automation_child (parameter, string_is_affirmative (gui_object_state().get_string (*i, X_("visible"))));
264 MidiTimeAxisView::first_idle ()
271 MidiTimeAxisView::~MidiTimeAxisView ()
273 delete _piano_roll_header;
274 _piano_roll_header = 0;
276 delete _range_scroomer;
279 delete controller_menu;
284 MidiTimeAxisView::enter_internal_edit_mode ()
287 midi_view()->enter_internal_edit_mode ();
292 MidiTimeAxisView::leave_internal_edit_mode ()
295 midi_view()->leave_internal_edit_mode ();
300 MidiTimeAxisView::check_step_edit ()
302 ensure_step_editor ();
303 _step_editor->check_step_edit ();
307 MidiTimeAxisView::model_changed()
309 std::list<std::string> device_modes = MIDI::Name::MidiPatchManager::instance()
310 .custom_device_mode_names_by_model(_model_selector.get_active_text());
312 _custom_device_mode_selector.clear_items();
314 for (std::list<std::string>::const_iterator i = device_modes.begin();
315 i != device_modes.end(); ++i) {
316 cerr << "found custom device mode " << *i << " thread_id: " << pthread_self() << endl;
317 _custom_device_mode_selector.append_text(*i);
320 _custom_device_mode_selector.set_active(0);
323 void MidiTimeAxisView::custom_device_mode_changed()
325 _midi_patch_settings_changed.emit(_model_selector.get_active_text(),
326 _custom_device_mode_selector.get_active_text());
330 MidiTimeAxisView::midi_view()
332 return dynamic_cast<MidiStreamView*>(_view);
336 MidiTimeAxisView::set_height (uint32_t h)
338 if (h >= MIDI_CONTROLS_BOX_MIN_HEIGHT) {
339 _midi_controls_box.show_all ();
341 _midi_controls_box.hide();
344 if (h >= KEYBOARD_MIN_HEIGHT) {
345 if (is_track() && _range_scroomer) {
346 _range_scroomer->show();
348 if (is_track() && _piano_roll_header) {
349 _piano_roll_header->show();
352 if (is_track() && _range_scroomer) {
353 _range_scroomer->hide();
355 if (is_track() && _piano_roll_header) {
356 _piano_roll_header->hide();
360 /* We need to do this after changing visibility of our stuff, as it will
361 eventually trigger a call to Editor::reset_controls_layout_width(),
362 which needs to know if we have just shown or hidden a scroomer /
365 RouteTimeAxisView::set_height (h);
369 MidiTimeAxisView::append_extra_display_menu_items ()
371 using namespace Menu_Helpers;
373 MenuList& items = display_menu->items();
376 Menu *range_menu = manage(new Menu);
377 MenuList& range_items = range_menu->items();
378 range_menu->set_name ("ArdourContextMenu");
380 range_items.push_back (MenuElem (_("Show Full Range"), sigc::bind (
381 sigc::mem_fun(*this, &MidiTimeAxisView::set_note_range),
382 MidiStreamView::FullRange)));
384 range_items.push_back (MenuElem (_("Fit Contents"), sigc::bind (
385 sigc::mem_fun(*this, &MidiTimeAxisView::set_note_range),
386 MidiStreamView::ContentsRange)));
388 items.push_back (MenuElem (_("Note Range"), *range_menu));
389 items.push_back (MenuElem (_("Note Mode"), *build_note_mode_menu()));
391 items.push_back (CheckMenuElem (_("MIDI Thru"), sigc::mem_fun(*this, &MidiTimeAxisView::toggle_midi_thru)));
392 _midi_thru_item = dynamic_cast<CheckMenuItem*>(&items.back());
394 items.push_back (SeparatorElem ());
398 MidiTimeAxisView::toggle_midi_thru ()
400 if (!_midi_thru_item) {
404 bool view_yn = _midi_thru_item->get_active();
405 if (view_yn != midi_track()->midi_thru()) {
406 midi_track()->set_midi_thru (view_yn);
411 MidiTimeAxisView::build_automation_action_menu (bool for_selection)
413 using namespace Menu_Helpers;
415 /* If we have a controller menu, we need to detach it before
416 RouteTimeAxis::build_automation_action_menu destroys the
417 menu it is attached to. Otherwise GTK destroys
418 controller_menu's gobj, meaning that it can't be reattached
419 below. See bug #3134.
422 if (controller_menu) {
423 detach_menu (*controller_menu);
426 _channel_command_menu_map.clear ();
427 RouteTimeAxisView::build_automation_action_menu (for_selection);
429 MenuList& automation_items = automation_action_menu->items();
431 uint16_t selected_channels = _channel_selector.get_selected_channels();
433 if (selected_channels != 0) {
435 automation_items.push_back (SeparatorElem());
437 /* these 2 MIDI "command" types are semantically more like automation than note data,
438 but they are not MIDI controllers. We give them special status in this menu, since
439 they will not show up in the controller list and anyone who actually knows
440 something about MIDI (!) would not expect to find them there.
443 add_channel_command_menu_item (automation_items, _("Bender"), MidiPitchBenderAutomation, 0);
444 automation_items.back().set_sensitive (!for_selection || _editor.get_selection().tracks.size() == 1);
445 add_channel_command_menu_item (automation_items, _("Pressure"), MidiChannelPressureAutomation, 0);
446 automation_items.back().set_sensitive (!for_selection || _editor.get_selection().tracks.size() == 1);
448 /* now all MIDI controllers. Always offer the possibility that we will rebuild the controllers menu
449 since it might need to be updated after a channel mode change or other change. Also detach it
450 first in case it has been used anywhere else.
453 build_controller_menu ();
455 automation_items.push_back (SeparatorElem());
456 automation_items.push_back (MenuElem (_("Controllers"), *controller_menu));
457 automation_items.back().set_sensitive (!for_selection || _editor.get_selection().tracks.size() == 1);
459 automation_items.push_back (MenuElem (string_compose ("<i>%1</i>", _("No MIDI Channels selected"))));
460 dynamic_cast<Label*> (automation_items.back().get_child())->set_use_markup (true);
466 MidiTimeAxisView::change_all_channel_tracks_visibility (bool yn, Evoral::Parameter param)
468 uint16_t selected_channels = _channel_selector.get_selected_channels();
470 for (uint8_t chn = 0; chn < 16; chn++) {
471 if (selected_channels & (0x0001 << chn)) {
473 Evoral::Parameter fully_qualified_param (param.type(), chn, param.id());
474 Gtk::CheckMenuItem* menu = automation_child_menu_item (fully_qualified_param);
477 menu->set_active (yn);
484 MidiTimeAxisView::add_channel_command_menu_item (Menu_Helpers::MenuList& items, const string& label, AutomationType auto_type, uint8_t cmd)
486 using namespace Menu_Helpers;
488 /* count the number of selected channels because we will build a different menu structure if there is more than 1 selected.
491 uint16_t selected_channels = _channel_selector.get_selected_channels();
494 for (uint8_t chn = 0; chn < 16; chn++) {
495 if (selected_channels & (0x0001 << chn)) {
504 /* multiple channels - create a submenu, with 1 item per channel */
506 Menu* chn_menu = manage (new Menu);
507 MenuList& chn_items (chn_menu->items());
508 Evoral::Parameter param_without_channel (auto_type, 0, cmd);
510 /* add a couple of items to hide/show all of them */
512 chn_items.push_back (MenuElem (_("Hide all channels"),
513 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
514 false, param_without_channel)));
515 chn_items.push_back (MenuElem (_("Show all channels"),
516 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
517 true, param_without_channel)));
519 for (uint8_t chn = 0; chn < 16; chn++) {
520 if (selected_channels & (0x0001 << chn)) {
522 /* for each selected channel, add a menu item for this controller */
524 Evoral::Parameter fully_qualified_param (auto_type, chn, cmd);
525 chn_items.push_back (CheckMenuElem (string_compose (_("Channel %1"), chn+1),
526 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
527 fully_qualified_param)));
529 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
530 bool visible = false;
533 if (track->marked_for_display()) {
538 CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&chn_items.back());
539 _channel_command_menu_map[fully_qualified_param] = cmi;
540 cmi->set_active (visible);
544 /* now create an item in the parent menu that has the per-channel list as a submenu */
546 items.push_back (MenuElem (label, *chn_menu));
550 /* just one channel - create a single menu item for this command+channel combination*/
552 for (uint8_t chn = 0; chn < 16; chn++) {
553 if (selected_channels & (0x0001 << chn)) {
555 Evoral::Parameter fully_qualified_param (auto_type, chn, cmd);
556 items.push_back (CheckMenuElem (label,
557 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
558 fully_qualified_param)));
560 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
561 bool visible = false;
564 if (track->marked_for_display()) {
569 CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&items.back());
570 _channel_command_menu_map[fully_qualified_param] = cmi;
571 cmi->set_active (visible);
573 /* one channel only */
581 MidiTimeAxisView::build_controller_menu ()
583 using namespace Menu_Helpers;
585 if (controller_menu) {
586 /* it exists and has not been invalidated by a channel mode change, so just return it */
590 controller_menu = new Menu; // explicitly managed by us
591 MenuList& items (controller_menu->items());
593 /* create several "top level" menu items for sets of controllers (16 at a time), and populate each one with a submenu
594 for each controller+channel combination covering the currently selected channels for this track
597 uint16_t selected_channels = _channel_selector.get_selected_channels();
599 /* count the number of selected channels because we will build a different menu structure if there is more than 1 selected.
604 for (uint8_t chn = 0; chn < 16; chn++) {
605 if (selected_channels & (0x0001 << chn)) {
612 /* loop over all 127 MIDI controllers, in groups of 16; except don't offer
613 bank select controllers, as they are handled by the `patch' code */
615 for (int i = 0; i < 127; i += 16) {
617 Menu* ctl_menu = manage (new Menu);
618 MenuList& ctl_items (ctl_menu->items());
621 /* for each controller, consider whether to create a submenu or a single item */
623 for (int ctl = i; ctl < i+16; ++ctl) {
625 if (ctl == MIDI_CTL_MSB_BANK || ctl == MIDI_CTL_LSB_BANK) {
631 /* multiple channels - create a submenu, with 1 item per channel */
633 Menu* chn_menu = manage (new Menu);
634 MenuList& chn_items (chn_menu->items());
636 /* add a couple of items to hide/show this controller on all channels */
638 Evoral::Parameter param_without_channel (MidiCCAutomation, 0, ctl);
639 chn_items.push_back (MenuElem (_("Hide all channels"),
640 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
641 false, param_without_channel)));
642 chn_items.push_back (MenuElem (_("Show all channels"),
643 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
644 true, param_without_channel)));
646 for (uint8_t chn = 0; chn < 16; chn++) {
647 if (selected_channels & (0x0001 << chn)) {
649 /* for each selected channel, add a menu item for this controller */
651 Evoral::Parameter fully_qualified_param (MidiCCAutomation, chn, ctl);
652 chn_items.push_back (CheckMenuElem (string_compose (_("Channel %1"), chn+1),
653 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
654 fully_qualified_param)));
656 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
657 bool visible = false;
660 if (track->marked_for_display()) {
665 CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&chn_items.back());
666 _controller_menu_map[fully_qualified_param] = cmi;
667 cmi->set_active (visible);
671 /* add the per-channel menu to the list of controllers, with the name of the controller */
672 ctl_items.push_back (MenuElem (string_compose ("<b>%1</b>: %2", ctl, midi_name (ctl)), *chn_menu));
673 dynamic_cast<Label*> (ctl_items.back().get_child())->set_use_markup (true);
677 /* just one channel - create a single menu item for this ctl+channel combination*/
679 for (uint8_t chn = 0; chn < 16; chn++) {
680 if (selected_channels & (0x0001 << chn)) {
682 Evoral::Parameter fully_qualified_param (MidiCCAutomation, chn, ctl);
683 ctl_items.push_back (
685 string_compose ("<b>%1</b>: %2 [%3]", ctl, midi_name (ctl), int (chn)),
686 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
687 fully_qualified_param)
690 dynamic_cast<Label*> (ctl_items.back().get_child())->set_use_markup (true);
692 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
693 bool visible = false;
696 if (track->marked_for_display()) {
701 CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&ctl_items.back());
702 _controller_menu_map[fully_qualified_param] = cmi;
703 cmi->set_active (visible);
705 /* one channel only */
712 /* add the menu for this block of controllers to the overall controller menu */
714 items.push_back (MenuElem (string_compose (_("Controllers %1-%2"), i, i+15), *ctl_menu));
719 MidiTimeAxisView::build_note_mode_menu()
721 using namespace Menu_Helpers;
723 Menu* mode_menu = manage (new Menu);
724 MenuList& items = mode_menu->items();
725 mode_menu->set_name ("ArdourContextMenu");
727 RadioMenuItem::Group mode_group;
728 items.push_back (RadioMenuElem (mode_group, _("Sustained"),
729 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_mode), Sustained)));
730 _note_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
731 _note_mode_item->set_active(_note_mode == Sustained);
733 items.push_back (RadioMenuElem (mode_group, _("Percussive"),
734 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_mode), Percussive)));
735 _percussion_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
736 _percussion_mode_item->set_active(_note_mode == Percussive);
742 MidiTimeAxisView::build_color_mode_menu()
744 using namespace Menu_Helpers;
746 Menu* mode_menu = manage (new Menu);
747 MenuList& items = mode_menu->items();
748 mode_menu->set_name ("ArdourContextMenu");
750 RadioMenuItem::Group mode_group;
751 items.push_back (RadioMenuElem (mode_group, _("Meter Colors"),
752 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode),
753 MeterColors, false, true)));
754 _meter_color_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
755 _meter_color_mode_item->set_active(_color_mode == MeterColors);
757 items.push_back (RadioMenuElem (mode_group, _("Channel Colors"),
758 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode),
759 ChannelColors, false, true)));
760 _channel_color_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
761 _channel_color_mode_item->set_active(_color_mode == ChannelColors);
763 items.push_back (RadioMenuElem (mode_group, _("Track Color"),
764 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode),
765 TrackColor, false, true)));
766 _channel_color_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
767 _channel_color_mode_item->set_active(_color_mode == TrackColor);
773 MidiTimeAxisView::set_note_mode(NoteMode mode)
775 if (_note_mode != mode || midi_track()->note_mode() != mode) {
777 midi_track()->set_note_mode(mode);
778 set_gui_property ("note-mode", enum_2_string(_note_mode));
779 _view->redisplay_track();
784 MidiTimeAxisView::set_color_mode (ColorMode mode, bool force, bool redisplay)
786 if (_color_mode == mode && !force) {
790 if (mode == ChannelColors) {
791 _channel_selector.set_channel_colors(CanvasNoteEvent::midi_channel_colors);
793 _channel_selector.set_default_channel_color();
797 set_gui_property ("color-mode", enum_2_string(_color_mode));
799 _view->redisplay_track();
804 MidiTimeAxisView::set_note_range(MidiStreamView::VisibleNoteRange range)
806 if (!_ignore_signals)
807 midi_view()->set_note_range(range);
812 MidiTimeAxisView::update_range()
814 MidiGhostRegion* mgr;
816 for(list<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
817 if ((mgr = dynamic_cast<MidiGhostRegion*>(*i)) != 0) {
824 MidiTimeAxisView::show_all_automation (bool apply_to_selection)
826 if (apply_to_selection) {
827 _editor.get_selection().tracks.foreach_midi_time_axis (boost::bind (&MidiTimeAxisView::show_all_automation, _1, false));
830 const set<Evoral::Parameter> params = midi_track()->midi_playlist()->contained_automation();
832 for (set<Evoral::Parameter>::const_iterator i = params.begin(); i != params.end(); ++i) {
833 create_automation_child(*i, true);
837 RouteTimeAxisView::show_all_automation ();
842 MidiTimeAxisView::show_existing_automation (bool apply_to_selection)
844 if (apply_to_selection) {
845 _editor.get_selection().tracks.foreach_midi_time_axis (boost::bind (&MidiTimeAxisView::show_existing_automation, _1, false));
848 const set<Evoral::Parameter> params = midi_track()->midi_playlist()->contained_automation();
850 for (set<Evoral::Parameter>::const_iterator i = params.begin(); i != params.end(); ++i) {
851 create_automation_child (*i, true);
855 RouteTimeAxisView::show_existing_automation ();
859 /** Create an automation track for the given parameter (pitch bend, channel pressure).
862 MidiTimeAxisView::create_automation_child (const Evoral::Parameter& param, bool show)
864 if (param.type() == NullAutomation) {
865 cerr << "WARNING: Attempt to create NullAutomation child, ignoring" << endl;
869 AutomationTracks::iterator existing = _automation_tracks.find (param);
871 if (existing != _automation_tracks.end()) {
873 /* automation track created because we had existing data for
874 * the processor, but visibility may need to be controlled
875 * since it will have been set visible by default.
878 cerr << "show existing auto track: " << show << " noredraw " << no_redraw << endl;
880 if (existing->second->set_marked_for_display (show) && !no_redraw) {
887 boost::shared_ptr<AutomationTimeAxisView> track;
889 switch (param.type()) {
892 create_gain_automation_child (param, show);
895 case PluginAutomation:
896 /* handled elsewhere */
899 case MidiCCAutomation:
900 case MidiPgmChangeAutomation:
901 case MidiPitchBenderAutomation:
902 case MidiChannelPressureAutomation:
903 case MidiSystemExclusiveAutomation:
904 /* These controllers are region "automation" - they are owned
905 * by regions (and their MidiModels), not by the track. As a
906 * result we do not create an AutomationList/Line for the track
907 * ... except here we are doing something!! XXX
910 track.reset (new AutomationTimeAxisView (
913 boost::shared_ptr<Automatable> (),
914 boost::shared_ptr<AutomationControl> (),
920 _route->describe_parameter(param)
924 _view->foreach_regionview (sigc::mem_fun (*track.get(), &TimeAxisView::add_ghost));
927 add_automation_child (param, track, show);
931 error << "MidiTimeAxisView: unknown automation child " << EventTypeMap::instance().to_symbol(param) << endmsg;
936 MidiTimeAxisView::route_active_changed ()
938 RouteUI::route_active_changed ();
941 if (_route->active()) {
942 controls_ebox.set_name ("MidiTrackControlsBaseUnselected");
943 controls_base_selected_name = "MidiTrackControlsBaseSelected";
944 controls_base_unselected_name = "MidiTrackControlsBaseUnselected";
946 controls_ebox.set_name ("MidiTrackControlsBaseInactiveUnselected");
947 controls_base_selected_name = "MidiTrackControlsBaseInactiveSelected";
948 controls_base_unselected_name = "MidiTrackControlsBaseInactiveUnselected";
954 if (_route->active()) {
955 controls_ebox.set_name ("BusControlsBaseUnselected");
956 controls_base_selected_name = "BusControlsBaseSelected";
957 controls_base_unselected_name = "BusControlsBaseUnselected";
959 controls_ebox.set_name ("BusControlsBaseInactiveUnselected");
960 controls_base_selected_name = "BusControlsBaseInactiveSelected";
961 controls_base_unselected_name = "BusControlsBaseInactiveUnselected";
967 MidiTimeAxisView::set_note_selection (uint8_t note)
969 if (!_editor.internal_editing()) {
973 uint16_t chn_mask = _channel_selector.get_selected_channels();
975 if (_view->num_selected_regionviews() == 0) {
976 _view->foreach_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_selection_region_view), note, chn_mask));
978 _view->foreach_selected_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_selection_region_view), note, chn_mask));
983 MidiTimeAxisView::add_note_selection (uint8_t note)
985 if (!_editor.internal_editing()) {
989 uint16_t chn_mask = _channel_selector.get_selected_channels();
991 if (_view->num_selected_regionviews() == 0) {
992 _view->foreach_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection_region_view), note, chn_mask));
994 _view->foreach_selected_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection_region_view), note, chn_mask));
999 MidiTimeAxisView::extend_note_selection (uint8_t note)
1001 if (!_editor.internal_editing()) {
1005 uint16_t chn_mask = _channel_selector.get_selected_channels();
1007 if (_view->num_selected_regionviews() == 0) {
1008 _view->foreach_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::extend_note_selection_region_view), note, chn_mask));
1010 _view->foreach_selected_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::extend_note_selection_region_view), note, chn_mask));
1015 MidiTimeAxisView::toggle_note_selection (uint8_t note)
1017 if (!_editor.internal_editing()) {
1021 uint16_t chn_mask = _channel_selector.get_selected_channels();
1023 if (_view->num_selected_regionviews() == 0) {
1024 _view->foreach_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection_region_view), note, chn_mask));
1026 _view->foreach_selected_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection_region_view), note, chn_mask));
1031 MidiTimeAxisView::set_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1033 dynamic_cast<MidiRegionView*>(rv)->select_matching_notes (note, chn_mask, false, false);
1037 MidiTimeAxisView::add_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1039 dynamic_cast<MidiRegionView*>(rv)->select_matching_notes (note, chn_mask, true, false);
1043 MidiTimeAxisView::extend_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1045 dynamic_cast<MidiRegionView*>(rv)->select_matching_notes (note, chn_mask, true, true);
1049 MidiTimeAxisView::toggle_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1051 dynamic_cast<MidiRegionView*>(rv)->toggle_matching_notes (note, chn_mask);
1055 MidiTimeAxisView::set_channel_mode (ChannelMode, uint16_t)
1057 /* hide all automation tracks that use the wrong channel(s) and show all those that use
1061 uint16_t selected_channels = _channel_selector.get_selected_channels();
1062 bool changed = false;
1066 for (uint32_t ctl = 0; ctl < 127; ++ctl) {
1068 for (uint32_t chn = 0; chn < 16; ++chn) {
1069 Evoral::Parameter fully_qualified_param (MidiCCAutomation, chn, ctl);
1070 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
1076 if ((selected_channels & (0x0001 << chn)) == 0) {
1077 /* channel not in use. hiding it will trigger RouteTimeAxisView::automation_track_hidden()
1078 which will cause a redraw. We don't want one per channel, so block that with no_redraw.
1080 changed = track->set_marked_for_display (false) || changed;
1082 changed = track->set_marked_for_display (true) || changed;
1089 /* TODO: Bender, Pressure */
1091 /* invalidate the controller menu, so that we rebuild it next time */
1092 _controller_menu_map.clear ();
1093 delete controller_menu;
1094 controller_menu = 0;
1102 MidiTimeAxisView::automation_child_menu_item (Evoral::Parameter param)
1104 Gtk::CheckMenuItem* m = RouteTimeAxisView::automation_child_menu_item (param);
1109 ParameterMenuMap::iterator i = _controller_menu_map.find (param);
1110 if (i != _controller_menu_map.end()) {
1114 i = _channel_command_menu_map.find (param);
1115 if (i != _channel_command_menu_map.end()) {
1122 boost::shared_ptr<MidiRegion>
1123 MidiTimeAxisView::add_region (framepos_t pos, framecnt_t length, bool commit)
1125 Editor* real_editor = dynamic_cast<Editor*> (&_editor);
1127 real_editor->begin_reversible_command (Operations::create_region);
1128 playlist()->clear_changes ();
1130 real_editor->snap_to (pos, 0);
1132 boost::shared_ptr<Source> src = _session->create_midi_source_for_session (view()->trackview().track().get(),
1133 view()->trackview().track()->name());
1136 plist.add (ARDOUR::Properties::start, 0);
1137 plist.add (ARDOUR::Properties::length, length);
1138 plist.add (ARDOUR::Properties::name, PBD::basename_nosuffix(src->name()));
1140 boost::shared_ptr<Region> region = (RegionFactory::create (src, plist));
1142 playlist()->add_region (region, pos);
1143 _session->add_command (new StatefulDiffCommand (playlist()));
1146 real_editor->commit_reversible_command ();
1149 return boost::dynamic_pointer_cast<MidiRegion>(region);
1153 MidiTimeAxisView::ensure_step_editor ()
1155 if (!_step_editor) {
1156 _step_editor = new StepEditor (_editor, midi_track(), *this);
1161 MidiTimeAxisView::start_step_editing ()
1163 ensure_step_editor ();
1164 _step_editor->start_step_editing ();
1168 MidiTimeAxisView::stop_step_editing ()
1171 _step_editor->stop_step_editing ();
1176 /** @return channel (counted from 0) to add an event to, based on the current setting
1177 * of the channel selector.
1180 MidiTimeAxisView::get_channel_for_add () const
1182 uint16_t const chn_mask = _channel_selector.get_selected_channels ();
1184 uint8_t channel = 0;
1186 /* pick the highest selected channel, unless all channels are selected,
1187 which is interpreted to mean channel 1 (zero)
1190 for (uint16_t i = 0; i < 16; ++i) {
1191 if (chn_mask & (1<<i)) {
1197 if (chn_cnt == 16) {
1205 MidiTimeAxisView::note_range_changed ()
1207 set_gui_property ("note-range-min", (int) midi_view()->lowest_note ());
1208 set_gui_property ("note-range-max", (int) midi_view()->highest_note ());