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 and the keyboard in a VBox with a padding
176 label so that they can be reduced in height for stacked-view
179 VBox* v = manage (new VBox);
180 HBox* h = manage (new HBox);
181 h->pack_start (*_range_scroomer);
182 h->pack_start (*_piano_roll_header);
183 v->pack_start (*h, false, false);
184 v->pack_start (*manage (new Label ("")), true, true);
185 controls_hbox.pack_start(*v);
187 controls_ebox.set_name ("MidiTrackControlsBaseUnselected");
188 controls_base_selected_name = "MidiTrackControlsBaseSelected";
189 controls_base_unselected_name = "MidiTrackControlsBaseUnselected";
191 midi_view()->NoteRangeChanged.connect (sigc::mem_fun(*this, &MidiTimeAxisView::update_range));
193 /* ask for notifications of any new RegionViews */
194 _view->RegionViewAdded.connect (sigc::mem_fun(*this, &MidiTimeAxisView::region_view_added));
196 if (!_editor.have_idled()) {
197 /* first idle will do what we need */
203 HBox* midi_controls_hbox = manage(new HBox());
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_hbox->pack_start(_channel_selector, true, false);
225 if (!patch_manager.all_models().empty()) {
226 _midi_controls_box.set_border_width (5);
227 _midi_controls_box.pack_start(_model_selector, true, false);
228 _midi_controls_box.pack_start(_custom_device_mode_selector, true, false);
231 _midi_controls_box.pack_start(*midi_controls_hbox, true, true);
233 controls_vbox.pack_start(_midi_controls_box, false, false);
235 // restore channel selector settings
236 _channel_selector.set_channel_mode(midi_track()->get_channel_mode(), midi_track()->get_channel_mask());
237 _channel_selector.mode_changed.connect(
238 sigc::mem_fun(*midi_track(), &MidiTrack::set_channel_mode));
239 _channel_selector.mode_changed.connect(
240 sigc::mem_fun(*this, &MidiTimeAxisView::set_channel_mode));
242 string prop = gui_property ("color-mode");
244 _color_mode = ColorMode (string_2_enum(prop, _color_mode));
245 if (_color_mode == ChannelColors) {
246 _channel_selector.set_channel_colors(CanvasNoteEvent::midi_channel_colors);
250 set_color_mode (_color_mode, true, false);
252 prop = gui_property ("note-mode");
254 _note_mode = NoteMode (string_2_enum (prop, _note_mode));
255 if (_percussion_mode_item) {
256 _percussion_mode_item->set_active (_note_mode == Percussive);
260 /* Look for any GUI object state nodes that represent automation children that should exist, and create
264 list<string> gui_ids = gui_object_state().all_ids ();
265 for (list<string>::const_iterator i = gui_ids.begin(); i != gui_ids.end(); ++i) {
268 Evoral::Parameter parameter (0, 0, 0);
270 bool const p = AutomationTimeAxisView::parse_state_id (*i, route_id, has_parameter, parameter);
271 if (p && route_id == _route->id () && has_parameter) {
272 create_automation_child (parameter, string_is_affirmative (gui_object_state().get_string (*i, X_("visible"))));
278 MidiTimeAxisView::first_idle ()
285 MidiTimeAxisView::~MidiTimeAxisView ()
287 delete _piano_roll_header;
288 _piano_roll_header = 0;
290 delete _range_scroomer;
293 delete controller_menu;
298 MidiTimeAxisView::enter_internal_edit_mode ()
301 midi_view()->enter_internal_edit_mode ();
306 MidiTimeAxisView::leave_internal_edit_mode ()
309 midi_view()->leave_internal_edit_mode ();
314 MidiTimeAxisView::check_step_edit ()
316 ensure_step_editor ();
317 _step_editor->check_step_edit ();
321 MidiTimeAxisView::model_changed()
323 std::list<std::string> device_modes = MIDI::Name::MidiPatchManager::instance()
324 .custom_device_mode_names_by_model(_model_selector.get_active_text());
326 _custom_device_mode_selector.clear_items();
328 for (std::list<std::string>::const_iterator i = device_modes.begin();
329 i != device_modes.end(); ++i) {
330 _custom_device_mode_selector.append_text(*i);
333 _custom_device_mode_selector.set_active(0);
335 _route->instrument_info().set_external_instrument (_model_selector.get_active_text(), _custom_device_mode_selector.get_active_text());
339 MidiTimeAxisView::custom_device_mode_changed()
341 _route->instrument_info().set_external_instrument (_model_selector.get_active_text(), _custom_device_mode_selector.get_active_text());
345 MidiTimeAxisView::midi_view()
347 return dynamic_cast<MidiStreamView*>(_view);
351 MidiTimeAxisView::set_height (uint32_t h)
353 if (h >= MIDI_CONTROLS_BOX_MIN_HEIGHT) {
354 _midi_controls_box.show_all ();
356 _midi_controls_box.hide();
359 if (h >= KEYBOARD_MIN_HEIGHT) {
360 if (is_track() && _range_scroomer) {
361 _range_scroomer->show();
363 if (is_track() && _piano_roll_header) {
364 _piano_roll_header->show();
367 if (is_track() && _range_scroomer) {
368 _range_scroomer->hide();
370 if (is_track() && _piano_roll_header) {
371 _piano_roll_header->hide();
375 /* We need to do this after changing visibility of our stuff, as it will
376 eventually trigger a call to Editor::reset_controls_layout_width(),
377 which needs to know if we have just shown or hidden a scroomer /
380 RouteTimeAxisView::set_height (h);
384 MidiTimeAxisView::append_extra_display_menu_items ()
386 using namespace Menu_Helpers;
388 MenuList& items = display_menu->items();
391 Menu *range_menu = manage(new Menu);
392 MenuList& range_items = range_menu->items();
393 range_menu->set_name ("ArdourContextMenu");
395 range_items.push_back (MenuElem (_("Show Full Range"), sigc::bind (
396 sigc::mem_fun(*this, &MidiTimeAxisView::set_note_range),
397 MidiStreamView::FullRange, true)));
399 range_items.push_back (MenuElem (_("Fit Contents"), sigc::bind (
400 sigc::mem_fun(*this, &MidiTimeAxisView::set_note_range),
401 MidiStreamView::ContentsRange, true)));
403 items.push_back (MenuElem (_("Note Range"), *range_menu));
404 items.push_back (MenuElem (_("Note Mode"), *build_note_mode_menu()));
406 items.push_back (CheckMenuElem (_("MIDI Thru"), sigc::mem_fun(*this, &MidiTimeAxisView::toggle_midi_thru)));
407 _midi_thru_item = dynamic_cast<CheckMenuItem*>(&items.back());
409 items.push_back (SeparatorElem ());
413 MidiTimeAxisView::toggle_midi_thru ()
415 if (!_midi_thru_item) {
419 bool view_yn = _midi_thru_item->get_active();
420 if (view_yn != midi_track()->midi_thru()) {
421 midi_track()->set_midi_thru (view_yn);
426 MidiTimeAxisView::build_automation_action_menu (bool for_selection)
428 using namespace Menu_Helpers;
430 /* If we have a controller menu, we need to detach it before
431 RouteTimeAxis::build_automation_action_menu destroys the
432 menu it is attached to. Otherwise GTK destroys
433 controller_menu's gobj, meaning that it can't be reattached
434 below. See bug #3134.
437 if (controller_menu) {
438 detach_menu (*controller_menu);
441 _channel_command_menu_map.clear ();
442 RouteTimeAxisView::build_automation_action_menu (for_selection);
444 MenuList& automation_items = automation_action_menu->items();
446 uint16_t selected_channels = _channel_selector.get_selected_channels();
448 if (selected_channels != 0) {
450 automation_items.push_back (SeparatorElem());
452 /* these 2 MIDI "command" types are semantically more like automation than note data,
453 but they are not MIDI controllers. We give them special status in this menu, since
454 they will not show up in the controller list and anyone who actually knows
455 something about MIDI (!) would not expect to find them there.
458 add_channel_command_menu_item (automation_items, _("Bender"), MidiPitchBenderAutomation, 0);
459 automation_items.back().set_sensitive (!for_selection || _editor.get_selection().tracks.size() == 1);
460 add_channel_command_menu_item (automation_items, _("Pressure"), MidiChannelPressureAutomation, 0);
461 automation_items.back().set_sensitive (!for_selection || _editor.get_selection().tracks.size() == 1);
463 /* now all MIDI controllers. Always offer the possibility that we will rebuild the controllers menu
464 since it might need to be updated after a channel mode change or other change. Also detach it
465 first in case it has been used anywhere else.
468 build_controller_menu ();
470 automation_items.push_back (SeparatorElem());
471 automation_items.push_back (MenuElem (_("Controllers"), *controller_menu));
472 automation_items.back().set_sensitive (!for_selection || _editor.get_selection().tracks.size() == 1);
474 automation_items.push_back (MenuElem (string_compose ("<i>%1</i>", _("No MIDI Channels selected"))));
475 dynamic_cast<Label*> (automation_items.back().get_child())->set_use_markup (true);
481 MidiTimeAxisView::change_all_channel_tracks_visibility (bool yn, Evoral::Parameter param)
483 uint16_t selected_channels = _channel_selector.get_selected_channels();
485 for (uint8_t chn = 0; chn < 16; chn++) {
486 if (selected_channels & (0x0001 << chn)) {
488 Evoral::Parameter fully_qualified_param (param.type(), chn, param.id());
489 Gtk::CheckMenuItem* menu = automation_child_menu_item (fully_qualified_param);
492 menu->set_active (yn);
499 MidiTimeAxisView::add_channel_command_menu_item (Menu_Helpers::MenuList& items, const string& label, AutomationType auto_type, uint8_t cmd)
501 using namespace Menu_Helpers;
503 /* count the number of selected channels because we will build a different menu structure if there is more than 1 selected.
506 uint16_t selected_channels = _channel_selector.get_selected_channels();
509 for (uint8_t chn = 0; chn < 16; chn++) {
510 if (selected_channels & (0x0001 << chn)) {
519 /* multiple channels - create a submenu, with 1 item per channel */
521 Menu* chn_menu = manage (new Menu);
522 MenuList& chn_items (chn_menu->items());
523 Evoral::Parameter param_without_channel (auto_type, 0, cmd);
525 /* add a couple of items to hide/show all of them */
527 chn_items.push_back (MenuElem (_("Hide all channels"),
528 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
529 false, param_without_channel)));
530 chn_items.push_back (MenuElem (_("Show all channels"),
531 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
532 true, param_without_channel)));
534 for (uint8_t chn = 0; chn < 16; chn++) {
535 if (selected_channels & (0x0001 << chn)) {
537 /* for each selected channel, add a menu item for this controller */
539 Evoral::Parameter fully_qualified_param (auto_type, chn, cmd);
540 chn_items.push_back (CheckMenuElem (string_compose (_("Channel %1"), chn+1),
541 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
542 fully_qualified_param)));
544 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
545 bool visible = false;
548 if (track->marked_for_display()) {
553 CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&chn_items.back());
554 _channel_command_menu_map[fully_qualified_param] = cmi;
555 cmi->set_active (visible);
559 /* now create an item in the parent menu that has the per-channel list as a submenu */
561 items.push_back (MenuElem (label, *chn_menu));
565 /* just one channel - create a single menu item for this command+channel combination*/
567 for (uint8_t chn = 0; chn < 16; chn++) {
568 if (selected_channels & (0x0001 << chn)) {
570 Evoral::Parameter fully_qualified_param (auto_type, chn, cmd);
571 items.push_back (CheckMenuElem (label,
572 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
573 fully_qualified_param)));
575 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
576 bool visible = false;
579 if (track->marked_for_display()) {
584 CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&items.back());
585 _channel_command_menu_map[fully_qualified_param] = cmi;
586 cmi->set_active (visible);
588 /* one channel only */
596 MidiTimeAxisView::build_controller_menu ()
598 using namespace Menu_Helpers;
600 if (controller_menu) {
601 /* it exists and has not been invalidated by a channel mode change, so just return it */
605 controller_menu = new Menu; // explicitly managed by us
606 MenuList& items (controller_menu->items());
608 /* create several "top level" menu items for sets of controllers (16 at a time), and populate each one with a submenu
609 for each controller+channel combination covering the currently selected channels for this track
612 uint16_t selected_channels = _channel_selector.get_selected_channels();
614 /* count the number of selected channels because we will build a different menu structure if there is more than 1 selected.
619 for (uint8_t chn = 0; chn < 16; chn++) {
620 if (selected_channels & (0x0001 << chn)) {
627 /* loop over all 127 MIDI controllers, in groups of 16; except don't offer
628 bank select controllers, as they are handled by the `patch' code */
630 for (int i = 0; i < 127; i += 16) {
632 Menu* ctl_menu = manage (new Menu);
633 MenuList& ctl_items (ctl_menu->items());
636 /* for each controller, consider whether to create a submenu or a single item */
638 for (int ctl = i; ctl < i+16; ++ctl) {
640 if (ctl == MIDI_CTL_MSB_BANK || ctl == MIDI_CTL_LSB_BANK) {
646 /* multiple channels - create a submenu, with 1 item per channel */
648 Menu* chn_menu = manage (new Menu);
649 MenuList& chn_items (chn_menu->items());
651 /* add a couple of items to hide/show this controller on all channels */
653 Evoral::Parameter param_without_channel (MidiCCAutomation, 0, ctl);
654 chn_items.push_back (MenuElem (_("Hide all channels"),
655 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
656 false, param_without_channel)));
657 chn_items.push_back (MenuElem (_("Show all channels"),
658 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
659 true, param_without_channel)));
661 for (uint8_t chn = 0; chn < 16; chn++) {
662 if (selected_channels & (0x0001 << chn)) {
664 /* for each selected channel, add a menu item for this controller */
666 Evoral::Parameter fully_qualified_param (MidiCCAutomation, chn, ctl);
667 chn_items.push_back (CheckMenuElem (string_compose (_("Channel %1"), chn+1),
668 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
669 fully_qualified_param)));
671 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
672 bool visible = false;
675 if (track->marked_for_display()) {
680 CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&chn_items.back());
681 _controller_menu_map[fully_qualified_param] = cmi;
682 cmi->set_active (visible);
686 /* add the per-channel menu to the list of controllers, with the name of the controller */
687 ctl_items.push_back (MenuElem (string_compose ("<b>%1</b>: %2", ctl, midi_name (ctl)), *chn_menu));
688 dynamic_cast<Label*> (ctl_items.back().get_child())->set_use_markup (true);
692 /* just one channel - create a single menu item for this ctl+channel combination*/
694 for (uint8_t chn = 0; chn < 16; chn++) {
695 if (selected_channels & (0x0001 << chn)) {
697 Evoral::Parameter fully_qualified_param (MidiCCAutomation, chn, ctl);
698 ctl_items.push_back (
700 string_compose ("<b>%1</b>: %2 [%3]", ctl, midi_name (ctl), int (chn)),
701 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
702 fully_qualified_param)
705 dynamic_cast<Label*> (ctl_items.back().get_child())->set_use_markup (true);
707 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
708 bool visible = false;
711 if (track->marked_for_display()) {
716 CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&ctl_items.back());
717 _controller_menu_map[fully_qualified_param] = cmi;
718 cmi->set_active (visible);
720 /* one channel only */
727 /* add the menu for this block of controllers to the overall controller menu */
729 items.push_back (MenuElem (string_compose (_("Controllers %1-%2"), i, i+15), *ctl_menu));
734 MidiTimeAxisView::build_note_mode_menu()
736 using namespace Menu_Helpers;
738 Menu* mode_menu = manage (new Menu);
739 MenuList& items = mode_menu->items();
740 mode_menu->set_name ("ArdourContextMenu");
742 RadioMenuItem::Group mode_group;
743 items.push_back (RadioMenuElem (mode_group,_("Sustained"),
744 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_mode), Sustained, true)));
745 _note_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
746 _note_mode_item->set_active(_note_mode == Sustained);
748 items.push_back (RadioMenuElem (mode_group, _("Percussive"),
749 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_mode), Percussive, true)));
750 _percussion_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
751 _percussion_mode_item->set_active(_note_mode == Percussive);
757 MidiTimeAxisView::build_color_mode_menu()
759 using namespace Menu_Helpers;
761 Menu* mode_menu = manage (new Menu);
762 MenuList& items = mode_menu->items();
763 mode_menu->set_name ("ArdourContextMenu");
765 RadioMenuItem::Group mode_group;
766 items.push_back (RadioMenuElem (mode_group, _("Meter Colors"),
767 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode),
768 MeterColors, false, true, true)));
769 _meter_color_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
770 _meter_color_mode_item->set_active(_color_mode == MeterColors);
772 items.push_back (RadioMenuElem (mode_group, _("Channel Colors"),
773 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode),
774 ChannelColors, false, true, true)));
775 _channel_color_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
776 _channel_color_mode_item->set_active(_color_mode == ChannelColors);
778 items.push_back (RadioMenuElem (mode_group, _("Track Color"),
779 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode),
780 TrackColor, false, true, true)));
781 _channel_color_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
782 _channel_color_mode_item->set_active(_color_mode == TrackColor);
788 MidiTimeAxisView::set_note_mode(NoteMode mode, bool apply_to_selection)
790 if (apply_to_selection) {
791 _editor.get_selection().tracks.foreach_midi_time_axis (boost::bind (&MidiTimeAxisView::set_note_mode, _1, mode, false));
793 if (_note_mode != mode || midi_track()->note_mode() != mode) {
795 midi_track()->set_note_mode(mode);
796 set_gui_property ("note-mode", enum_2_string(_note_mode));
797 _view->redisplay_track();
803 MidiTimeAxisView::set_color_mode (ColorMode mode, bool force, bool redisplay, bool apply_to_selection)
805 if (apply_to_selection) {
806 _editor.get_selection().tracks.foreach_midi_time_axis (
807 boost::bind (&MidiTimeAxisView::set_color_mode, _1, mode, force, redisplay, false)
811 if (_color_mode == mode && !force) {
815 if (mode == ChannelColors) {
816 _channel_selector.set_channel_colors(CanvasNoteEvent::midi_channel_colors);
818 _channel_selector.set_default_channel_color();
822 set_gui_property ("color-mode", enum_2_string(_color_mode));
824 _view->redisplay_track();
830 MidiTimeAxisView::set_note_range (MidiStreamView::VisibleNoteRange range, bool apply_to_selection)
832 if (apply_to_selection) {
833 _editor.get_selection().tracks.foreach_midi_time_axis (
834 boost::bind (&MidiTimeAxisView::set_note_range, _1, range, false)
837 if (!_ignore_signals) {
838 midi_view()->set_note_range(range);
844 MidiTimeAxisView::update_range()
846 MidiGhostRegion* mgr;
848 for(list<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
849 if ((mgr = dynamic_cast<MidiGhostRegion*>(*i)) != 0) {
856 MidiTimeAxisView::show_all_automation (bool apply_to_selection)
858 if (apply_to_selection) {
859 _editor.get_selection().tracks.foreach_midi_time_axis (boost::bind (&MidiTimeAxisView::show_all_automation, _1, false));
862 const set<Evoral::Parameter> params = midi_track()->midi_playlist()->contained_automation();
864 for (set<Evoral::Parameter>::const_iterator i = params.begin(); i != params.end(); ++i) {
865 create_automation_child(*i, true);
869 RouteTimeAxisView::show_all_automation ();
874 MidiTimeAxisView::show_existing_automation (bool apply_to_selection)
876 if (apply_to_selection) {
877 _editor.get_selection().tracks.foreach_midi_time_axis (boost::bind (&MidiTimeAxisView::show_existing_automation, _1, false));
880 const set<Evoral::Parameter> params = midi_track()->midi_playlist()->contained_automation();
882 for (set<Evoral::Parameter>::const_iterator i = params.begin(); i != params.end(); ++i) {
883 create_automation_child (*i, true);
887 RouteTimeAxisView::show_existing_automation ();
891 /** Create an automation track for the given parameter (pitch bend, channel pressure).
894 MidiTimeAxisView::create_automation_child (const Evoral::Parameter& param, bool show)
896 if (param.type() == NullAutomation) {
900 AutomationTracks::iterator existing = _automation_tracks.find (param);
902 if (existing != _automation_tracks.end()) {
904 /* automation track created because we had existing data for
905 * the processor, but visibility may need to be controlled
906 * since it will have been set visible by default.
909 if (existing->second->set_marked_for_display (show) && !no_redraw) {
916 boost::shared_ptr<AutomationTimeAxisView> track;
918 switch (param.type()) {
921 create_gain_automation_child (param, show);
924 case PluginAutomation:
925 /* handled elsewhere */
928 case MidiCCAutomation:
929 case MidiPgmChangeAutomation:
930 case MidiPitchBenderAutomation:
931 case MidiChannelPressureAutomation:
932 case MidiSystemExclusiveAutomation:
933 /* These controllers are region "automation" - they are owned
934 * by regions (and their MidiModels), not by the track. As a
935 * result we do not create an AutomationList/Line for the track
936 * ... except here we are doing something!! XXX
939 track.reset (new AutomationTimeAxisView (
942 boost::shared_ptr<Automatable> (),
943 boost::shared_ptr<AutomationControl> (),
949 _route->describe_parameter(param)
953 _view->foreach_regionview (sigc::mem_fun (*track.get(), &TimeAxisView::add_ghost));
956 add_automation_child (param, track, show);
960 error << "MidiTimeAxisView: unknown automation child " << EventTypeMap::instance().to_symbol(param) << endmsg;
965 MidiTimeAxisView::route_active_changed ()
967 RouteUI::route_active_changed ();
970 if (_route->active()) {
971 controls_ebox.set_name ("MidiTrackControlsBaseUnselected");
972 controls_base_selected_name = "MidiTrackControlsBaseSelected";
973 controls_base_unselected_name = "MidiTrackControlsBaseUnselected";
975 controls_ebox.set_name ("MidiTrackControlsBaseInactiveUnselected");
976 controls_base_selected_name = "MidiTrackControlsBaseInactiveSelected";
977 controls_base_unselected_name = "MidiTrackControlsBaseInactiveUnselected";
983 if (_route->active()) {
984 controls_ebox.set_name ("BusControlsBaseUnselected");
985 controls_base_selected_name = "BusControlsBaseSelected";
986 controls_base_unselected_name = "BusControlsBaseUnselected";
988 controls_ebox.set_name ("BusControlsBaseInactiveUnselected");
989 controls_base_selected_name = "BusControlsBaseInactiveSelected";
990 controls_base_unselected_name = "BusControlsBaseInactiveUnselected";
996 MidiTimeAxisView::set_note_selection (uint8_t note)
998 if (!_editor.internal_editing()) {
1002 uint16_t chn_mask = _channel_selector.get_selected_channels();
1004 if (_view->num_selected_regionviews() == 0) {
1005 _view->foreach_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_selection_region_view), note, chn_mask));
1007 _view->foreach_selected_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_selection_region_view), note, chn_mask));
1012 MidiTimeAxisView::add_note_selection (uint8_t note)
1014 if (!_editor.internal_editing()) {
1018 uint16_t chn_mask = _channel_selector.get_selected_channels();
1020 if (_view->num_selected_regionviews() == 0) {
1021 _view->foreach_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection_region_view), note, chn_mask));
1023 _view->foreach_selected_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection_region_view), note, chn_mask));
1028 MidiTimeAxisView::extend_note_selection (uint8_t note)
1030 if (!_editor.internal_editing()) {
1034 uint16_t chn_mask = _channel_selector.get_selected_channels();
1036 if (_view->num_selected_regionviews() == 0) {
1037 _view->foreach_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::extend_note_selection_region_view), note, chn_mask));
1039 _view->foreach_selected_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::extend_note_selection_region_view), note, chn_mask));
1044 MidiTimeAxisView::toggle_note_selection (uint8_t note)
1046 if (!_editor.internal_editing()) {
1050 uint16_t chn_mask = _channel_selector.get_selected_channels();
1052 if (_view->num_selected_regionviews() == 0) {
1053 _view->foreach_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection_region_view), note, chn_mask));
1055 _view->foreach_selected_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection_region_view), note, chn_mask));
1060 MidiTimeAxisView::set_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1062 dynamic_cast<MidiRegionView*>(rv)->select_matching_notes (note, chn_mask, false, false);
1066 MidiTimeAxisView::add_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1068 dynamic_cast<MidiRegionView*>(rv)->select_matching_notes (note, chn_mask, true, false);
1072 MidiTimeAxisView::extend_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1074 dynamic_cast<MidiRegionView*>(rv)->select_matching_notes (note, chn_mask, true, true);
1078 MidiTimeAxisView::toggle_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1080 dynamic_cast<MidiRegionView*>(rv)->toggle_matching_notes (note, chn_mask);
1084 MidiTimeAxisView::set_channel_mode (ChannelMode, uint16_t)
1086 /* hide all automation tracks that use the wrong channel(s) and show all those that use
1090 uint16_t selected_channels = _channel_selector.get_selected_channels();
1091 bool changed = false;
1095 for (uint32_t ctl = 0; ctl < 127; ++ctl) {
1097 for (uint32_t chn = 0; chn < 16; ++chn) {
1098 Evoral::Parameter fully_qualified_param (MidiCCAutomation, chn, ctl);
1099 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
1105 if ((selected_channels & (0x0001 << chn)) == 0) {
1106 /* channel not in use. hiding it will trigger RouteTimeAxisView::automation_track_hidden()
1107 which will cause a redraw. We don't want one per channel, so block that with no_redraw.
1109 changed = track->set_marked_for_display (false) || changed;
1111 changed = track->set_marked_for_display (true) || changed;
1118 /* TODO: Bender, Pressure */
1120 /* invalidate the controller menu, so that we rebuild it next time */
1121 _controller_menu_map.clear ();
1122 delete controller_menu;
1123 controller_menu = 0;
1131 MidiTimeAxisView::automation_child_menu_item (Evoral::Parameter param)
1133 Gtk::CheckMenuItem* m = RouteTimeAxisView::automation_child_menu_item (param);
1138 ParameterMenuMap::iterator i = _controller_menu_map.find (param);
1139 if (i != _controller_menu_map.end()) {
1143 i = _channel_command_menu_map.find (param);
1144 if (i != _channel_command_menu_map.end()) {
1151 boost::shared_ptr<MidiRegion>
1152 MidiTimeAxisView::add_region (framepos_t pos, framecnt_t length, bool commit)
1154 Editor* real_editor = dynamic_cast<Editor*> (&_editor);
1156 real_editor->begin_reversible_command (Operations::create_region);
1157 playlist()->clear_changes ();
1159 real_editor->snap_to (pos, 0);
1161 boost::shared_ptr<Source> src = _session->create_midi_source_for_session (view()->trackview().track().get(),
1162 view()->trackview().track()->name());
1165 plist.add (ARDOUR::Properties::start, 0);
1166 plist.add (ARDOUR::Properties::length, length);
1167 plist.add (ARDOUR::Properties::name, PBD::basename_nosuffix(src->name()));
1169 boost::shared_ptr<Region> region = (RegionFactory::create (src, plist));
1171 playlist()->add_region (region, pos);
1172 _session->add_command (new StatefulDiffCommand (playlist()));
1175 real_editor->commit_reversible_command ();
1178 return boost::dynamic_pointer_cast<MidiRegion>(region);
1182 MidiTimeAxisView::ensure_step_editor ()
1184 if (!_step_editor) {
1185 _step_editor = new StepEditor (_editor, midi_track(), *this);
1190 MidiTimeAxisView::start_step_editing ()
1192 ensure_step_editor ();
1193 _step_editor->start_step_editing ();
1197 MidiTimeAxisView::stop_step_editing ()
1200 _step_editor->stop_step_editing ();
1205 /** @return channel (counted from 0) to add an event to, based on the current setting
1206 * of the channel selector.
1209 MidiTimeAxisView::get_channel_for_add () const
1211 uint16_t const chn_mask = _channel_selector.get_selected_channels ();
1213 uint8_t channel = 0;
1215 /* pick the highest selected channel, unless all channels are selected,
1216 which is interpreted to mean channel 1 (zero)
1219 for (uint16_t i = 0; i < 16; ++i) {
1220 if (chn_mask & (1<<i)) {
1226 if (chn_cnt == 16) {
1234 MidiTimeAxisView::note_range_changed ()
1236 set_gui_property ("note-range-min", (int) midi_view()->lowest_note ());
1237 set_gui_property ("note-range-max", (int) midi_view()->highest_note ());
1241 MidiTimeAxisView::contents_height_changed ()
1243 _range_scroomer->set_size_request (-1, _view->child_height ());