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 , controller_menu (0)
122 MidiTimeAxisView::set_route (boost::shared_ptr<Route> rt)
126 _view = new MidiStreamView (*this);
129 _piano_roll_header = new PianoRollHeader(*midi_view());
130 _range_scroomer = new MidiScroomer(midi_view()->note_range_adjustment);
131 _range_scroomer->DoubleClicked.connect (sigc::bind (
132 sigc::mem_fun(*this, &MidiTimeAxisView::set_note_range),
133 MidiStreamView::ContentsRange, false));
136 /* This next call will result in our height being set up, so it must come after
137 the creation of the piano roll / range scroomer as their visibility is set up
140 RouteTimeAxisView::set_route (rt);
142 _view->apply_color (_color, StreamView::RegionColor);
144 subplugin_menu.set_name ("ArdourContextMenu");
146 if (!gui_property ("note-range-min").empty ()) {
147 midi_view()->apply_note_range (atoi (gui_property ("note-range-min").c_str()), atoi (gui_property ("note-range-max").c_str()), true);
149 midi_view()->NoteRangeChanged.connect (sigc::mem_fun (*this, &MidiTimeAxisView::note_range_changed));
150 _view->ContentsHeightChanged.connect (sigc::mem_fun (*this, &MidiTimeAxisView::contents_height_changed));
152 ignore_toggle = false;
154 if (is_midi_track()) {
155 controls_ebox.set_name ("MidiTimeAxisViewControlsBaseUnselected");
156 _note_mode = midi_track()->note_mode();
157 } else { // MIDI bus (which doesn't exist yet..)
158 controls_ebox.set_name ("MidiBusControlsBaseUnselected");
161 /* map current state of the route */
163 processors_changed (RouteProcessorChange ());
165 _route->processors_changed.connect (*this, invalidator (*this), boost::bind (&MidiTimeAxisView::processors_changed, this, _1), gui_context());
168 _piano_roll_header->SetNoteSelection.connect (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_selection));
169 _piano_roll_header->AddNoteSelection.connect (sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection));
170 _piano_roll_header->ExtendNoteSelection.connect (sigc::mem_fun (*this, &MidiTimeAxisView::extend_note_selection));
171 _piano_roll_header->ToggleNoteSelection.connect (sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection));
173 /* Suspend updates of the StreamView during scroomer drags to speed things up */
174 _range_scroomer->DragStarting.connect (sigc::mem_fun (*midi_view(), &MidiStreamView::suspend_updates));
175 _range_scroomer->DragFinishing.connect (sigc::mem_fun (*midi_view(), &MidiStreamView::resume_updates));
177 /* Put the scroomer and the keyboard in a VBox with a padding
178 label so that they can be reduced in height for stacked-view
181 VBox* v = manage (new VBox);
182 HBox* h = manage (new HBox);
183 h->pack_start (*_range_scroomer);
184 h->pack_start (*_piano_roll_header);
185 v->pack_start (*h, false, false);
186 v->pack_start (*manage (new Label ("")), true, true);
189 controls_hbox.pack_start(*v);
191 controls_ebox.set_name ("MidiTrackControlsBaseUnselected");
192 controls_base_selected_name = "MidiTrackControlsBaseSelected";
193 controls_base_unselected_name = "MidiTrackControlsBaseUnselected";
195 midi_view()->NoteRangeChanged.connect (sigc::mem_fun(*this, &MidiTimeAxisView::update_range));
197 /* ask for notifications of any new RegionViews */
198 _view->RegionViewAdded.connect (sigc::mem_fun(*this, &MidiTimeAxisView::region_view_added));
200 if (!_editor.have_idled()) {
201 /* first idle will do what we need */
208 MIDI::Name::MidiPatchManager& patch_manager = MIDI::Name::MidiPatchManager::instance();
210 MIDI::Name::MasterDeviceNames::Models::const_iterator m = patch_manager.all_models().begin();
211 for (; m != patch_manager.all_models().end(); ++m) {
212 _midnam_model_selector.append_text(m->c_str());
215 _midnam_model_selector.set_active_text (gui_property (X_("midnam-model-name")));
217 _midnam_custom_device_mode_selector.set_active_text (gui_property (X_("midnam-custom-device-mode")));
219 _midnam_model_selector.signal_changed().connect(sigc::mem_fun(*this, &MidiTimeAxisView::model_changed));
220 _midnam_custom_device_mode_selector.signal_changed().connect(
221 sigc::mem_fun(*this, &MidiTimeAxisView::custom_device_mode_changed));
223 ARDOUR_UI::instance()->set_tip (_midnam_model_selector, _("External MIDI Device"));
224 ARDOUR_UI::instance()->set_tip (_midnam_custom_device_mode_selector, _("External Device Mode"));
226 _midi_controls_box.set_homogeneous(false);
227 _midi_controls_box.set_border_width (10);
228 if (!patch_manager.all_models().empty()) {
229 _channel_selector.set_border_width(2);
230 _midi_controls_box.resize(3, 2);
231 _midi_controls_box.attach(_channel_selector, 0, 2, 0, 1);
233 _midi_controls_box.attach(*manage(new HSeparator()), 0, 2, 1, 2);
235 _midnam_model_selector.set_size_request(22, 30);
236 _midnam_model_selector.set_border_width(2);
237 _midi_controls_box.attach(_midnam_model_selector, 0, 1, 2, 3);
239 _midnam_custom_device_mode_selector.set_size_request(10, 30);
240 _midnam_custom_device_mode_selector.set_border_width(2);
241 _midi_controls_box.attach(_midnam_custom_device_mode_selector, 1, 2, 2, 3);
243 _midi_controls_box.attach(_channel_selector, 0, 1, 0, 1);
246 controls_vbox.pack_start(_midi_controls_box, false, false);
248 // restore channel selector settings
249 _channel_selector.set_channel_mode(midi_track()->get_channel_mode(), midi_track()->get_channel_mask());
250 _channel_selector.mode_changed.connect(
251 sigc::mem_fun(*midi_track(), &MidiTrack::set_channel_mode));
252 _channel_selector.mode_changed.connect(
253 sigc::mem_fun(*this, &MidiTimeAxisView::set_channel_mode));
255 string prop = gui_property ("color-mode");
257 _color_mode = ColorMode (string_2_enum(prop, _color_mode));
258 if (_color_mode == ChannelColors) {
259 _channel_selector.set_channel_colors(CanvasNoteEvent::midi_channel_colors);
263 set_color_mode (_color_mode, true, false);
265 prop = gui_property ("note-mode");
267 _note_mode = NoteMode (string_2_enum (prop, _note_mode));
268 if (_percussion_mode_item) {
269 _percussion_mode_item->set_active (_note_mode == Percussive);
273 /* Look for any GUI object state nodes that represent automation children that should exist, and create
277 list<string> gui_ids = gui_object_state().all_ids ();
278 for (list<string>::const_iterator i = gui_ids.begin(); i != gui_ids.end(); ++i) {
281 Evoral::Parameter parameter (0, 0, 0);
283 bool const p = AutomationTimeAxisView::parse_state_id (*i, route_id, has_parameter, parameter);
284 if (p && route_id == _route->id () && has_parameter) {
285 create_automation_child (parameter, string_is_affirmative (gui_object_state().get_string (*i, X_("visible"))));
291 MidiTimeAxisView::first_idle ()
298 MidiTimeAxisView::~MidiTimeAxisView ()
300 delete _piano_roll_header;
301 _piano_roll_header = 0;
303 delete _range_scroomer;
306 delete controller_menu;
311 MidiTimeAxisView::enter_internal_edit_mode ()
314 midi_view()->enter_internal_edit_mode ();
319 MidiTimeAxisView::leave_internal_edit_mode ()
322 midi_view()->leave_internal_edit_mode ();
327 MidiTimeAxisView::check_step_edit ()
329 ensure_step_editor ();
330 _step_editor->check_step_edit ();
334 MidiTimeAxisView::model_changed()
336 string model = _midnam_model_selector.get_active_text();
337 set_gui_property (X_("midnam-model-name"), model);
339 std::list<std::string> device_modes = MIDI::Name::MidiPatchManager::instance()
340 .custom_device_mode_names_by_model(model);
342 _midnam_custom_device_mode_selector.clear_items();
344 for (std::list<std::string>::const_iterator i = device_modes.begin();
345 i != device_modes.end(); ++i) {
346 _midnam_custom_device_mode_selector.append_text(*i);
349 _midnam_custom_device_mode_selector.set_active(0);
351 _route->instrument_info().set_external_instrument (_midnam_model_selector.get_active_text(), _midnam_custom_device_mode_selector.get_active_text());
355 MidiTimeAxisView::custom_device_mode_changed()
357 string mode = _midnam_custom_device_mode_selector.get_active_text();
358 set_gui_property (X_("midnam-custom-device-mode"), mode);
359 _route->instrument_info().set_external_instrument (_midnam_model_selector.get_active_text(), mode);
363 MidiTimeAxisView::midi_view()
365 return dynamic_cast<MidiStreamView*>(_view);
369 MidiTimeAxisView::set_height (uint32_t h)
371 if (h >= MIDI_CONTROLS_BOX_MIN_HEIGHT) {
372 _midi_controls_box.show_all ();
374 _midi_controls_box.hide();
377 if (h >= KEYBOARD_MIN_HEIGHT) {
378 if (is_track() && _range_scroomer) {
379 _range_scroomer->show();
381 if (is_track() && _piano_roll_header) {
382 _piano_roll_header->show();
385 if (is_track() && _range_scroomer) {
386 _range_scroomer->hide();
388 if (is_track() && _piano_roll_header) {
389 _piano_roll_header->hide();
393 /* We need to do this after changing visibility of our stuff, as it will
394 eventually trigger a call to Editor::reset_controls_layout_width(),
395 which needs to know if we have just shown or hidden a scroomer /
398 RouteTimeAxisView::set_height (h);
402 MidiTimeAxisView::append_extra_display_menu_items ()
404 using namespace Menu_Helpers;
406 MenuList& items = display_menu->items();
409 Menu *range_menu = manage(new Menu);
410 MenuList& range_items = range_menu->items();
411 range_menu->set_name ("ArdourContextMenu");
413 range_items.push_back (MenuElem (_("Show Full Range"), sigc::bind (
414 sigc::mem_fun(*this, &MidiTimeAxisView::set_note_range),
415 MidiStreamView::FullRange, true)));
417 range_items.push_back (MenuElem (_("Fit Contents"), sigc::bind (
418 sigc::mem_fun(*this, &MidiTimeAxisView::set_note_range),
419 MidiStreamView::ContentsRange, true)));
421 items.push_back (MenuElem (_("Note Range"), *range_menu));
422 items.push_back (MenuElem (_("Note Mode"), *build_note_mode_menu()));
424 items.push_back (SeparatorElem ());
428 MidiTimeAxisView::build_automation_action_menu (bool for_selection)
430 using namespace Menu_Helpers;
432 /* If we have a controller menu, we need to detach it before
433 RouteTimeAxis::build_automation_action_menu destroys the
434 menu it is attached to. Otherwise GTK destroys
435 controller_menu's gobj, meaning that it can't be reattached
436 below. See bug #3134.
439 if (controller_menu) {
440 detach_menu (*controller_menu);
443 _channel_command_menu_map.clear ();
444 RouteTimeAxisView::build_automation_action_menu (for_selection);
446 MenuList& automation_items = automation_action_menu->items();
448 uint16_t selected_channels = _channel_selector.get_selected_channels();
450 if (selected_channels != 0) {
452 automation_items.push_back (SeparatorElem());
454 /* these 2 MIDI "command" types are semantically more like automation than note data,
455 but they are not MIDI controllers. We give them special status in this menu, since
456 they will not show up in the controller list and anyone who actually knows
457 something about MIDI (!) would not expect to find them there.
460 add_channel_command_menu_item (automation_items, _("Bender"), MidiPitchBenderAutomation, 0);
461 automation_items.back().set_sensitive (!for_selection || _editor.get_selection().tracks.size() == 1);
462 add_channel_command_menu_item (automation_items, _("Pressure"), MidiChannelPressureAutomation, 0);
463 automation_items.back().set_sensitive (!for_selection || _editor.get_selection().tracks.size() == 1);
465 /* now all MIDI controllers. Always offer the possibility that we will rebuild the controllers menu
466 since it might need to be updated after a channel mode change or other change. Also detach it
467 first in case it has been used anywhere else.
470 build_controller_menu ();
472 automation_items.push_back (SeparatorElem());
473 automation_items.push_back (MenuElem (_("Controllers"), *controller_menu));
474 automation_items.back().set_sensitive (!for_selection || _editor.get_selection().tracks.size() == 1);
476 automation_items.push_back (MenuElem (string_compose ("<i>%1</i>", _("No MIDI Channels selected"))));
477 dynamic_cast<Label*> (automation_items.back().get_child())->set_use_markup (true);
483 MidiTimeAxisView::change_all_channel_tracks_visibility (bool yn, Evoral::Parameter param)
485 uint16_t selected_channels = _channel_selector.get_selected_channels();
487 for (uint8_t chn = 0; chn < 16; chn++) {
488 if (selected_channels & (0x0001 << chn)) {
490 Evoral::Parameter fully_qualified_param (param.type(), chn, param.id());
491 Gtk::CheckMenuItem* menu = automation_child_menu_item (fully_qualified_param);
494 menu->set_active (yn);
501 MidiTimeAxisView::add_channel_command_menu_item (Menu_Helpers::MenuList& items, const string& label, AutomationType auto_type, uint8_t cmd)
503 using namespace Menu_Helpers;
505 /* count the number of selected channels because we will build a different menu structure if there is more than 1 selected.
508 uint16_t selected_channels = _channel_selector.get_selected_channels();
511 for (uint8_t chn = 0; chn < 16; chn++) {
512 if (selected_channels & (0x0001 << chn)) {
521 /* multiple channels - create a submenu, with 1 item per channel */
523 Menu* chn_menu = manage (new Menu);
524 MenuList& chn_items (chn_menu->items());
525 Evoral::Parameter param_without_channel (auto_type, 0, cmd);
527 /* add a couple of items to hide/show all of them */
529 chn_items.push_back (MenuElem (_("Hide all channels"),
530 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
531 false, param_without_channel)));
532 chn_items.push_back (MenuElem (_("Show all channels"),
533 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
534 true, param_without_channel)));
536 for (uint8_t chn = 0; chn < 16; chn++) {
537 if (selected_channels & (0x0001 << chn)) {
539 /* for each selected channel, add a menu item for this controller */
541 Evoral::Parameter fully_qualified_param (auto_type, chn, cmd);
542 chn_items.push_back (CheckMenuElem (string_compose (_("Channel %1"), chn+1),
543 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
544 fully_qualified_param)));
546 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
547 bool visible = false;
550 if (track->marked_for_display()) {
555 CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&chn_items.back());
556 _channel_command_menu_map[fully_qualified_param] = cmi;
557 cmi->set_active (visible);
561 /* now create an item in the parent menu that has the per-channel list as a submenu */
563 items.push_back (MenuElem (label, *chn_menu));
567 /* just one channel - create a single menu item for this command+channel combination*/
569 for (uint8_t chn = 0; chn < 16; chn++) {
570 if (selected_channels & (0x0001 << chn)) {
572 Evoral::Parameter fully_qualified_param (auto_type, chn, cmd);
573 items.push_back (CheckMenuElem (label,
574 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
575 fully_qualified_param)));
577 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
578 bool visible = false;
581 if (track->marked_for_display()) {
586 CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&items.back());
587 _channel_command_menu_map[fully_qualified_param] = cmi;
588 cmi->set_active (visible);
590 /* one channel only */
598 MidiTimeAxisView::build_controller_menu ()
600 using namespace Menu_Helpers;
602 if (controller_menu) {
603 /* it exists and has not been invalidated by a channel mode change, so just return it */
607 controller_menu = new Menu; // explicitly managed by us
608 MenuList& items (controller_menu->items());
610 /* create several "top level" menu items for sets of controllers (16 at a time), and populate each one with a submenu
611 for each controller+channel combination covering the currently selected channels for this track
614 uint16_t selected_channels = _channel_selector.get_selected_channels();
616 /* count the number of selected channels because we will build a different menu structure if there is more than 1 selected.
621 for (uint8_t chn = 0; chn < 16; chn++) {
622 if (selected_channels & (0x0001 << chn)) {
629 /* loop over all 127 MIDI controllers, in groups of 16; except don't offer
630 bank select controllers, as they are handled by the `patch' code */
632 for (int i = 0; i < 127; i += 16) {
634 Menu* ctl_menu = manage (new Menu);
635 MenuList& ctl_items (ctl_menu->items());
638 /* for each controller, consider whether to create a submenu or a single item */
640 for (int ctl = i; ctl < i+16; ++ctl) {
642 if (ctl == MIDI_CTL_MSB_BANK || ctl == MIDI_CTL_LSB_BANK) {
648 /* multiple channels - create a submenu, with 1 item per channel */
650 Menu* chn_menu = manage (new Menu);
651 MenuList& chn_items (chn_menu->items());
653 /* add a couple of items to hide/show this controller on all channels */
655 Evoral::Parameter param_without_channel (MidiCCAutomation, 0, ctl);
656 chn_items.push_back (MenuElem (_("Hide all channels"),
657 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
658 false, param_without_channel)));
659 chn_items.push_back (MenuElem (_("Show all channels"),
660 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
661 true, param_without_channel)));
663 for (uint8_t chn = 0; chn < 16; chn++) {
664 if (selected_channels & (0x0001 << chn)) {
666 /* for each selected channel, add a menu item for this controller */
668 Evoral::Parameter fully_qualified_param (MidiCCAutomation, chn, ctl);
669 chn_items.push_back (CheckMenuElem (string_compose (_("Channel %1"), chn+1),
670 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
671 fully_qualified_param)));
673 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
674 bool visible = false;
677 if (track->marked_for_display()) {
682 CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&chn_items.back());
683 _controller_menu_map[fully_qualified_param] = cmi;
684 cmi->set_active (visible);
688 /* add the per-channel menu to the list of controllers, with the name of the controller */
689 ctl_items.push_back (MenuElem (string_compose ("<b>%1</b>: %2", ctl, midi_name (ctl)), *chn_menu));
690 dynamic_cast<Label*> (ctl_items.back().get_child())->set_use_markup (true);
694 /* just one channel - create a single menu item for this ctl+channel combination*/
696 for (uint8_t chn = 0; chn < 16; chn++) {
697 if (selected_channels & (0x0001 << chn)) {
699 Evoral::Parameter fully_qualified_param (MidiCCAutomation, chn, ctl);
700 ctl_items.push_back (
702 string_compose ("<b>%1</b>: %2 [%3]", ctl, midi_name (ctl), int (chn)),
703 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
704 fully_qualified_param)
707 dynamic_cast<Label*> (ctl_items.back().get_child())->set_use_markup (true);
709 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
710 bool visible = false;
713 if (track->marked_for_display()) {
718 CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&ctl_items.back());
719 _controller_menu_map[fully_qualified_param] = cmi;
720 cmi->set_active (visible);
722 /* one channel only */
729 /* add the menu for this block of controllers to the overall controller menu */
731 items.push_back (MenuElem (string_compose (_("Controllers %1-%2"), i, i+15), *ctl_menu));
736 MidiTimeAxisView::build_note_mode_menu()
738 using namespace Menu_Helpers;
740 Menu* mode_menu = manage (new Menu);
741 MenuList& items = mode_menu->items();
742 mode_menu->set_name ("ArdourContextMenu");
744 RadioMenuItem::Group mode_group;
745 items.push_back (RadioMenuElem (mode_group,_("Sustained"),
746 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_mode), Sustained, true)));
747 _note_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
748 _note_mode_item->set_active(_note_mode == Sustained);
750 items.push_back (RadioMenuElem (mode_group, _("Percussive"),
751 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_mode), Percussive, true)));
752 _percussion_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
753 _percussion_mode_item->set_active(_note_mode == Percussive);
759 MidiTimeAxisView::build_color_mode_menu()
761 using namespace Menu_Helpers;
763 Menu* mode_menu = manage (new Menu);
764 MenuList& items = mode_menu->items();
765 mode_menu->set_name ("ArdourContextMenu");
767 RadioMenuItem::Group mode_group;
768 items.push_back (RadioMenuElem (mode_group, _("Meter Colors"),
769 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode),
770 MeterColors, false, true, true)));
771 _meter_color_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
772 _meter_color_mode_item->set_active(_color_mode == MeterColors);
774 items.push_back (RadioMenuElem (mode_group, _("Channel Colors"),
775 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode),
776 ChannelColors, false, true, true)));
777 _channel_color_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
778 _channel_color_mode_item->set_active(_color_mode == ChannelColors);
780 items.push_back (RadioMenuElem (mode_group, _("Track Color"),
781 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode),
782 TrackColor, false, true, true)));
783 _channel_color_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
784 _channel_color_mode_item->set_active(_color_mode == TrackColor);
790 MidiTimeAxisView::set_note_mode(NoteMode mode, bool apply_to_selection)
792 if (apply_to_selection) {
793 _editor.get_selection().tracks.foreach_midi_time_axis (boost::bind (&MidiTimeAxisView::set_note_mode, _1, mode, false));
795 if (_note_mode != mode || midi_track()->note_mode() != mode) {
797 midi_track()->set_note_mode(mode);
798 set_gui_property ("note-mode", enum_2_string(_note_mode));
799 _view->redisplay_track();
805 MidiTimeAxisView::set_color_mode (ColorMode mode, bool force, bool redisplay, bool apply_to_selection)
807 if (apply_to_selection) {
808 _editor.get_selection().tracks.foreach_midi_time_axis (
809 boost::bind (&MidiTimeAxisView::set_color_mode, _1, mode, force, redisplay, false)
813 if (_color_mode == mode && !force) {
817 if (mode == ChannelColors) {
818 _channel_selector.set_channel_colors(CanvasNoteEvent::midi_channel_colors);
820 _channel_selector.set_default_channel_color();
824 set_gui_property ("color-mode", enum_2_string(_color_mode));
826 _view->redisplay_track();
832 MidiTimeAxisView::set_note_range (MidiStreamView::VisibleNoteRange range, bool apply_to_selection)
834 if (apply_to_selection) {
835 _editor.get_selection().tracks.foreach_midi_time_axis (
836 boost::bind (&MidiTimeAxisView::set_note_range, _1, range, false)
839 if (!_ignore_signals) {
840 midi_view()->set_note_range(range);
846 MidiTimeAxisView::update_range()
848 MidiGhostRegion* mgr;
850 for(list<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
851 if ((mgr = dynamic_cast<MidiGhostRegion*>(*i)) != 0) {
858 MidiTimeAxisView::show_all_automation (bool apply_to_selection)
860 if (apply_to_selection) {
861 _editor.get_selection().tracks.foreach_midi_time_axis (boost::bind (&MidiTimeAxisView::show_all_automation, _1, false));
864 const set<Evoral::Parameter> params = midi_track()->midi_playlist()->contained_automation();
866 for (set<Evoral::Parameter>::const_iterator i = params.begin(); i != params.end(); ++i) {
867 create_automation_child(*i, true);
871 RouteTimeAxisView::show_all_automation ();
876 MidiTimeAxisView::show_existing_automation (bool apply_to_selection)
878 if (apply_to_selection) {
879 _editor.get_selection().tracks.foreach_midi_time_axis (boost::bind (&MidiTimeAxisView::show_existing_automation, _1, false));
882 const set<Evoral::Parameter> params = midi_track()->midi_playlist()->contained_automation();
884 for (set<Evoral::Parameter>::const_iterator i = params.begin(); i != params.end(); ++i) {
885 create_automation_child (*i, true);
889 RouteTimeAxisView::show_existing_automation ();
893 /** Create an automation track for the given parameter (pitch bend, channel pressure).
896 MidiTimeAxisView::create_automation_child (const Evoral::Parameter& param, bool show)
898 if (param.type() == NullAutomation) {
902 AutomationTracks::iterator existing = _automation_tracks.find (param);
904 if (existing != _automation_tracks.end()) {
906 /* automation track created because we had existing data for
907 * the processor, but visibility may need to be controlled
908 * since it will have been set visible by default.
911 if (existing->second->set_marked_for_display (show) && !no_redraw) {
918 boost::shared_ptr<AutomationTimeAxisView> track;
920 switch (param.type()) {
923 create_gain_automation_child (param, show);
926 case PluginAutomation:
927 /* handled elsewhere */
930 case MidiCCAutomation:
931 case MidiPgmChangeAutomation:
932 case MidiPitchBenderAutomation:
933 case MidiChannelPressureAutomation:
934 case MidiSystemExclusiveAutomation:
935 /* These controllers are region "automation" - they are owned
936 * by regions (and their MidiModels), not by the track. As a
937 * result we do not create an AutomationList/Line for the track
938 * ... except here we are doing something!! XXX
941 track.reset (new AutomationTimeAxisView (
944 boost::shared_ptr<Automatable> (),
945 boost::shared_ptr<AutomationControl> (),
951 _route->describe_parameter(param)
955 _view->foreach_regionview (sigc::mem_fun (*track.get(), &TimeAxisView::add_ghost));
958 add_automation_child (param, track, show);
962 error << "MidiTimeAxisView: unknown automation child " << EventTypeMap::instance().to_symbol(param) << endmsg;
967 MidiTimeAxisView::route_active_changed ()
969 RouteUI::route_active_changed ();
972 if (_route->active()) {
973 controls_ebox.set_name ("MidiTrackControlsBaseUnselected");
974 controls_base_selected_name = "MidiTrackControlsBaseSelected";
975 controls_base_unselected_name = "MidiTrackControlsBaseUnselected";
977 controls_ebox.set_name ("MidiTrackControlsBaseInactiveUnselected");
978 controls_base_selected_name = "MidiTrackControlsBaseInactiveSelected";
979 controls_base_unselected_name = "MidiTrackControlsBaseInactiveUnselected";
985 if (_route->active()) {
986 controls_ebox.set_name ("BusControlsBaseUnselected");
987 controls_base_selected_name = "BusControlsBaseSelected";
988 controls_base_unselected_name = "BusControlsBaseUnselected";
990 controls_ebox.set_name ("BusControlsBaseInactiveUnselected");
991 controls_base_selected_name = "BusControlsBaseInactiveSelected";
992 controls_base_unselected_name = "BusControlsBaseInactiveUnselected";
998 MidiTimeAxisView::set_note_selection (uint8_t note)
1000 if (!_editor.internal_editing()) {
1004 uint16_t chn_mask = _channel_selector.get_selected_channels();
1006 if (_view->num_selected_regionviews() == 0) {
1007 _view->foreach_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_selection_region_view), note, chn_mask));
1009 _view->foreach_selected_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_selection_region_view), note, chn_mask));
1014 MidiTimeAxisView::add_note_selection (uint8_t note)
1016 if (!_editor.internal_editing()) {
1020 uint16_t chn_mask = _channel_selector.get_selected_channels();
1022 if (_view->num_selected_regionviews() == 0) {
1023 _view->foreach_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection_region_view), note, chn_mask));
1025 _view->foreach_selected_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection_region_view), note, chn_mask));
1030 MidiTimeAxisView::extend_note_selection (uint8_t note)
1032 if (!_editor.internal_editing()) {
1036 uint16_t chn_mask = _channel_selector.get_selected_channels();
1038 if (_view->num_selected_regionviews() == 0) {
1039 _view->foreach_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::extend_note_selection_region_view), note, chn_mask));
1041 _view->foreach_selected_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::extend_note_selection_region_view), note, chn_mask));
1046 MidiTimeAxisView::toggle_note_selection (uint8_t note)
1048 if (!_editor.internal_editing()) {
1052 uint16_t chn_mask = _channel_selector.get_selected_channels();
1054 if (_view->num_selected_regionviews() == 0) {
1055 _view->foreach_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection_region_view), note, chn_mask));
1057 _view->foreach_selected_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection_region_view), note, chn_mask));
1062 MidiTimeAxisView::set_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1064 dynamic_cast<MidiRegionView*>(rv)->select_matching_notes (note, chn_mask, false, false);
1068 MidiTimeAxisView::add_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1070 dynamic_cast<MidiRegionView*>(rv)->select_matching_notes (note, chn_mask, true, false);
1074 MidiTimeAxisView::extend_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1076 dynamic_cast<MidiRegionView*>(rv)->select_matching_notes (note, chn_mask, true, true);
1080 MidiTimeAxisView::toggle_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1082 dynamic_cast<MidiRegionView*>(rv)->toggle_matching_notes (note, chn_mask);
1086 MidiTimeAxisView::set_channel_mode (ChannelMode, uint16_t)
1088 /* hide all automation tracks that use the wrong channel(s) and show all those that use
1092 uint16_t selected_channels = _channel_selector.get_selected_channels();
1093 bool changed = false;
1097 for (uint32_t ctl = 0; ctl < 127; ++ctl) {
1099 for (uint32_t chn = 0; chn < 16; ++chn) {
1100 Evoral::Parameter fully_qualified_param (MidiCCAutomation, chn, ctl);
1101 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
1107 if ((selected_channels & (0x0001 << chn)) == 0) {
1108 /* channel not in use. hiding it will trigger RouteTimeAxisView::automation_track_hidden()
1109 which will cause a redraw. We don't want one per channel, so block that with no_redraw.
1111 changed = track->set_marked_for_display (false) || changed;
1113 changed = track->set_marked_for_display (true) || changed;
1120 /* TODO: Bender, Pressure */
1122 /* invalidate the controller menu, so that we rebuild it next time */
1123 _controller_menu_map.clear ();
1124 delete controller_menu;
1125 controller_menu = 0;
1133 MidiTimeAxisView::automation_child_menu_item (Evoral::Parameter param)
1135 Gtk::CheckMenuItem* m = RouteTimeAxisView::automation_child_menu_item (param);
1140 ParameterMenuMap::iterator i = _controller_menu_map.find (param);
1141 if (i != _controller_menu_map.end()) {
1145 i = _channel_command_menu_map.find (param);
1146 if (i != _channel_command_menu_map.end()) {
1153 boost::shared_ptr<MidiRegion>
1154 MidiTimeAxisView::add_region (framepos_t pos, framecnt_t length, bool commit)
1156 Editor* real_editor = dynamic_cast<Editor*> (&_editor);
1158 real_editor->begin_reversible_command (Operations::create_region);
1159 playlist()->clear_changes ();
1161 real_editor->snap_to (pos, 0);
1163 boost::shared_ptr<Source> src = _session->create_midi_source_for_session (view()->trackview().track().get(),
1164 view()->trackview().track()->name());
1167 plist.add (ARDOUR::Properties::start, 0);
1168 plist.add (ARDOUR::Properties::length, length);
1169 plist.add (ARDOUR::Properties::name, PBD::basename_nosuffix(src->name()));
1171 boost::shared_ptr<Region> region = (RegionFactory::create (src, plist));
1173 playlist()->add_region (region, pos);
1174 _session->add_command (new StatefulDiffCommand (playlist()));
1177 real_editor->commit_reversible_command ();
1180 return boost::dynamic_pointer_cast<MidiRegion>(region);
1184 MidiTimeAxisView::ensure_step_editor ()
1186 if (!_step_editor) {
1187 _step_editor = new StepEditor (_editor, midi_track(), *this);
1192 MidiTimeAxisView::start_step_editing ()
1194 ensure_step_editor ();
1195 _step_editor->start_step_editing ();
1199 MidiTimeAxisView::stop_step_editing ()
1202 _step_editor->stop_step_editing ();
1207 /** @return channel (counted from 0) to add an event to, based on the current setting
1208 * of the channel selector.
1211 MidiTimeAxisView::get_channel_for_add () const
1213 uint16_t const chn_mask = _channel_selector.get_selected_channels ();
1215 uint8_t channel = 0;
1217 /* pick the highest selected channel, unless all channels are selected,
1218 which is interpreted to mean channel 1 (zero)
1221 for (uint16_t i = 0; i < 16; ++i) {
1222 if (chn_mask & (1<<i)) {
1228 if (chn_cnt == 16) {
1236 MidiTimeAxisView::note_range_changed ()
1238 set_gui_property ("note-range-min", (int) midi_view()->lowest_note ());
1239 set_gui_property ("note-range-max", (int) midi_view()->highest_note ());
1243 MidiTimeAxisView::contents_height_changed ()
1245 _range_scroomer->set_size_request (-1, _view->child_height ());