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);
133 /* This next call will result in our height being set up, so it must come after
134 the creation of the piano roll / range scroomer as their visibility is set up
137 RouteTimeAxisView::set_route (rt);
139 _view->apply_color (_color, StreamView::RegionColor);
141 subplugin_menu.set_name ("ArdourContextMenu");
143 if (!gui_property ("note-range-min").empty ()) {
144 midi_view()->apply_note_range (atoi (gui_property ("note-range-min").c_str()), atoi (gui_property ("note-range-max").c_str()), true);
146 midi_view()->NoteRangeChanged.connect (sigc::mem_fun (*this, &MidiTimeAxisView::note_range_changed));
147 _view->ContentsHeightChanged.connect (sigc::mem_fun (*this, &MidiTimeAxisView::contents_height_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 /* Put the scroomer and the keyboard in a VBox with a padding
175 label so that they can be reduced in height for stacked-view
178 VBox* v = manage (new VBox);
179 HBox* h = manage (new HBox);
180 h->pack_start (*_range_scroomer);
181 h->pack_start (*_piano_roll_header);
182 v->pack_start (*h, false, false);
183 v->pack_start (*manage (new Label ("")), true, true);
186 controls_hbox.pack_start(*v);
188 controls_ebox.set_name ("MidiTrackControlsBaseUnselected");
189 controls_base_selected_name = "MidiTrackControlsBaseSelected";
190 controls_base_unselected_name = "MidiTrackControlsBaseUnselected";
192 midi_view()->NoteRangeChanged.connect (sigc::mem_fun(*this, &MidiTimeAxisView::update_range));
194 /* ask for notifications of any new RegionViews */
195 _view->RegionViewAdded.connect (sigc::mem_fun(*this, &MidiTimeAxisView::region_view_added));
197 if (!_editor.have_idled()) {
198 /* first idle will do what we need */
205 MIDI::Name::MidiPatchManager& patch_manager = MIDI::Name::MidiPatchManager::instance();
207 MIDI::Name::MasterDeviceNames::Models::const_iterator m = patch_manager.all_models().begin();
208 for (; m != patch_manager.all_models().end(); ++m) {
209 _model_selector.append_text(m->c_str());
213 _model_selector.signal_changed().connect(sigc::mem_fun(*this, &MidiTimeAxisView::model_changed));
215 _custom_device_mode_selector.signal_changed().connect(
216 sigc::mem_fun(*this, &MidiTimeAxisView::custom_device_mode_changed));
218 _model_selector.set_active_text (gui_property (X_("midnam-model-name")));
219 _custom_device_mode_selector.set_active_text (gui_property (X_("midnam-custom-device-mode")));
221 ARDOUR_UI::instance()->set_tip (_model_selector, _("External MIDI Device"));
222 ARDOUR_UI::instance()->set_tip (_custom_device_mode_selector, _("External Device Mode"));
224 _midi_controls_box.set_homogeneous(false);
226 if (!patch_manager.all_models().empty()) {
227 _midi_controls_box.resize(3, 3);
228 _midi_controls_box.set_border_width (5);
229 _midi_controls_box.attach(_channel_selector, 1, 2, 0, 1);
230 _midi_controls_box.attach(*(new Label(" ", false)), 0, 1, 1, 2);
231 _midi_controls_box.attach(*(new Label(" ", false)), 2, 3, 1, 2);
232 _midi_controls_box.attach(_model_selector, 1, 2, 1, 2);
233 _midi_controls_box.attach(_custom_device_mode_selector, 1, 2, 2, 3);
235 _midi_controls_box.attach(_channel_selector, 1, 2, 0, 1);
238 controls_vbox.pack_start(_midi_controls_box, false, false);
240 // restore channel selector settings
241 _channel_selector.set_channel_mode(midi_track()->get_channel_mode(), midi_track()->get_channel_mask());
242 _channel_selector.mode_changed.connect(
243 sigc::mem_fun(*midi_track(), &MidiTrack::set_channel_mode));
244 _channel_selector.mode_changed.connect(
245 sigc::mem_fun(*this, &MidiTimeAxisView::set_channel_mode));
247 string prop = gui_property ("color-mode");
249 _color_mode = ColorMode (string_2_enum(prop, _color_mode));
250 if (_color_mode == ChannelColors) {
251 _channel_selector.set_channel_colors(CanvasNoteEvent::midi_channel_colors);
255 set_color_mode (_color_mode, true, false);
257 prop = gui_property ("note-mode");
259 _note_mode = NoteMode (string_2_enum (prop, _note_mode));
260 if (_percussion_mode_item) {
261 _percussion_mode_item->set_active (_note_mode == Percussive);
265 /* Look for any GUI object state nodes that represent automation children that should exist, and create
269 list<string> gui_ids = gui_object_state().all_ids ();
270 for (list<string>::const_iterator i = gui_ids.begin(); i != gui_ids.end(); ++i) {
273 Evoral::Parameter parameter (0, 0, 0);
275 bool const p = AutomationTimeAxisView::parse_state_id (*i, route_id, has_parameter, parameter);
276 if (p && route_id == _route->id () && has_parameter) {
277 create_automation_child (parameter, string_is_affirmative (gui_object_state().get_string (*i, X_("visible"))));
283 MidiTimeAxisView::first_idle ()
290 MidiTimeAxisView::~MidiTimeAxisView ()
292 delete _piano_roll_header;
293 _piano_roll_header = 0;
295 delete _range_scroomer;
298 delete controller_menu;
303 MidiTimeAxisView::enter_internal_edit_mode ()
306 midi_view()->enter_internal_edit_mode ();
311 MidiTimeAxisView::leave_internal_edit_mode ()
314 midi_view()->leave_internal_edit_mode ();
319 MidiTimeAxisView::check_step_edit ()
321 ensure_step_editor ();
322 _step_editor->check_step_edit ();
326 MidiTimeAxisView::model_changed()
328 std::list<std::string> device_modes = MIDI::Name::MidiPatchManager::instance()
329 .custom_device_mode_names_by_model(_model_selector.get_active_text());
331 _custom_device_mode_selector.clear_items();
333 for (std::list<std::string>::const_iterator i = device_modes.begin();
334 i != device_modes.end(); ++i) {
335 _custom_device_mode_selector.append_text(*i);
338 _custom_device_mode_selector.set_active(0);
340 _route->instrument_info().set_external_instrument (_model_selector.get_active_text(), _custom_device_mode_selector.get_active_text());
344 MidiTimeAxisView::custom_device_mode_changed()
346 _route->instrument_info().set_external_instrument (_model_selector.get_active_text(), _custom_device_mode_selector.get_active_text());
350 MidiTimeAxisView::midi_view()
352 return dynamic_cast<MidiStreamView*>(_view);
356 MidiTimeAxisView::set_height (uint32_t h)
358 if (h >= MIDI_CONTROLS_BOX_MIN_HEIGHT) {
359 _midi_controls_box.show_all ();
361 _midi_controls_box.hide();
364 if (h >= KEYBOARD_MIN_HEIGHT) {
365 if (is_track() && _range_scroomer) {
366 _range_scroomer->show();
368 if (is_track() && _piano_roll_header) {
369 _piano_roll_header->show();
372 if (is_track() && _range_scroomer) {
373 _range_scroomer->hide();
375 if (is_track() && _piano_roll_header) {
376 _piano_roll_header->hide();
380 /* We need to do this after changing visibility of our stuff, as it will
381 eventually trigger a call to Editor::reset_controls_layout_width(),
382 which needs to know if we have just shown or hidden a scroomer /
385 RouteTimeAxisView::set_height (h);
389 MidiTimeAxisView::append_extra_display_menu_items ()
391 using namespace Menu_Helpers;
393 MenuList& items = display_menu->items();
396 Menu *range_menu = manage(new Menu);
397 MenuList& range_items = range_menu->items();
398 range_menu->set_name ("ArdourContextMenu");
400 range_items.push_back (MenuElem (_("Show Full Range"), sigc::bind (
401 sigc::mem_fun(*this, &MidiTimeAxisView::set_note_range),
402 MidiStreamView::FullRange, true)));
404 range_items.push_back (MenuElem (_("Fit Contents"), sigc::bind (
405 sigc::mem_fun(*this, &MidiTimeAxisView::set_note_range),
406 MidiStreamView::ContentsRange, true)));
408 items.push_back (MenuElem (_("Note Range"), *range_menu));
409 items.push_back (MenuElem (_("Note Mode"), *build_note_mode_menu()));
411 items.push_back (SeparatorElem ());
415 MidiTimeAxisView::build_automation_action_menu (bool for_selection)
417 using namespace Menu_Helpers;
419 /* If we have a controller menu, we need to detach it before
420 RouteTimeAxis::build_automation_action_menu destroys the
421 menu it is attached to. Otherwise GTK destroys
422 controller_menu's gobj, meaning that it can't be reattached
423 below. See bug #3134.
426 if (controller_menu) {
427 detach_menu (*controller_menu);
430 _channel_command_menu_map.clear ();
431 RouteTimeAxisView::build_automation_action_menu (for_selection);
433 MenuList& automation_items = automation_action_menu->items();
435 uint16_t selected_channels = _channel_selector.get_selected_channels();
437 if (selected_channels != 0) {
439 automation_items.push_back (SeparatorElem());
441 /* these 2 MIDI "command" types are semantically more like automation than note data,
442 but they are not MIDI controllers. We give them special status in this menu, since
443 they will not show up in the controller list and anyone who actually knows
444 something about MIDI (!) would not expect to find them there.
447 add_channel_command_menu_item (automation_items, _("Bender"), MidiPitchBenderAutomation, 0);
448 automation_items.back().set_sensitive (!for_selection || _editor.get_selection().tracks.size() == 1);
449 add_channel_command_menu_item (automation_items, _("Pressure"), MidiChannelPressureAutomation, 0);
450 automation_items.back().set_sensitive (!for_selection || _editor.get_selection().tracks.size() == 1);
452 /* now all MIDI controllers. Always offer the possibility that we will rebuild the controllers menu
453 since it might need to be updated after a channel mode change or other change. Also detach it
454 first in case it has been used anywhere else.
457 build_controller_menu ();
459 automation_items.push_back (SeparatorElem());
460 automation_items.push_back (MenuElem (_("Controllers"), *controller_menu));
461 automation_items.back().set_sensitive (!for_selection || _editor.get_selection().tracks.size() == 1);
463 automation_items.push_back (MenuElem (string_compose ("<i>%1</i>", _("No MIDI Channels selected"))));
464 dynamic_cast<Label*> (automation_items.back().get_child())->set_use_markup (true);
470 MidiTimeAxisView::change_all_channel_tracks_visibility (bool yn, Evoral::Parameter param)
472 uint16_t selected_channels = _channel_selector.get_selected_channels();
474 for (uint8_t chn = 0; chn < 16; chn++) {
475 if (selected_channels & (0x0001 << chn)) {
477 Evoral::Parameter fully_qualified_param (param.type(), chn, param.id());
478 Gtk::CheckMenuItem* menu = automation_child_menu_item (fully_qualified_param);
481 menu->set_active (yn);
488 MidiTimeAxisView::add_channel_command_menu_item (Menu_Helpers::MenuList& items, const string& label, AutomationType auto_type, uint8_t cmd)
490 using namespace Menu_Helpers;
492 /* count the number of selected channels because we will build a different menu structure if there is more than 1 selected.
495 uint16_t selected_channels = _channel_selector.get_selected_channels();
498 for (uint8_t chn = 0; chn < 16; chn++) {
499 if (selected_channels & (0x0001 << chn)) {
508 /* multiple channels - create a submenu, with 1 item per channel */
510 Menu* chn_menu = manage (new Menu);
511 MenuList& chn_items (chn_menu->items());
512 Evoral::Parameter param_without_channel (auto_type, 0, cmd);
514 /* add a couple of items to hide/show all of them */
516 chn_items.push_back (MenuElem (_("Hide all channels"),
517 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
518 false, param_without_channel)));
519 chn_items.push_back (MenuElem (_("Show all channels"),
520 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
521 true, param_without_channel)));
523 for (uint8_t chn = 0; chn < 16; chn++) {
524 if (selected_channels & (0x0001 << chn)) {
526 /* for each selected channel, add a menu item for this controller */
528 Evoral::Parameter fully_qualified_param (auto_type, chn, cmd);
529 chn_items.push_back (CheckMenuElem (string_compose (_("Channel %1"), chn+1),
530 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
531 fully_qualified_param)));
533 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
534 bool visible = false;
537 if (track->marked_for_display()) {
542 CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&chn_items.back());
543 _channel_command_menu_map[fully_qualified_param] = cmi;
544 cmi->set_active (visible);
548 /* now create an item in the parent menu that has the per-channel list as a submenu */
550 items.push_back (MenuElem (label, *chn_menu));
554 /* just one channel - create a single menu item for this command+channel combination*/
556 for (uint8_t chn = 0; chn < 16; chn++) {
557 if (selected_channels & (0x0001 << chn)) {
559 Evoral::Parameter fully_qualified_param (auto_type, chn, cmd);
560 items.push_back (CheckMenuElem (label,
561 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
562 fully_qualified_param)));
564 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
565 bool visible = false;
568 if (track->marked_for_display()) {
573 CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&items.back());
574 _channel_command_menu_map[fully_qualified_param] = cmi;
575 cmi->set_active (visible);
577 /* one channel only */
585 MidiTimeAxisView::build_controller_menu ()
587 using namespace Menu_Helpers;
589 if (controller_menu) {
590 /* it exists and has not been invalidated by a channel mode change, so just return it */
594 controller_menu = new Menu; // explicitly managed by us
595 MenuList& items (controller_menu->items());
597 /* create several "top level" menu items for sets of controllers (16 at a time), and populate each one with a submenu
598 for each controller+channel combination covering the currently selected channels for this track
601 uint16_t selected_channels = _channel_selector.get_selected_channels();
603 /* count the number of selected channels because we will build a different menu structure if there is more than 1 selected.
608 for (uint8_t chn = 0; chn < 16; chn++) {
609 if (selected_channels & (0x0001 << chn)) {
616 /* loop over all 127 MIDI controllers, in groups of 16; except don't offer
617 bank select controllers, as they are handled by the `patch' code */
619 for (int i = 0; i < 127; i += 16) {
621 Menu* ctl_menu = manage (new Menu);
622 MenuList& ctl_items (ctl_menu->items());
625 /* for each controller, consider whether to create a submenu or a single item */
627 for (int ctl = i; ctl < i+16; ++ctl) {
629 if (ctl == MIDI_CTL_MSB_BANK || ctl == MIDI_CTL_LSB_BANK) {
635 /* multiple channels - create a submenu, with 1 item per channel */
637 Menu* chn_menu = manage (new Menu);
638 MenuList& chn_items (chn_menu->items());
640 /* add a couple of items to hide/show this controller on all channels */
642 Evoral::Parameter param_without_channel (MidiCCAutomation, 0, ctl);
643 chn_items.push_back (MenuElem (_("Hide all channels"),
644 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
645 false, param_without_channel)));
646 chn_items.push_back (MenuElem (_("Show all channels"),
647 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
648 true, param_without_channel)));
650 for (uint8_t chn = 0; chn < 16; chn++) {
651 if (selected_channels & (0x0001 << chn)) {
653 /* for each selected channel, add a menu item for this controller */
655 Evoral::Parameter fully_qualified_param (MidiCCAutomation, chn, ctl);
656 chn_items.push_back (CheckMenuElem (string_compose (_("Channel %1"), chn+1),
657 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
658 fully_qualified_param)));
660 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
661 bool visible = false;
664 if (track->marked_for_display()) {
669 CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&chn_items.back());
670 _controller_menu_map[fully_qualified_param] = cmi;
671 cmi->set_active (visible);
675 /* add the per-channel menu to the list of controllers, with the name of the controller */
676 ctl_items.push_back (MenuElem (string_compose ("<b>%1</b>: %2", ctl, midi_name (ctl)), *chn_menu));
677 dynamic_cast<Label*> (ctl_items.back().get_child())->set_use_markup (true);
681 /* just one channel - create a single menu item for this ctl+channel combination*/
683 for (uint8_t chn = 0; chn < 16; chn++) {
684 if (selected_channels & (0x0001 << chn)) {
686 Evoral::Parameter fully_qualified_param (MidiCCAutomation, chn, ctl);
687 ctl_items.push_back (
689 string_compose ("<b>%1</b>: %2 [%3]", ctl, midi_name (ctl), int (chn)),
690 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
691 fully_qualified_param)
694 dynamic_cast<Label*> (ctl_items.back().get_child())->set_use_markup (true);
696 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
697 bool visible = false;
700 if (track->marked_for_display()) {
705 CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&ctl_items.back());
706 _controller_menu_map[fully_qualified_param] = cmi;
707 cmi->set_active (visible);
709 /* one channel only */
716 /* add the menu for this block of controllers to the overall controller menu */
718 items.push_back (MenuElem (string_compose (_("Controllers %1-%2"), i, i+15), *ctl_menu));
723 MidiTimeAxisView::build_note_mode_menu()
725 using namespace Menu_Helpers;
727 Menu* mode_menu = manage (new Menu);
728 MenuList& items = mode_menu->items();
729 mode_menu->set_name ("ArdourContextMenu");
731 RadioMenuItem::Group mode_group;
732 items.push_back (RadioMenuElem (mode_group,_("Sustained"),
733 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_mode), Sustained, true)));
734 _note_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
735 _note_mode_item->set_active(_note_mode == Sustained);
737 items.push_back (RadioMenuElem (mode_group, _("Percussive"),
738 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_mode), Percussive, true)));
739 _percussion_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
740 _percussion_mode_item->set_active(_note_mode == Percussive);
746 MidiTimeAxisView::build_color_mode_menu()
748 using namespace Menu_Helpers;
750 Menu* mode_menu = manage (new Menu);
751 MenuList& items = mode_menu->items();
752 mode_menu->set_name ("ArdourContextMenu");
754 RadioMenuItem::Group mode_group;
755 items.push_back (RadioMenuElem (mode_group, _("Meter Colors"),
756 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode),
757 MeterColors, false, true, true)));
758 _meter_color_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
759 _meter_color_mode_item->set_active(_color_mode == MeterColors);
761 items.push_back (RadioMenuElem (mode_group, _("Channel Colors"),
762 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode),
763 ChannelColors, false, true, true)));
764 _channel_color_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
765 _channel_color_mode_item->set_active(_color_mode == ChannelColors);
767 items.push_back (RadioMenuElem (mode_group, _("Track Color"),
768 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode),
769 TrackColor, false, true, true)));
770 _channel_color_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
771 _channel_color_mode_item->set_active(_color_mode == TrackColor);
777 MidiTimeAxisView::set_note_mode(NoteMode mode, bool apply_to_selection)
779 if (apply_to_selection) {
780 _editor.get_selection().tracks.foreach_midi_time_axis (boost::bind (&MidiTimeAxisView::set_note_mode, _1, mode, false));
782 if (_note_mode != mode || midi_track()->note_mode() != mode) {
784 midi_track()->set_note_mode(mode);
785 set_gui_property ("note-mode", enum_2_string(_note_mode));
786 _view->redisplay_track();
792 MidiTimeAxisView::set_color_mode (ColorMode mode, bool force, bool redisplay, bool apply_to_selection)
794 if (apply_to_selection) {
795 _editor.get_selection().tracks.foreach_midi_time_axis (
796 boost::bind (&MidiTimeAxisView::set_color_mode, _1, mode, force, redisplay, false)
800 if (_color_mode == mode && !force) {
804 if (mode == ChannelColors) {
805 _channel_selector.set_channel_colors(CanvasNoteEvent::midi_channel_colors);
807 _channel_selector.set_default_channel_color();
811 set_gui_property ("color-mode", enum_2_string(_color_mode));
813 _view->redisplay_track();
819 MidiTimeAxisView::set_note_range (MidiStreamView::VisibleNoteRange range, bool apply_to_selection)
821 if (apply_to_selection) {
822 _editor.get_selection().tracks.foreach_midi_time_axis (
823 boost::bind (&MidiTimeAxisView::set_note_range, _1, range, false)
826 if (!_ignore_signals) {
827 midi_view()->set_note_range(range);
833 MidiTimeAxisView::update_range()
835 MidiGhostRegion* mgr;
837 for(list<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
838 if ((mgr = dynamic_cast<MidiGhostRegion*>(*i)) != 0) {
845 MidiTimeAxisView::show_all_automation (bool apply_to_selection)
847 if (apply_to_selection) {
848 _editor.get_selection().tracks.foreach_midi_time_axis (boost::bind (&MidiTimeAxisView::show_all_automation, _1, false));
851 const set<Evoral::Parameter> params = midi_track()->midi_playlist()->contained_automation();
853 for (set<Evoral::Parameter>::const_iterator i = params.begin(); i != params.end(); ++i) {
854 create_automation_child(*i, true);
858 RouteTimeAxisView::show_all_automation ();
863 MidiTimeAxisView::show_existing_automation (bool apply_to_selection)
865 if (apply_to_selection) {
866 _editor.get_selection().tracks.foreach_midi_time_axis (boost::bind (&MidiTimeAxisView::show_existing_automation, _1, false));
869 const set<Evoral::Parameter> params = midi_track()->midi_playlist()->contained_automation();
871 for (set<Evoral::Parameter>::const_iterator i = params.begin(); i != params.end(); ++i) {
872 create_automation_child (*i, true);
876 RouteTimeAxisView::show_existing_automation ();
880 /** Create an automation track for the given parameter (pitch bend, channel pressure).
883 MidiTimeAxisView::create_automation_child (const Evoral::Parameter& param, bool show)
885 if (param.type() == NullAutomation) {
889 AutomationTracks::iterator existing = _automation_tracks.find (param);
891 if (existing != _automation_tracks.end()) {
893 /* automation track created because we had existing data for
894 * the processor, but visibility may need to be controlled
895 * since it will have been set visible by default.
898 if (existing->second->set_marked_for_display (show) && !no_redraw) {
905 boost::shared_ptr<AutomationTimeAxisView> track;
907 switch (param.type()) {
910 create_gain_automation_child (param, show);
913 case PluginAutomation:
914 /* handled elsewhere */
917 case MidiCCAutomation:
918 case MidiPgmChangeAutomation:
919 case MidiPitchBenderAutomation:
920 case MidiChannelPressureAutomation:
921 case MidiSystemExclusiveAutomation:
922 /* These controllers are region "automation" - they are owned
923 * by regions (and their MidiModels), not by the track. As a
924 * result we do not create an AutomationList/Line for the track
925 * ... except here we are doing something!! XXX
928 track.reset (new AutomationTimeAxisView (
931 boost::shared_ptr<Automatable> (),
932 boost::shared_ptr<AutomationControl> (),
938 _route->describe_parameter(param)
942 _view->foreach_regionview (sigc::mem_fun (*track.get(), &TimeAxisView::add_ghost));
945 add_automation_child (param, track, show);
949 error << "MidiTimeAxisView: unknown automation child " << EventTypeMap::instance().to_symbol(param) << endmsg;
954 MidiTimeAxisView::route_active_changed ()
956 RouteUI::route_active_changed ();
959 if (_route->active()) {
960 controls_ebox.set_name ("MidiTrackControlsBaseUnselected");
961 controls_base_selected_name = "MidiTrackControlsBaseSelected";
962 controls_base_unselected_name = "MidiTrackControlsBaseUnselected";
964 controls_ebox.set_name ("MidiTrackControlsBaseInactiveUnselected");
965 controls_base_selected_name = "MidiTrackControlsBaseInactiveSelected";
966 controls_base_unselected_name = "MidiTrackControlsBaseInactiveUnselected";
972 if (_route->active()) {
973 controls_ebox.set_name ("BusControlsBaseUnselected");
974 controls_base_selected_name = "BusControlsBaseSelected";
975 controls_base_unselected_name = "BusControlsBaseUnselected";
977 controls_ebox.set_name ("BusControlsBaseInactiveUnselected");
978 controls_base_selected_name = "BusControlsBaseInactiveSelected";
979 controls_base_unselected_name = "BusControlsBaseInactiveUnselected";
985 MidiTimeAxisView::set_note_selection (uint8_t note)
987 if (!_editor.internal_editing()) {
991 uint16_t chn_mask = _channel_selector.get_selected_channels();
993 if (_view->num_selected_regionviews() == 0) {
994 _view->foreach_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_selection_region_view), note, chn_mask));
996 _view->foreach_selected_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_selection_region_view), note, chn_mask));
1001 MidiTimeAxisView::add_note_selection (uint8_t note)
1003 if (!_editor.internal_editing()) {
1007 uint16_t chn_mask = _channel_selector.get_selected_channels();
1009 if (_view->num_selected_regionviews() == 0) {
1010 _view->foreach_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection_region_view), note, chn_mask));
1012 _view->foreach_selected_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection_region_view), note, chn_mask));
1017 MidiTimeAxisView::extend_note_selection (uint8_t note)
1019 if (!_editor.internal_editing()) {
1023 uint16_t chn_mask = _channel_selector.get_selected_channels();
1025 if (_view->num_selected_regionviews() == 0) {
1026 _view->foreach_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::extend_note_selection_region_view), note, chn_mask));
1028 _view->foreach_selected_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::extend_note_selection_region_view), note, chn_mask));
1033 MidiTimeAxisView::toggle_note_selection (uint8_t note)
1035 if (!_editor.internal_editing()) {
1039 uint16_t chn_mask = _channel_selector.get_selected_channels();
1041 if (_view->num_selected_regionviews() == 0) {
1042 _view->foreach_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection_region_view), note, chn_mask));
1044 _view->foreach_selected_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection_region_view), note, chn_mask));
1049 MidiTimeAxisView::set_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1051 dynamic_cast<MidiRegionView*>(rv)->select_matching_notes (note, chn_mask, false, false);
1055 MidiTimeAxisView::add_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1057 dynamic_cast<MidiRegionView*>(rv)->select_matching_notes (note, chn_mask, true, false);
1061 MidiTimeAxisView::extend_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1063 dynamic_cast<MidiRegionView*>(rv)->select_matching_notes (note, chn_mask, true, true);
1067 MidiTimeAxisView::toggle_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1069 dynamic_cast<MidiRegionView*>(rv)->toggle_matching_notes (note, chn_mask);
1073 MidiTimeAxisView::set_channel_mode (ChannelMode, uint16_t)
1075 /* hide all automation tracks that use the wrong channel(s) and show all those that use
1079 uint16_t selected_channels = _channel_selector.get_selected_channels();
1080 bool changed = false;
1084 for (uint32_t ctl = 0; ctl < 127; ++ctl) {
1086 for (uint32_t chn = 0; chn < 16; ++chn) {
1087 Evoral::Parameter fully_qualified_param (MidiCCAutomation, chn, ctl);
1088 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
1094 if ((selected_channels & (0x0001 << chn)) == 0) {
1095 /* channel not in use. hiding it will trigger RouteTimeAxisView::automation_track_hidden()
1096 which will cause a redraw. We don't want one per channel, so block that with no_redraw.
1098 changed = track->set_marked_for_display (false) || changed;
1100 changed = track->set_marked_for_display (true) || changed;
1107 /* TODO: Bender, Pressure */
1109 /* invalidate the controller menu, so that we rebuild it next time */
1110 _controller_menu_map.clear ();
1111 delete controller_menu;
1112 controller_menu = 0;
1120 MidiTimeAxisView::automation_child_menu_item (Evoral::Parameter param)
1122 Gtk::CheckMenuItem* m = RouteTimeAxisView::automation_child_menu_item (param);
1127 ParameterMenuMap::iterator i = _controller_menu_map.find (param);
1128 if (i != _controller_menu_map.end()) {
1132 i = _channel_command_menu_map.find (param);
1133 if (i != _channel_command_menu_map.end()) {
1140 boost::shared_ptr<MidiRegion>
1141 MidiTimeAxisView::add_region (framepos_t pos, framecnt_t length, bool commit)
1143 Editor* real_editor = dynamic_cast<Editor*> (&_editor);
1145 real_editor->begin_reversible_command (Operations::create_region);
1146 playlist()->clear_changes ();
1148 real_editor->snap_to (pos, 0);
1150 boost::shared_ptr<Source> src = _session->create_midi_source_for_session (view()->trackview().track().get(),
1151 view()->trackview().track()->name());
1154 plist.add (ARDOUR::Properties::start, 0);
1155 plist.add (ARDOUR::Properties::length, length);
1156 plist.add (ARDOUR::Properties::name, PBD::basename_nosuffix(src->name()));
1158 boost::shared_ptr<Region> region = (RegionFactory::create (src, plist));
1160 playlist()->add_region (region, pos);
1161 _session->add_command (new StatefulDiffCommand (playlist()));
1164 real_editor->commit_reversible_command ();
1167 return boost::dynamic_pointer_cast<MidiRegion>(region);
1171 MidiTimeAxisView::ensure_step_editor ()
1173 if (!_step_editor) {
1174 _step_editor = new StepEditor (_editor, midi_track(), *this);
1179 MidiTimeAxisView::start_step_editing ()
1181 ensure_step_editor ();
1182 _step_editor->start_step_editing ();
1186 MidiTimeAxisView::stop_step_editing ()
1189 _step_editor->stop_step_editing ();
1194 /** @return channel (counted from 0) to add an event to, based on the current setting
1195 * of the channel selector.
1198 MidiTimeAxisView::get_channel_for_add () const
1200 uint16_t const chn_mask = _channel_selector.get_selected_channels ();
1202 uint8_t channel = 0;
1204 /* pick the highest selected channel, unless all channels are selected,
1205 which is interpreted to mean channel 1 (zero)
1208 for (uint16_t i = 0; i < 16; ++i) {
1209 if (chn_mask & (1<<i)) {
1215 if (chn_cnt == 16) {
1223 MidiTimeAxisView::note_range_changed ()
1225 set_gui_property ("note-range-min", (int) midi_view()->lowest_note ());
1226 set_gui_property ("note-range-max", (int) midi_view()->highest_note ());
1230 MidiTimeAxisView::contents_height_changed ()
1232 _range_scroomer->set_size_request (-1, _view->child_height ());