2 Copyright (C) 2000 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
26 #include <sigc++/bind.h>
28 #include "pbd/error.h"
29 #include "pbd/stl_delete.h"
30 #include "pbd/whitespace.h"
31 #include "pbd/basename.h"
32 #include "pbd/enumwriter.h"
33 #include "pbd/memento_command.h"
34 #include "pbd/stateful_diff_command.h"
36 #include "gtkmm2ext/gtk_ui.h"
37 #include "gtkmm2ext/selector.h"
38 #include "gtkmm2ext/bindable_button.h"
39 #include "gtkmm2ext/utils.h"
41 #include "ardour/event_type_map.h"
42 #include "ardour/midi_patch_manager.h"
43 #include "ardour/midi_playlist.h"
44 #include "ardour/midi_region.h"
45 #include "ardour/midi_source.h"
46 #include "ardour/midi_track.h"
47 #include "ardour/operations.h"
48 #include "ardour/playlist.h"
49 #include "ardour/region.h"
50 #include "ardour/region_factory.h"
51 #include "ardour/route.h"
52 #include "ardour/session.h"
53 #include "ardour/session_object.h"
54 #include "ardour/source.h"
55 #include "ardour/track.h"
56 #include "ardour/types.h"
58 #include "midi++/names.h"
60 #include "ardour_ui.h"
61 #include "ardour_button.h"
62 #include "automation_line.h"
63 #include "automation_time_axis.h"
64 #include "canvas-note-event.h"
65 #include "canvas_impl.h"
68 #include "ghostregion.h"
69 #include "gui_thread.h"
71 #include "midi_scroomer.h"
72 #include "midi_streamview.h"
73 #include "midi_region_view.h"
74 #include "midi_time_axis.h"
75 #include "piano_roll_header.h"
76 #include "playlist_selector.h"
77 #include "plugin_selector.h"
78 #include "plugin_ui.h"
79 #include "point_selection.h"
81 #include "region_view.h"
82 #include "rgb_macros.h"
83 #include "selection.h"
84 #include "step_editor.h"
85 #include "simplerect.h"
88 #include "ardour/midi_track.h"
92 using namespace ARDOUR;
95 using namespace Gtkmm2ext;
96 using namespace Editing;
98 // Minimum height at which a control is displayed
99 static const uint32_t MIDI_CONTROLS_BOX_MIN_HEIGHT = 162;
100 static const uint32_t KEYBOARD_MIN_HEIGHT = 140;
102 MidiTimeAxisView::MidiTimeAxisView (PublicEditor& ed, Session* sess, Canvas& canvas)
103 : AxisView(sess) // virtually inherited
104 , RouteTimeAxisView(ed, sess, canvas)
105 , _ignore_signals(false)
107 , _piano_roll_header(0)
108 , _note_mode(Sustained)
110 , _percussion_mode_item(0)
111 , _color_mode(MeterColors)
112 , _meter_color_mode_item(0)
113 , _channel_color_mode_item(0)
114 , _track_color_mode_item(0)
115 , _step_edit_item (0)
116 , _midi_thru_item (0)
117 , controller_menu (0)
123 MidiTimeAxisView::set_route (boost::shared_ptr<Route> rt)
127 _view = new MidiStreamView (*this);
130 _piano_roll_header = new PianoRollHeader(*midi_view());
131 _range_scroomer = new MidiScroomer(midi_view()->note_range_adjustment);
134 /* This next call will result in our height being set up, so it must come after
135 the creation of the piano roll / range scroomer as their visibility is set up
138 RouteTimeAxisView::set_route (rt);
140 _view->apply_color (_color, StreamView::RegionColor);
142 subplugin_menu.set_name ("ArdourContextMenu");
144 if (!gui_property ("note-range-min").empty ()) {
145 midi_view()->apply_note_range (atoi (gui_property ("note-range-min").c_str()), atoi (gui_property ("note-range-max").c_str()), true);
147 midi_view()->NoteRangeChanged.connect (sigc::mem_fun (*this, &MidiTimeAxisView::note_range_changed));
148 _view->ContentsHeightChanged.connect (sigc::mem_fun (*this, &MidiTimeAxisView::contents_height_changed));
150 ignore_toggle = false;
152 if (is_midi_track()) {
153 controls_ebox.set_name ("MidiTimeAxisViewControlsBaseUnselected");
154 _note_mode = midi_track()->note_mode();
155 } else { // MIDI bus (which doesn't exist yet..)
156 controls_ebox.set_name ("MidiBusControlsBaseUnselected");
159 /* map current state of the route */
161 processors_changed (RouteProcessorChange ());
163 _route->processors_changed.connect (*this, invalidator (*this), boost::bind (&MidiTimeAxisView::processors_changed, this, _1), gui_context());
166 _piano_roll_header->SetNoteSelection.connect (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_selection));
167 _piano_roll_header->AddNoteSelection.connect (sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection));
168 _piano_roll_header->ExtendNoteSelection.connect (sigc::mem_fun (*this, &MidiTimeAxisView::extend_note_selection));
169 _piano_roll_header->ToggleNoteSelection.connect (sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection));
171 /* Suspend updates of the StreamView during scroomer drags to speed things up */
172 _range_scroomer->DragStarting.connect (sigc::mem_fun (*midi_view(), &MidiStreamView::suspend_updates));
173 _range_scroomer->DragFinishing.connect (sigc::mem_fun (*midi_view(), &MidiStreamView::resume_updates));
175 /* Put the scroomer in a VBox with a padding label so that it can be reduced in height
176 for stacked-view tracks.
178 VBox* b = manage (new VBox);
179 b->pack_start (*_range_scroomer, false, false);
180 b->pack_start (*manage (new Label ("")), true, true);
181 controls_hbox.pack_start(*b);
182 controls_hbox.pack_start(*_piano_roll_header);
184 controls_ebox.set_name ("MidiTrackControlsBaseUnselected");
185 controls_base_selected_name = "MidiTrackControlsBaseSelected";
186 controls_base_unselected_name = "MidiTrackControlsBaseUnselected";
188 midi_view()->NoteRangeChanged.connect (sigc::mem_fun(*this, &MidiTimeAxisView::update_range));
190 /* ask for notifications of any new RegionViews */
191 _view->RegionViewAdded.connect (sigc::mem_fun(*this, &MidiTimeAxisView::region_view_added));
193 if (!_editor.have_idled()) {
194 /* first idle will do what we need */
200 HBox* midi_controls_hbox = manage(new HBox());
202 MIDI::Name::MidiPatchManager& patch_manager = MIDI::Name::MidiPatchManager::instance();
204 MIDI::Name::MasterDeviceNames::Models::const_iterator m = patch_manager.all_models().begin();
205 for (; m != patch_manager.all_models().end(); ++m) {
206 _model_selector.append_text(m->c_str());
210 _model_selector.signal_changed().connect(sigc::mem_fun(*this, &MidiTimeAxisView::model_changed));
212 _custom_device_mode_selector.signal_changed().connect(
213 sigc::mem_fun(*this, &MidiTimeAxisView::custom_device_mode_changed));
215 _model_selector.set_active_text (gui_property (X_("midnam-model-name")));
216 _custom_device_mode_selector.set_active_text (gui_property (X_("midnam-custom-device-mode")));
218 ARDOUR_UI::instance()->set_tip (_model_selector, _("External MIDI Device"));
219 ARDOUR_UI::instance()->set_tip (_custom_device_mode_selector, _("External Device Mode"));
221 midi_controls_hbox->pack_start(_channel_selector, true, false);
222 if (!patch_manager.all_models().empty()) {
223 _midi_controls_box.set_border_width (5);
224 _midi_controls_box.pack_start(_model_selector, true, false);
225 _midi_controls_box.pack_start(_custom_device_mode_selector, true, false);
228 _midi_controls_box.pack_start(*midi_controls_hbox, true, true);
230 controls_vbox.pack_start(_midi_controls_box, false, false);
232 // restore channel selector settings
233 _channel_selector.set_channel_mode(midi_track()->get_channel_mode(), midi_track()->get_channel_mask());
234 _channel_selector.mode_changed.connect(
235 sigc::mem_fun(*midi_track(), &MidiTrack::set_channel_mode));
236 _channel_selector.mode_changed.connect(
237 sigc::mem_fun(*this, &MidiTimeAxisView::set_channel_mode));
239 string prop = gui_property ("color-mode");
241 _color_mode = ColorMode (string_2_enum(prop, _color_mode));
242 if (_color_mode == ChannelColors) {
243 _channel_selector.set_channel_colors(CanvasNoteEvent::midi_channel_colors);
247 set_color_mode (_color_mode, true, false);
249 prop = gui_property ("note-mode");
251 _note_mode = NoteMode (string_2_enum (prop, _note_mode));
252 if (_percussion_mode_item) {
253 _percussion_mode_item->set_active (_note_mode == Percussive);
257 /* Look for any GUI object state nodes that represent automation children that should exist, and create
261 list<string> gui_ids = gui_object_state().all_ids ();
262 for (list<string>::const_iterator i = gui_ids.begin(); i != gui_ids.end(); ++i) {
265 Evoral::Parameter parameter (0, 0, 0);
267 bool const p = AutomationTimeAxisView::parse_state_id (*i, route_id, has_parameter, parameter);
268 if (p && route_id == _route->id () && has_parameter) {
269 create_automation_child (parameter, string_is_affirmative (gui_object_state().get_string (*i, X_("visible"))));
275 MidiTimeAxisView::first_idle ()
282 MidiTimeAxisView::~MidiTimeAxisView ()
284 delete _piano_roll_header;
285 _piano_roll_header = 0;
287 delete _range_scroomer;
290 delete controller_menu;
295 MidiTimeAxisView::enter_internal_edit_mode ()
298 midi_view()->enter_internal_edit_mode ();
303 MidiTimeAxisView::leave_internal_edit_mode ()
306 midi_view()->leave_internal_edit_mode ();
311 MidiTimeAxisView::check_step_edit ()
313 ensure_step_editor ();
314 _step_editor->check_step_edit ();
318 MidiTimeAxisView::model_changed()
320 std::list<std::string> device_modes = MIDI::Name::MidiPatchManager::instance()
321 .custom_device_mode_names_by_model(_model_selector.get_active_text());
323 _custom_device_mode_selector.clear_items();
325 for (std::list<std::string>::const_iterator i = device_modes.begin();
326 i != device_modes.end(); ++i) {
327 _custom_device_mode_selector.append_text(*i);
330 _custom_device_mode_selector.set_active(0);
332 _route->instrument_info().set_external_instrument (_model_selector.get_active_text(), _custom_device_mode_selector.get_active_text());
336 MidiTimeAxisView::custom_device_mode_changed()
338 _route->instrument_info().set_external_instrument (_model_selector.get_active_text(), _custom_device_mode_selector.get_active_text());
342 MidiTimeAxisView::midi_view()
344 return dynamic_cast<MidiStreamView*>(_view);
348 MidiTimeAxisView::set_height (uint32_t h)
350 if (h >= MIDI_CONTROLS_BOX_MIN_HEIGHT) {
351 _midi_controls_box.show_all ();
353 _midi_controls_box.hide();
356 if (h >= KEYBOARD_MIN_HEIGHT) {
357 if (is_track() && _range_scroomer) {
358 _range_scroomer->show();
360 if (is_track() && _piano_roll_header) {
361 _piano_roll_header->show();
364 if (is_track() && _range_scroomer) {
365 _range_scroomer->hide();
367 if (is_track() && _piano_roll_header) {
368 _piano_roll_header->hide();
372 /* We need to do this after changing visibility of our stuff, as it will
373 eventually trigger a call to Editor::reset_controls_layout_width(),
374 which needs to know if we have just shown or hidden a scroomer /
377 RouteTimeAxisView::set_height (h);
381 MidiTimeAxisView::append_extra_display_menu_items ()
383 using namespace Menu_Helpers;
385 MenuList& items = display_menu->items();
388 Menu *range_menu = manage(new Menu);
389 MenuList& range_items = range_menu->items();
390 range_menu->set_name ("ArdourContextMenu");
392 range_items.push_back (MenuElem (_("Show Full Range"), sigc::bind (
393 sigc::mem_fun(*this, &MidiTimeAxisView::set_note_range),
394 MidiStreamView::FullRange, true)));
396 range_items.push_back (MenuElem (_("Fit Contents"), sigc::bind (
397 sigc::mem_fun(*this, &MidiTimeAxisView::set_note_range),
398 MidiStreamView::ContentsRange, true)));
400 items.push_back (MenuElem (_("Note Range"), *range_menu));
401 items.push_back (MenuElem (_("Note Mode"), *build_note_mode_menu()));
403 items.push_back (CheckMenuElem (_("MIDI Thru"), sigc::mem_fun(*this, &MidiTimeAxisView::toggle_midi_thru)));
404 _midi_thru_item = dynamic_cast<CheckMenuItem*>(&items.back());
406 items.push_back (SeparatorElem ());
410 MidiTimeAxisView::toggle_midi_thru ()
412 if (!_midi_thru_item) {
416 bool view_yn = _midi_thru_item->get_active();
417 if (view_yn != midi_track()->midi_thru()) {
418 midi_track()->set_midi_thru (view_yn);
423 MidiTimeAxisView::build_automation_action_menu (bool for_selection)
425 using namespace Menu_Helpers;
427 /* If we have a controller menu, we need to detach it before
428 RouteTimeAxis::build_automation_action_menu destroys the
429 menu it is attached to. Otherwise GTK destroys
430 controller_menu's gobj, meaning that it can't be reattached
431 below. See bug #3134.
434 if (controller_menu) {
435 detach_menu (*controller_menu);
438 _channel_command_menu_map.clear ();
439 RouteTimeAxisView::build_automation_action_menu (for_selection);
441 MenuList& automation_items = automation_action_menu->items();
443 uint16_t selected_channels = _channel_selector.get_selected_channels();
445 if (selected_channels != 0) {
447 automation_items.push_back (SeparatorElem());
449 /* these 2 MIDI "command" types are semantically more like automation than note data,
450 but they are not MIDI controllers. We give them special status in this menu, since
451 they will not show up in the controller list and anyone who actually knows
452 something about MIDI (!) would not expect to find them there.
455 add_channel_command_menu_item (automation_items, _("Bender"), MidiPitchBenderAutomation, 0);
456 automation_items.back().set_sensitive (!for_selection || _editor.get_selection().tracks.size() == 1);
457 add_channel_command_menu_item (automation_items, _("Pressure"), MidiChannelPressureAutomation, 0);
458 automation_items.back().set_sensitive (!for_selection || _editor.get_selection().tracks.size() == 1);
460 /* now all MIDI controllers. Always offer the possibility that we will rebuild the controllers menu
461 since it might need to be updated after a channel mode change or other change. Also detach it
462 first in case it has been used anywhere else.
465 build_controller_menu ();
467 automation_items.push_back (SeparatorElem());
468 automation_items.push_back (MenuElem (_("Controllers"), *controller_menu));
469 automation_items.back().set_sensitive (!for_selection || _editor.get_selection().tracks.size() == 1);
471 automation_items.push_back (MenuElem (string_compose ("<i>%1</i>", _("No MIDI Channels selected"))));
472 dynamic_cast<Label*> (automation_items.back().get_child())->set_use_markup (true);
478 MidiTimeAxisView::change_all_channel_tracks_visibility (bool yn, Evoral::Parameter param)
480 uint16_t selected_channels = _channel_selector.get_selected_channels();
482 for (uint8_t chn = 0; chn < 16; chn++) {
483 if (selected_channels & (0x0001 << chn)) {
485 Evoral::Parameter fully_qualified_param (param.type(), chn, param.id());
486 Gtk::CheckMenuItem* menu = automation_child_menu_item (fully_qualified_param);
489 menu->set_active (yn);
496 MidiTimeAxisView::add_channel_command_menu_item (Menu_Helpers::MenuList& items, const string& label, AutomationType auto_type, uint8_t cmd)
498 using namespace Menu_Helpers;
500 /* count the number of selected channels because we will build a different menu structure if there is more than 1 selected.
503 uint16_t selected_channels = _channel_selector.get_selected_channels();
506 for (uint8_t chn = 0; chn < 16; chn++) {
507 if (selected_channels & (0x0001 << chn)) {
516 /* multiple channels - create a submenu, with 1 item per channel */
518 Menu* chn_menu = manage (new Menu);
519 MenuList& chn_items (chn_menu->items());
520 Evoral::Parameter param_without_channel (auto_type, 0, cmd);
522 /* add a couple of items to hide/show all of them */
524 chn_items.push_back (MenuElem (_("Hide all channels"),
525 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
526 false, param_without_channel)));
527 chn_items.push_back (MenuElem (_("Show all channels"),
528 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
529 true, param_without_channel)));
531 for (uint8_t chn = 0; chn < 16; chn++) {
532 if (selected_channels & (0x0001 << chn)) {
534 /* for each selected channel, add a menu item for this controller */
536 Evoral::Parameter fully_qualified_param (auto_type, chn, cmd);
537 chn_items.push_back (CheckMenuElem (string_compose (_("Channel %1"), chn+1),
538 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
539 fully_qualified_param)));
541 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
542 bool visible = false;
545 if (track->marked_for_display()) {
550 CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&chn_items.back());
551 _channel_command_menu_map[fully_qualified_param] = cmi;
552 cmi->set_active (visible);
556 /* now create an item in the parent menu that has the per-channel list as a submenu */
558 items.push_back (MenuElem (label, *chn_menu));
562 /* just one channel - create a single menu item for this command+channel combination*/
564 for (uint8_t chn = 0; chn < 16; chn++) {
565 if (selected_channels & (0x0001 << chn)) {
567 Evoral::Parameter fully_qualified_param (auto_type, chn, cmd);
568 items.push_back (CheckMenuElem (label,
569 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
570 fully_qualified_param)));
572 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
573 bool visible = false;
576 if (track->marked_for_display()) {
581 CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&items.back());
582 _channel_command_menu_map[fully_qualified_param] = cmi;
583 cmi->set_active (visible);
585 /* one channel only */
593 MidiTimeAxisView::build_controller_menu ()
595 using namespace Menu_Helpers;
597 if (controller_menu) {
598 /* it exists and has not been invalidated by a channel mode change, so just return it */
602 controller_menu = new Menu; // explicitly managed by us
603 MenuList& items (controller_menu->items());
605 /* create several "top level" menu items for sets of controllers (16 at a time), and populate each one with a submenu
606 for each controller+channel combination covering the currently selected channels for this track
609 uint16_t selected_channels = _channel_selector.get_selected_channels();
611 /* count the number of selected channels because we will build a different menu structure if there is more than 1 selected.
616 for (uint8_t chn = 0; chn < 16; chn++) {
617 if (selected_channels & (0x0001 << chn)) {
624 /* loop over all 127 MIDI controllers, in groups of 16; except don't offer
625 bank select controllers, as they are handled by the `patch' code */
627 for (int i = 0; i < 127; i += 16) {
629 Menu* ctl_menu = manage (new Menu);
630 MenuList& ctl_items (ctl_menu->items());
633 /* for each controller, consider whether to create a submenu or a single item */
635 for (int ctl = i; ctl < i+16; ++ctl) {
637 if (ctl == MIDI_CTL_MSB_BANK || ctl == MIDI_CTL_LSB_BANK) {
643 /* multiple channels - create a submenu, with 1 item per channel */
645 Menu* chn_menu = manage (new Menu);
646 MenuList& chn_items (chn_menu->items());
648 /* add a couple of items to hide/show this controller on all channels */
650 Evoral::Parameter param_without_channel (MidiCCAutomation, 0, ctl);
651 chn_items.push_back (MenuElem (_("Hide all channels"),
652 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
653 false, param_without_channel)));
654 chn_items.push_back (MenuElem (_("Show all channels"),
655 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
656 true, param_without_channel)));
658 for (uint8_t chn = 0; chn < 16; chn++) {
659 if (selected_channels & (0x0001 << chn)) {
661 /* for each selected channel, add a menu item for this controller */
663 Evoral::Parameter fully_qualified_param (MidiCCAutomation, chn, ctl);
664 chn_items.push_back (CheckMenuElem (string_compose (_("Channel %1"), chn+1),
665 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
666 fully_qualified_param)));
668 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
669 bool visible = false;
672 if (track->marked_for_display()) {
677 CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&chn_items.back());
678 _controller_menu_map[fully_qualified_param] = cmi;
679 cmi->set_active (visible);
683 /* add the per-channel menu to the list of controllers, with the name of the controller */
684 ctl_items.push_back (MenuElem (string_compose ("<b>%1</b>: %2", ctl, midi_name (ctl)), *chn_menu));
685 dynamic_cast<Label*> (ctl_items.back().get_child())->set_use_markup (true);
689 /* just one channel - create a single menu item for this ctl+channel combination*/
691 for (uint8_t chn = 0; chn < 16; chn++) {
692 if (selected_channels & (0x0001 << chn)) {
694 Evoral::Parameter fully_qualified_param (MidiCCAutomation, chn, ctl);
695 ctl_items.push_back (
697 string_compose ("<b>%1</b>: %2 [%3]", ctl, midi_name (ctl), int (chn)),
698 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
699 fully_qualified_param)
702 dynamic_cast<Label*> (ctl_items.back().get_child())->set_use_markup (true);
704 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
705 bool visible = false;
708 if (track->marked_for_display()) {
713 CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&ctl_items.back());
714 _controller_menu_map[fully_qualified_param] = cmi;
715 cmi->set_active (visible);
717 /* one channel only */
724 /* add the menu for this block of controllers to the overall controller menu */
726 items.push_back (MenuElem (string_compose (_("Controllers %1-%2"), i, i+15), *ctl_menu));
731 MidiTimeAxisView::build_note_mode_menu()
733 using namespace Menu_Helpers;
735 Menu* mode_menu = manage (new Menu);
736 MenuList& items = mode_menu->items();
737 mode_menu->set_name ("ArdourContextMenu");
739 RadioMenuItem::Group mode_group;
740 items.push_back (RadioMenuElem (mode_group,_("Sustained"),
741 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_mode), Sustained, true)));
742 _note_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
743 _note_mode_item->set_active(_note_mode == Sustained);
745 items.push_back (RadioMenuElem (mode_group, _("Percussive"),
746 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_mode), Percussive, true)));
747 _percussion_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
748 _percussion_mode_item->set_active(_note_mode == Percussive);
754 MidiTimeAxisView::build_color_mode_menu()
756 using namespace Menu_Helpers;
758 Menu* mode_menu = manage (new Menu);
759 MenuList& items = mode_menu->items();
760 mode_menu->set_name ("ArdourContextMenu");
762 RadioMenuItem::Group mode_group;
763 items.push_back (RadioMenuElem (mode_group, _("Meter Colors"),
764 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode),
765 MeterColors, false, true, true)));
766 _meter_color_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
767 _meter_color_mode_item->set_active(_color_mode == MeterColors);
769 items.push_back (RadioMenuElem (mode_group, _("Channel Colors"),
770 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode),
771 ChannelColors, false, true, true)));
772 _channel_color_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
773 _channel_color_mode_item->set_active(_color_mode == ChannelColors);
775 items.push_back (RadioMenuElem (mode_group, _("Track Color"),
776 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode),
777 TrackColor, false, true, true)));
778 _channel_color_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
779 _channel_color_mode_item->set_active(_color_mode == TrackColor);
785 MidiTimeAxisView::set_note_mode(NoteMode mode, bool apply_to_selection)
787 if (apply_to_selection) {
788 _editor.get_selection().tracks.foreach_midi_time_axis (boost::bind (&MidiTimeAxisView::set_note_mode, _1, mode, false));
790 if (_note_mode != mode || midi_track()->note_mode() != mode) {
792 midi_track()->set_note_mode(mode);
793 set_gui_property ("note-mode", enum_2_string(_note_mode));
794 _view->redisplay_track();
800 MidiTimeAxisView::set_color_mode (ColorMode mode, bool force, bool redisplay, bool apply_to_selection)
802 if (apply_to_selection) {
803 _editor.get_selection().tracks.foreach_midi_time_axis (
804 boost::bind (&MidiTimeAxisView::set_color_mode, _1, mode, force, redisplay, false)
808 if (_color_mode == mode && !force) {
812 if (mode == ChannelColors) {
813 _channel_selector.set_channel_colors(CanvasNoteEvent::midi_channel_colors);
815 _channel_selector.set_default_channel_color();
819 set_gui_property ("color-mode", enum_2_string(_color_mode));
821 _view->redisplay_track();
827 MidiTimeAxisView::set_note_range (MidiStreamView::VisibleNoteRange range, bool apply_to_selection)
829 if (apply_to_selection) {
830 _editor.get_selection().tracks.foreach_midi_time_axis (
831 boost::bind (&MidiTimeAxisView::set_note_range, _1, range, false)
834 if (!_ignore_signals) {
835 midi_view()->set_note_range(range);
841 MidiTimeAxisView::update_range()
843 MidiGhostRegion* mgr;
845 for(list<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
846 if ((mgr = dynamic_cast<MidiGhostRegion*>(*i)) != 0) {
853 MidiTimeAxisView::show_all_automation (bool apply_to_selection)
855 if (apply_to_selection) {
856 _editor.get_selection().tracks.foreach_midi_time_axis (boost::bind (&MidiTimeAxisView::show_all_automation, _1, false));
859 const set<Evoral::Parameter> params = midi_track()->midi_playlist()->contained_automation();
861 for (set<Evoral::Parameter>::const_iterator i = params.begin(); i != params.end(); ++i) {
862 create_automation_child(*i, true);
866 RouteTimeAxisView::show_all_automation ();
871 MidiTimeAxisView::show_existing_automation (bool apply_to_selection)
873 if (apply_to_selection) {
874 _editor.get_selection().tracks.foreach_midi_time_axis (boost::bind (&MidiTimeAxisView::show_existing_automation, _1, false));
877 const set<Evoral::Parameter> params = midi_track()->midi_playlist()->contained_automation();
879 for (set<Evoral::Parameter>::const_iterator i = params.begin(); i != params.end(); ++i) {
880 create_automation_child (*i, true);
884 RouteTimeAxisView::show_existing_automation ();
888 /** Create an automation track for the given parameter (pitch bend, channel pressure).
891 MidiTimeAxisView::create_automation_child (const Evoral::Parameter& param, bool show)
893 if (param.type() == NullAutomation) {
897 AutomationTracks::iterator existing = _automation_tracks.find (param);
899 if (existing != _automation_tracks.end()) {
901 /* automation track created because we had existing data for
902 * the processor, but visibility may need to be controlled
903 * since it will have been set visible by default.
906 if (existing->second->set_marked_for_display (show) && !no_redraw) {
913 boost::shared_ptr<AutomationTimeAxisView> track;
915 switch (param.type()) {
918 create_gain_automation_child (param, show);
921 case PluginAutomation:
922 /* handled elsewhere */
925 case MidiCCAutomation:
926 case MidiPgmChangeAutomation:
927 case MidiPitchBenderAutomation:
928 case MidiChannelPressureAutomation:
929 case MidiSystemExclusiveAutomation:
930 /* These controllers are region "automation" - they are owned
931 * by regions (and their MidiModels), not by the track. As a
932 * result we do not create an AutomationList/Line for the track
933 * ... except here we are doing something!! XXX
936 track.reset (new AutomationTimeAxisView (
939 boost::shared_ptr<Automatable> (),
940 boost::shared_ptr<AutomationControl> (),
946 _route->describe_parameter(param)
950 _view->foreach_regionview (sigc::mem_fun (*track.get(), &TimeAxisView::add_ghost));
953 add_automation_child (param, track, show);
957 error << "MidiTimeAxisView: unknown automation child " << EventTypeMap::instance().to_symbol(param) << endmsg;
962 MidiTimeAxisView::route_active_changed ()
964 RouteUI::route_active_changed ();
967 if (_route->active()) {
968 controls_ebox.set_name ("MidiTrackControlsBaseUnselected");
969 controls_base_selected_name = "MidiTrackControlsBaseSelected";
970 controls_base_unselected_name = "MidiTrackControlsBaseUnselected";
972 controls_ebox.set_name ("MidiTrackControlsBaseInactiveUnselected");
973 controls_base_selected_name = "MidiTrackControlsBaseInactiveSelected";
974 controls_base_unselected_name = "MidiTrackControlsBaseInactiveUnselected";
980 if (_route->active()) {
981 controls_ebox.set_name ("BusControlsBaseUnselected");
982 controls_base_selected_name = "BusControlsBaseSelected";
983 controls_base_unselected_name = "BusControlsBaseUnselected";
985 controls_ebox.set_name ("BusControlsBaseInactiveUnselected");
986 controls_base_selected_name = "BusControlsBaseInactiveSelected";
987 controls_base_unselected_name = "BusControlsBaseInactiveUnselected";
993 MidiTimeAxisView::set_note_selection (uint8_t note)
995 if (!_editor.internal_editing()) {
999 uint16_t chn_mask = _channel_selector.get_selected_channels();
1001 if (_view->num_selected_regionviews() == 0) {
1002 _view->foreach_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_selection_region_view), note, chn_mask));
1004 _view->foreach_selected_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_selection_region_view), note, chn_mask));
1009 MidiTimeAxisView::add_note_selection (uint8_t note)
1011 if (!_editor.internal_editing()) {
1015 uint16_t chn_mask = _channel_selector.get_selected_channels();
1017 if (_view->num_selected_regionviews() == 0) {
1018 _view->foreach_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection_region_view), note, chn_mask));
1020 _view->foreach_selected_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection_region_view), note, chn_mask));
1025 MidiTimeAxisView::extend_note_selection (uint8_t note)
1027 if (!_editor.internal_editing()) {
1031 uint16_t chn_mask = _channel_selector.get_selected_channels();
1033 if (_view->num_selected_regionviews() == 0) {
1034 _view->foreach_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::extend_note_selection_region_view), note, chn_mask));
1036 _view->foreach_selected_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::extend_note_selection_region_view), note, chn_mask));
1041 MidiTimeAxisView::toggle_note_selection (uint8_t note)
1043 if (!_editor.internal_editing()) {
1047 uint16_t chn_mask = _channel_selector.get_selected_channels();
1049 if (_view->num_selected_regionviews() == 0) {
1050 _view->foreach_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection_region_view), note, chn_mask));
1052 _view->foreach_selected_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection_region_view), note, chn_mask));
1057 MidiTimeAxisView::set_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1059 dynamic_cast<MidiRegionView*>(rv)->select_matching_notes (note, chn_mask, false, false);
1063 MidiTimeAxisView::add_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1065 dynamic_cast<MidiRegionView*>(rv)->select_matching_notes (note, chn_mask, true, false);
1069 MidiTimeAxisView::extend_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1071 dynamic_cast<MidiRegionView*>(rv)->select_matching_notes (note, chn_mask, true, true);
1075 MidiTimeAxisView::toggle_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1077 dynamic_cast<MidiRegionView*>(rv)->toggle_matching_notes (note, chn_mask);
1081 MidiTimeAxisView::set_channel_mode (ChannelMode, uint16_t)
1083 /* hide all automation tracks that use the wrong channel(s) and show all those that use
1087 uint16_t selected_channels = _channel_selector.get_selected_channels();
1088 bool changed = false;
1092 for (uint32_t ctl = 0; ctl < 127; ++ctl) {
1094 for (uint32_t chn = 0; chn < 16; ++chn) {
1095 Evoral::Parameter fully_qualified_param (MidiCCAutomation, chn, ctl);
1096 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
1102 if ((selected_channels & (0x0001 << chn)) == 0) {
1103 /* channel not in use. hiding it will trigger RouteTimeAxisView::automation_track_hidden()
1104 which will cause a redraw. We don't want one per channel, so block that with no_redraw.
1106 changed = track->set_marked_for_display (false) || changed;
1108 changed = track->set_marked_for_display (true) || changed;
1115 /* TODO: Bender, Pressure */
1117 /* invalidate the controller menu, so that we rebuild it next time */
1118 _controller_menu_map.clear ();
1119 delete controller_menu;
1120 controller_menu = 0;
1128 MidiTimeAxisView::automation_child_menu_item (Evoral::Parameter param)
1130 Gtk::CheckMenuItem* m = RouteTimeAxisView::automation_child_menu_item (param);
1135 ParameterMenuMap::iterator i = _controller_menu_map.find (param);
1136 if (i != _controller_menu_map.end()) {
1140 i = _channel_command_menu_map.find (param);
1141 if (i != _channel_command_menu_map.end()) {
1148 boost::shared_ptr<MidiRegion>
1149 MidiTimeAxisView::add_region (framepos_t pos, framecnt_t length, bool commit)
1151 Editor* real_editor = dynamic_cast<Editor*> (&_editor);
1153 real_editor->begin_reversible_command (Operations::create_region);
1154 playlist()->clear_changes ();
1156 real_editor->snap_to (pos, 0);
1158 boost::shared_ptr<Source> src = _session->create_midi_source_for_session (view()->trackview().track().get(),
1159 view()->trackview().track()->name());
1162 plist.add (ARDOUR::Properties::start, 0);
1163 plist.add (ARDOUR::Properties::length, length);
1164 plist.add (ARDOUR::Properties::name, PBD::basename_nosuffix(src->name()));
1166 boost::shared_ptr<Region> region = (RegionFactory::create (src, plist));
1168 playlist()->add_region (region, pos);
1169 _session->add_command (new StatefulDiffCommand (playlist()));
1172 real_editor->commit_reversible_command ();
1175 return boost::dynamic_pointer_cast<MidiRegion>(region);
1179 MidiTimeAxisView::ensure_step_editor ()
1181 if (!_step_editor) {
1182 _step_editor = new StepEditor (_editor, midi_track(), *this);
1187 MidiTimeAxisView::start_step_editing ()
1189 ensure_step_editor ();
1190 _step_editor->start_step_editing ();
1194 MidiTimeAxisView::stop_step_editing ()
1197 _step_editor->stop_step_editing ();
1202 /** @return channel (counted from 0) to add an event to, based on the current setting
1203 * of the channel selector.
1206 MidiTimeAxisView::get_channel_for_add () const
1208 uint16_t const chn_mask = _channel_selector.get_selected_channels ();
1210 uint8_t channel = 0;
1212 /* pick the highest selected channel, unless all channels are selected,
1213 which is interpreted to mean channel 1 (zero)
1216 for (uint16_t i = 0; i < 16; ++i) {
1217 if (chn_mask & (1<<i)) {
1223 if (chn_cnt == 16) {
1231 MidiTimeAxisView::note_range_changed ()
1233 set_gui_property ("note-range-min", (int) midi_view()->lowest_note ());
1234 set_gui_property ("note-range-max", (int) midi_view()->highest_note ());
1238 MidiTimeAxisView::contents_height_changed ()
1240 _range_scroomer->set_size_request (-1, _view->child_height ());