Fix a couple of minor typos.
[ardour2.git] / gtk2_ardour / midi_time_axis.cc
blob0adb02725fd02c2a3c100ad1a0a8a4fbdcad9092
1 /*
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.
19 #include <cstdlib>
20 #include <cmath>
22 #include <algorithm>
23 #include <string>
24 #include <vector>
26 #include <sigc++/bind.h>
28 #include "pbd/error.h"
29 #include "pbd/stl_delete.h"
30 #include "pbd/whitespace.h"
31 #include "pbd/basename.h"
32 #include "pbd/enumwriter.h"
33 #include "pbd/memento_command.h"
34 #include "pbd/stateful_diff_command.h"
36 #include "gtkmm2ext/gtk_ui.h"
37 #include "gtkmm2ext/selector.h"
38 #include "gtkmm2ext/bindable_button.h"
39 #include "gtkmm2ext/utils.h"
41 #include "ardour/file_source.h"
42 #include "ardour/midi_playlist.h"
43 #include "ardour/midi_diskstream.h"
44 #include "ardour/midi_patch_manager.h"
45 #include "ardour/midi_source.h"
46 #include "ardour/processor.h"
47 #include "ardour/ladspa_plugin.h"
48 #include "ardour/location.h"
49 #include "ardour/playlist.h"
50 #include "ardour/region_factory.h"
51 #include "ardour/session.h"
52 #include "ardour/session_playlist.h"
53 #include "ardour/tempo.h"
54 #include "ardour/utils.h"
55 #include "ardour/operations.h"
57 #include "midi++/names.h"
59 #include "add_midi_cc_track_dialog.h"
60 #include "ardour_ui.h"
61 #include "automation_line.h"
62 #include "automation_time_axis.h"
63 #include "canvas-note-event.h"
64 #include "canvas_impl.h"
65 #include "crossfade_view.h"
66 #include "editor.h"
67 #include "enums.h"
68 #include "ghostregion.h"
69 #include "gui_thread.h"
70 #include "keyboard.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"
80 #include "prompter.h"
81 #include "region_view.h"
82 #include "rgb_macros.h"
83 #include "selection.h"
84 #include "step_editor.h"
85 #include "simplerect.h"
86 #include "utils.h"
88 #include "ardour/midi_track.h"
90 #include "i18n.h"
92 using namespace ARDOUR;
93 using namespace PBD;
94 using namespace Gtk;
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)
106 , _range_scroomer(0)
107 , _piano_roll_header(0)
108 , _note_mode(Sustained)
109 , _note_mode_item(0)
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)
118 , _step_editor (0)
122 void
123 MidiTimeAxisView::set_route (boost::shared_ptr<Route> rt)
125 RouteTimeAxisView::set_route (rt);
127 subplugin_menu.set_name ("ArdourContextMenu");
129 _view = new MidiStreamView (*this);
131 ignore_toggle = false;
133 mute_button->set_active (false);
134 solo_button->set_active (false);
136 if (is_midi_track()) {
137 controls_ebox.set_name ("MidiTimeAxisViewControlsBaseUnselected");
138 _note_mode = midi_track()->note_mode();
139 } else { // MIDI bus (which doesn't exist yet..)
140 controls_ebox.set_name ("MidiBusControlsBaseUnselected");
143 /* map current state of the route */
145 processors_changed (RouteProcessorChange ());
147 _route->processors_changed.connect (*this, invalidator (*this), ui_bind (&MidiTimeAxisView::processors_changed, this, _1), gui_context());
149 if (is_track()) {
150 _piano_roll_header = new PianoRollHeader(*midi_view());
152 _piano_roll_header->AddNoteSelection.connect (sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection));
153 _piano_roll_header->ExtendNoteSelection.connect (sigc::mem_fun (*this, &MidiTimeAxisView::extend_note_selection));
154 _piano_roll_header->ToggleNoteSelection.connect (sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection));
156 _range_scroomer = new MidiScroomer(midi_view()->note_range_adjustment);
158 /* Suspend updates of the StreamView during scroomer drags to speed things up */
159 _range_scroomer->DragStarting.connect (sigc::mem_fun (*midi_view(), &MidiStreamView::suspend_updates));
160 _range_scroomer->DragFinishing.connect (sigc::mem_fun (*midi_view(), &MidiStreamView::resume_updates));
162 controls_hbox.pack_start(*_range_scroomer);
163 controls_hbox.pack_start(*_piano_roll_header);
165 controls_ebox.set_name ("MidiTrackControlsBaseUnselected");
166 controls_base_selected_name = "MidiTrackControlsBaseSelected";
167 controls_base_unselected_name = "MidiTrackControlsBaseUnselected";
169 midi_view()->NoteRangeChanged.connect (sigc::mem_fun(*this, &MidiTimeAxisView::update_range));
171 /* ask for notifications of any new RegionViews */
172 _view->RegionViewAdded.connect (sigc::mem_fun(*this, &MidiTimeAxisView::region_view_added));
174 if (!_editor.have_idled()) {
175 /* first idle will do what we need */
176 } else {
177 first_idle ();
181 HBox* midi_controls_hbox = manage(new HBox());
183 MIDI::Name::MidiPatchManager& patch_manager = MIDI::Name::MidiPatchManager::instance();
185 MIDI::Name::MasterDeviceNames::Models::const_iterator m = patch_manager.all_models().begin();
186 for (; m != patch_manager.all_models().end(); ++m) {
187 _model_selector.append_text(m->c_str());
190 _model_selector.signal_changed().connect(sigc::mem_fun(*this, &MidiTimeAxisView::model_changed));
192 _custom_device_mode_selector.signal_changed().connect(
193 sigc::mem_fun(*this, &MidiTimeAxisView::custom_device_mode_changed));
195 // TODO: persist the choice
196 // this initializes the comboboxes and sends out the signal
197 _model_selector.set_active(0);
199 midi_controls_hbox->pack_start(_channel_selector, true, false);
200 if (!patch_manager.all_models().empty()) {
201 _midi_controls_box.pack_start(_model_selector, true, false);
202 _midi_controls_box.pack_start(_custom_device_mode_selector, true, false);
205 _midi_controls_box.pack_start(*midi_controls_hbox, true, true);
207 controls_vbox.pack_start(_midi_controls_box, false, false);
209 // restore channel selector settings
210 _channel_selector.set_channel_mode(midi_track()->get_channel_mode(), midi_track()->get_channel_mask());
211 _channel_selector.mode_changed.connect(
212 sigc::mem_fun(*midi_track(), &MidiTrack::set_channel_mode));
213 _channel_selector.mode_changed.connect(
214 sigc::mem_fun(*this, &MidiTimeAxisView::set_channel_mode));
216 string prop = gui_property ("color-mode");
217 if (!prop.empty()) {
218 _color_mode = ColorMode (string_2_enum(prop, _color_mode));
219 if (_color_mode == ChannelColors) {
220 _channel_selector.set_channel_colors(CanvasNoteEvent::midi_channel_colors);
224 set_color_mode (_color_mode, true, false);
226 prop = gui_property ("note-mode");
227 if (!prop.empty()) {
228 _note_mode = NoteMode (string_2_enum (prop, _note_mode));
229 if (_percussion_mode_item) {
230 _percussion_mode_item->set_active (_note_mode == Percussive);
234 /* Look for any GUI object state nodes that represent automation children that should exist, and create
235 * the children.
238 GUIObjectState& gui_state = gui_object_state ();
239 for (GUIObjectState::StringPropertyMap::const_iterator i = gui_state.begin(); i != gui_state.end(); ++i) {
240 PBD::ID route_id;
241 bool has_parameter;
242 Evoral::Parameter parameter (0, 0, 0);
244 bool const p = AutomationTimeAxisView::parse_state_id (i->first, route_id, has_parameter, parameter);
245 if (p && route_id == _route->id () && has_parameter) {
246 create_automation_child (parameter, string_is_affirmative (gui_object_state().get_string (i->first, X_("visible"))));
251 void
252 MidiTimeAxisView::first_idle ()
254 if (is_track ()) {
255 _view->attach ();
259 MidiTimeAxisView::~MidiTimeAxisView ()
261 delete _piano_roll_header;
262 _piano_roll_header = 0;
264 delete _range_scroomer;
265 _range_scroomer = 0;
267 delete controller_menu;
268 delete _step_editor;
271 void
272 MidiTimeAxisView::enter_internal_edit_mode ()
274 if (midi_view()) {
275 midi_view()->enter_internal_edit_mode ();
279 void
280 MidiTimeAxisView::leave_internal_edit_mode ()
282 if (midi_view()) {
283 midi_view()->leave_internal_edit_mode ();
287 void
288 MidiTimeAxisView::check_step_edit ()
290 ensure_step_editor ();
291 _step_editor->check_step_edit ();
294 void
295 MidiTimeAxisView::model_changed()
297 std::list<std::string> device_modes = MIDI::Name::MidiPatchManager::instance()
298 .custom_device_mode_names_by_model(_model_selector.get_active_text());
300 _custom_device_mode_selector.clear_items();
302 for (std::list<std::string>::const_iterator i = device_modes.begin();
303 i != device_modes.end(); ++i) {
304 cerr << "found custom device mode " << *i << " thread_id: " << pthread_self() << endl;
305 _custom_device_mode_selector.append_text(*i);
308 _custom_device_mode_selector.set_active(0);
311 void MidiTimeAxisView::custom_device_mode_changed()
313 _midi_patch_settings_changed.emit(_model_selector.get_active_text(),
314 _custom_device_mode_selector.get_active_text());
317 MidiStreamView*
318 MidiTimeAxisView::midi_view()
320 return dynamic_cast<MidiStreamView*>(_view);
323 void
324 MidiTimeAxisView::set_height (uint32_t h)
326 RouteTimeAxisView::set_height (h);
328 if (height >= MIDI_CONTROLS_BOX_MIN_HEIGHT) {
329 _midi_controls_box.show_all ();
330 } else {
331 _midi_controls_box.hide();
334 if (height >= KEYBOARD_MIN_HEIGHT) {
335 if (is_track() && _range_scroomer) {
336 _range_scroomer->show();
338 if (is_track() && _piano_roll_header) {
339 _piano_roll_header->show();
341 } else {
342 if (is_track() && _range_scroomer) {
343 _range_scroomer->hide();
345 if (is_track() && _piano_roll_header) {
346 _piano_roll_header->hide();
351 void
352 MidiTimeAxisView::append_extra_display_menu_items ()
354 using namespace Menu_Helpers;
356 MenuList& items = display_menu->items();
358 // Note range
359 Menu *range_menu = manage(new Menu);
360 MenuList& range_items = range_menu->items();
361 range_menu->set_name ("ArdourContextMenu");
363 range_items.push_back (MenuElem (_("Show Full Range"), sigc::bind (
364 sigc::mem_fun(*this, &MidiTimeAxisView::set_note_range),
365 MidiStreamView::FullRange)));
367 range_items.push_back (MenuElem (_("Fit Contents"), sigc::bind (
368 sigc::mem_fun(*this, &MidiTimeAxisView::set_note_range),
369 MidiStreamView::ContentsRange)));
371 items.push_back (MenuElem (_("Note Range"), *range_menu));
372 items.push_back (MenuElem (_("Note Mode"), *build_note_mode_menu()));
374 items.push_back (CheckMenuElem (_("MIDI Thru"), sigc::mem_fun(*this, &MidiTimeAxisView::toggle_midi_thru)));
375 _midi_thru_item = dynamic_cast<CheckMenuItem*>(&items.back());
377 items.push_back (SeparatorElem ());
380 void
381 MidiTimeAxisView::toggle_midi_thru ()
383 if (!_midi_thru_item) {
384 return;
387 bool view_yn = _midi_thru_item->get_active();
388 if (view_yn != midi_track()->midi_thru()) {
389 midi_track()->set_midi_thru (view_yn);
393 void
394 MidiTimeAxisView::build_automation_action_menu (bool for_selection)
396 using namespace Menu_Helpers;
398 /* If we have a controller menu, we need to detach it before
399 RouteTimeAxis::build_automation_action_menu destroys the
400 menu it is attached to. Otherwise GTK destroys
401 controller_menu's gobj, meaning that it can't be reattached
402 below. See bug #3134.
405 if (controller_menu) {
406 detach_menu (*controller_menu);
409 _channel_command_menu_map.clear ();
410 RouteTimeAxisView::build_automation_action_menu (for_selection);
412 MenuList& automation_items = automation_action_menu->items();
414 uint16_t selected_channels = _channel_selector.get_selected_channels();
416 if (selected_channels != 0) {
418 automation_items.push_back (SeparatorElem());
420 /* these 2 MIDI "command" types are semantically more like automation than note data,
421 but they are not MIDI controllers. We give them special status in this menu, since
422 they will not show up in the controller list and anyone who actually knows
423 something about MIDI (!) would not expect to find them there.
426 add_channel_command_menu_item (automation_items, _("Bender"), MidiPitchBenderAutomation, 0);
427 automation_items.back().set_sensitive (!for_selection || _editor.get_selection().tracks.size() == 1);
428 add_channel_command_menu_item (automation_items, _("Pressure"), MidiChannelPressureAutomation, 0);
429 automation_items.back().set_sensitive (!for_selection || _editor.get_selection().tracks.size() == 1);
431 /* now all MIDI controllers. Always offer the possibility that we will rebuild the controllers menu
432 since it might need to be updated after a channel mode change or other change. Also detach it
433 first in case it has been used anywhere else.
436 build_controller_menu ();
438 automation_items.push_back (SeparatorElem());
439 automation_items.push_back (MenuElem (_("Controllers"), *controller_menu));
440 automation_items.back().set_sensitive (!for_selection || _editor.get_selection().tracks.size() == 1);
441 } else {
442 automation_items.push_back (MenuElem (string_compose ("<i>%1</i>", _("No MIDI Channels selected"))));
443 dynamic_cast<Label*> (automation_items.back().get_child())->set_use_markup (true);
448 void
449 MidiTimeAxisView::change_all_channel_tracks_visibility (bool yn, Evoral::Parameter param)
451 uint16_t selected_channels = _channel_selector.get_selected_channels();
453 for (uint8_t chn = 0; chn < 16; chn++) {
454 if (selected_channels & (0x0001 << chn)) {
456 Evoral::Parameter fully_qualified_param (param.type(), chn, param.id());
457 Gtk::CheckMenuItem* menu = automation_child_menu_item (fully_qualified_param);
459 if (menu) {
460 menu->set_active (yn);
466 void
467 MidiTimeAxisView::add_channel_command_menu_item (Menu_Helpers::MenuList& items, const string& label, AutomationType auto_type, uint8_t cmd)
469 using namespace Menu_Helpers;
471 /* count the number of selected channels because we will build a different menu structure if there is more than 1 selected.
474 uint16_t selected_channels = _channel_selector.get_selected_channels();
475 int chn_cnt = 0;
477 for (uint8_t chn = 0; chn < 16; chn++) {
478 if (selected_channels & (0x0001 << chn)) {
479 if (++chn_cnt > 1) {
480 break;
485 if (chn_cnt > 1) {
487 /* multiple channels - create a submenu, with 1 item per channel */
489 Menu* chn_menu = manage (new Menu);
490 MenuList& chn_items (chn_menu->items());
491 Evoral::Parameter param_without_channel (auto_type, 0, cmd);
493 /* add a couple of items to hide/show all of them */
495 chn_items.push_back (MenuElem (_("Hide all channels"),
496 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
497 false, param_without_channel)));
498 chn_items.push_back (MenuElem (_("Show all channels"),
499 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
500 true, param_without_channel)));
502 for (uint8_t chn = 0; chn < 16; chn++) {
503 if (selected_channels & (0x0001 << chn)) {
505 /* for each selected channel, add a menu item for this controller */
507 Evoral::Parameter fully_qualified_param (auto_type, chn, cmd);
508 chn_items.push_back (CheckMenuElem (string_compose (_("Channel %1"), chn+1),
509 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
510 fully_qualified_param)));
512 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
513 bool visible = false;
515 if (track) {
516 if (track->marked_for_display()) {
517 visible = true;
521 CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&chn_items.back());
522 _channel_command_menu_map[fully_qualified_param] = cmi;
523 cmi->set_active (visible);
527 /* now create an item in the parent menu that has the per-channel list as a submenu */
529 items.push_back (MenuElem (label, *chn_menu));
531 } else {
533 /* just one channel - create a single menu item for this command+channel combination*/
535 for (uint8_t chn = 0; chn < 16; chn++) {
536 if (selected_channels & (0x0001 << chn)) {
538 Evoral::Parameter fully_qualified_param (auto_type, chn, cmd);
539 items.push_back (CheckMenuElem (label,
540 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
541 fully_qualified_param)));
543 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
544 bool visible = false;
546 if (track) {
547 if (track->marked_for_display()) {
548 visible = true;
552 CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&items.back());
553 _channel_command_menu_map[fully_qualified_param] = cmi;
554 cmi->set_active (visible);
556 /* one channel only */
557 break;
563 void
564 MidiTimeAxisView::build_controller_menu ()
566 using namespace Menu_Helpers;
568 if (controller_menu) {
569 /* it exists and has not been invalidated by a channel mode change, so just return it */
570 return;
573 controller_menu = new Menu; // explicitly managed by us
574 MenuList& items (controller_menu->items());
576 /* create several "top level" menu items for sets of controllers (16 at a time), and populate each one with a submenu
577 for each controller+channel combination covering the currently selected channels for this track
580 uint16_t selected_channels = _channel_selector.get_selected_channels();
582 /* count the number of selected channels because we will build a different menu structure if there is more than 1 selected.
585 int chn_cnt = 0;
587 for (uint8_t chn = 0; chn < 16; chn++) {
588 if (selected_channels & (0x0001 << chn)) {
589 if (++chn_cnt > 1) {
590 break;
595 /* loop over all 127 MIDI controllers, in groups of 16; except don't offer
596 bank select controllers, as they are handled by the `patch' code */
598 for (int i = 0; i < 127; i += 16) {
600 Menu* ctl_menu = manage (new Menu);
601 MenuList& ctl_items (ctl_menu->items());
604 /* for each controller, consider whether to create a submenu or a single item */
606 for (int ctl = i; ctl < i+16; ++ctl) {
608 if (ctl == MIDI_CTL_MSB_BANK || ctl == MIDI_CTL_LSB_BANK) {
609 continue;
612 if (chn_cnt > 1) {
614 /* multiple channels - create a submenu, with 1 item per channel */
616 Menu* chn_menu = manage (new Menu);
617 MenuList& chn_items (chn_menu->items());
619 /* add a couple of items to hide/show this controller on all channels */
621 Evoral::Parameter param_without_channel (MidiCCAutomation, 0, ctl);
622 chn_items.push_back (MenuElem (_("Hide all channels"),
623 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
624 false, param_without_channel)));
625 chn_items.push_back (MenuElem (_("Show all channels"),
626 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
627 true, param_without_channel)));
629 for (uint8_t chn = 0; chn < 16; chn++) {
630 if (selected_channels & (0x0001 << chn)) {
632 /* for each selected channel, add a menu item for this controller */
634 Evoral::Parameter fully_qualified_param (MidiCCAutomation, chn, ctl);
635 chn_items.push_back (CheckMenuElem (string_compose (_("Channel %1"), chn+1),
636 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
637 fully_qualified_param)));
639 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
640 bool visible = false;
642 if (track) {
643 if (track->marked_for_display()) {
644 visible = true;
648 CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&chn_items.back());
649 _controller_menu_map[fully_qualified_param] = cmi;
650 cmi->set_active (visible);
654 /* add the per-channel menu to the list of controllers, with the name of the controller */
655 ctl_items.push_back (MenuElem (string_compose ("<b>%1</b>: %2", ctl, midi_name (ctl)), *chn_menu));
656 dynamic_cast<Label*> (ctl_items.back().get_child())->set_use_markup (true);
658 } else {
660 /* just one channel - create a single menu item for this ctl+channel combination*/
662 for (uint8_t chn = 0; chn < 16; chn++) {
663 if (selected_channels & (0x0001 << chn)) {
665 Evoral::Parameter fully_qualified_param (MidiCCAutomation, chn, ctl);
666 ctl_items.push_back (
667 CheckMenuElem (
668 string_compose ("<b>%1</b>: %2 [%3]", ctl, midi_name (ctl), int (chn)),
669 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
670 fully_qualified_param)
673 dynamic_cast<Label*> (ctl_items.back().get_child())->set_use_markup (true);
675 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
676 bool visible = false;
678 if (track) {
679 if (track->marked_for_display()) {
680 visible = true;
684 CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&ctl_items.back());
685 _controller_menu_map[fully_qualified_param] = cmi;
686 cmi->set_active (visible);
688 /* one channel only */
689 break;
695 /* add the menu for this block of controllers to the overall controller menu */
697 items.push_back (MenuElem (string_compose (_("Controllers %1-%2"), i, i+15), *ctl_menu));
701 Gtk::Menu*
702 MidiTimeAxisView::build_note_mode_menu()
704 using namespace Menu_Helpers;
706 Menu* mode_menu = manage (new Menu);
707 MenuList& items = mode_menu->items();
708 mode_menu->set_name ("ArdourContextMenu");
710 RadioMenuItem::Group mode_group;
711 items.push_back (RadioMenuElem (mode_group, _("Sustained"),
712 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_mode), Sustained)));
713 _note_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
714 _note_mode_item->set_active(_note_mode == Sustained);
716 items.push_back (RadioMenuElem (mode_group, _("Percussive"),
717 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_mode), Percussive)));
718 _percussion_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
719 _percussion_mode_item->set_active(_note_mode == Percussive);
721 return mode_menu;
724 Gtk::Menu*
725 MidiTimeAxisView::build_color_mode_menu()
727 using namespace Menu_Helpers;
729 Menu* mode_menu = manage (new Menu);
730 MenuList& items = mode_menu->items();
731 mode_menu->set_name ("ArdourContextMenu");
733 RadioMenuItem::Group mode_group;
734 items.push_back (RadioMenuElem (mode_group, _("Meter Colors"),
735 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode),
736 MeterColors, false, true)));
737 _meter_color_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
738 _meter_color_mode_item->set_active(_color_mode == MeterColors);
740 items.push_back (RadioMenuElem (mode_group, _("Channel Colors"),
741 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode),
742 ChannelColors, false, true)));
743 _channel_color_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
744 _channel_color_mode_item->set_active(_color_mode == ChannelColors);
746 items.push_back (RadioMenuElem (mode_group, _("Track Color"),
747 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode),
748 TrackColor, false, true)));
749 _channel_color_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
750 _channel_color_mode_item->set_active(_color_mode == TrackColor);
752 return mode_menu;
755 void
756 MidiTimeAxisView::set_note_mode(NoteMode mode)
758 if (_note_mode != mode || midi_track()->note_mode() != mode) {
759 _note_mode = mode;
760 midi_track()->set_note_mode(mode);
761 set_gui_property ("note-mode", enum_2_string(_note_mode));
762 _view->redisplay_track();
766 void
767 MidiTimeAxisView::set_color_mode (ColorMode mode, bool force, bool redisplay)
769 if (_color_mode == mode && !force) {
770 return;
773 if (mode == ChannelColors) {
774 _channel_selector.set_channel_colors(CanvasNoteEvent::midi_channel_colors);
775 } else {
776 _channel_selector.set_default_channel_color();
779 _color_mode = mode;
780 set_gui_property ("color-mode", enum_2_string(_color_mode));
781 if (redisplay) {
782 _view->redisplay_track();
786 void
787 MidiTimeAxisView::set_note_range(MidiStreamView::VisibleNoteRange range)
789 if (!_ignore_signals)
790 midi_view()->set_note_range(range);
794 void
795 MidiTimeAxisView::update_range()
797 MidiGhostRegion* mgr;
799 for(list<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
800 if ((mgr = dynamic_cast<MidiGhostRegion*>(*i)) != 0) {
801 mgr->update_range();
806 void
807 MidiTimeAxisView::show_all_automation (bool apply_to_selection)
809 if (apply_to_selection) {
810 _editor.get_selection().tracks.foreach_midi_time_axis (boost::bind (&MidiTimeAxisView::show_all_automation, _1, false));
811 } else {
812 if (midi_track()) {
813 const set<Evoral::Parameter> params = midi_track()->midi_playlist()->contained_automation();
815 for (set<Evoral::Parameter>::const_iterator i = params.begin(); i != params.end(); ++i) {
816 create_automation_child(*i, true);
820 RouteTimeAxisView::show_all_automation ();
824 void
825 MidiTimeAxisView::show_existing_automation (bool apply_to_selection)
827 if (apply_to_selection) {
828 _editor.get_selection().tracks.foreach_midi_time_axis (boost::bind (&MidiTimeAxisView::show_existing_automation, _1, false));
829 } else {
830 if (midi_track()) {
831 const set<Evoral::Parameter> params = midi_track()->midi_playlist()->contained_automation();
833 for (set<Evoral::Parameter>::const_iterator i = params.begin(); i != params.end(); ++i) {
834 create_automation_child (*i, true);
838 RouteTimeAxisView::show_existing_automation ();
842 /** Create an automation track for the given parameter (pitch bend, channel pressure).
844 void
845 MidiTimeAxisView::create_automation_child (const Evoral::Parameter& param, bool show)
847 if (param.type() == NullAutomation) {
848 cerr << "WARNING: Attempt to create NullAutomation child, ignoring" << endl;
849 return;
852 AutomationTracks::iterator existing = _automation_tracks.find (param);
854 if (existing != _automation_tracks.end()) {
856 /* automation track created because we had existing data for
857 * the processor, but visibility may need to be controlled
858 * since it will have been set visible by default.
861 cerr << "show existing auto track: " << show << " noredraw " << no_redraw << endl;
863 if (existing->second->set_marked_for_display (show) && !no_redraw) {
864 request_redraw ();
867 return;
870 boost::shared_ptr<AutomationTimeAxisView> track;
872 switch (param.type()) {
874 case GainAutomation:
875 create_gain_automation_child (param, show);
876 break;
878 case PluginAutomation:
879 /* handled elsewhere */
880 break;
882 case MidiCCAutomation:
883 case MidiPgmChangeAutomation:
884 case MidiPitchBenderAutomation:
885 case MidiChannelPressureAutomation:
886 case MidiSystemExclusiveAutomation:
887 /* These controllers are region "automation" - they are owned
888 * by regions (and their MidiModels), not by the track. As a
889 * result we do not create an AutomationList/Line for the track
890 * ... except here we are doing something!! XXX
893 track.reset (new AutomationTimeAxisView (
894 _session,
895 _route,
896 boost::shared_ptr<Automatable> (),
897 boost::shared_ptr<AutomationControl> (),
898 param,
899 _editor,
900 *this,
901 true,
902 parent_canvas,
903 _route->describe_parameter(param)
906 if (_view) {
907 _view->foreach_regionview (sigc::mem_fun (*track.get(), &TimeAxisView::add_ghost));
910 add_automation_child (param, track, show);
911 break;
913 default:
914 error << "MidiTimeAxisView: unknown automation child " << EventTypeMap::instance().to_symbol(param) << endmsg;
918 void
919 MidiTimeAxisView::route_active_changed ()
921 RouteUI::route_active_changed ();
923 if (is_track()) {
924 if (_route->active()) {
925 controls_ebox.set_name ("MidiTrackControlsBaseUnselected");
926 controls_base_selected_name = "MidiTrackControlsBaseSelected";
927 controls_base_unselected_name = "MidiTrackControlsBaseUnselected";
928 } else {
929 controls_ebox.set_name ("MidiTrackControlsBaseInactiveUnselected");
930 controls_base_selected_name = "MidiTrackControlsBaseInactiveSelected";
931 controls_base_unselected_name = "MidiTrackControlsBaseInactiveUnselected";
933 } else {
935 throw; // wha?
937 if (_route->active()) {
938 controls_ebox.set_name ("BusControlsBaseUnselected");
939 controls_base_selected_name = "BusControlsBaseSelected";
940 controls_base_unselected_name = "BusControlsBaseUnselected";
941 } else {
942 controls_ebox.set_name ("BusControlsBaseInactiveUnselected");
943 controls_base_selected_name = "BusControlsBaseInactiveSelected";
944 controls_base_unselected_name = "BusControlsBaseInactiveUnselected";
951 void
952 MidiTimeAxisView::add_note_selection (uint8_t note)
954 if (!_editor.internal_editing()) {
955 return;
958 uint16_t chn_mask = _channel_selector.get_selected_channels();
960 if (_view->num_selected_regionviews() == 0) {
961 _view->foreach_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection_region_view), note, chn_mask));
962 } else {
963 _view->foreach_selected_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection_region_view), note, chn_mask));
967 void
968 MidiTimeAxisView::extend_note_selection (uint8_t note)
970 if (!_editor.internal_editing()) {
971 return;
974 uint16_t chn_mask = _channel_selector.get_selected_channels();
976 if (_view->num_selected_regionviews() == 0) {
977 _view->foreach_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::extend_note_selection_region_view), note, chn_mask));
978 } else {
979 _view->foreach_selected_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::extend_note_selection_region_view), note, chn_mask));
983 void
984 MidiTimeAxisView::toggle_note_selection (uint8_t note)
986 if (!_editor.internal_editing()) {
987 return;
990 uint16_t chn_mask = _channel_selector.get_selected_channels();
992 if (_view->num_selected_regionviews() == 0) {
993 _view->foreach_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection_region_view), note, chn_mask));
994 } else {
995 _view->foreach_selected_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection_region_view), note, chn_mask));
999 void
1000 MidiTimeAxisView::add_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1002 dynamic_cast<MidiRegionView*>(rv)->select_matching_notes (note, chn_mask, false, false);
1005 void
1006 MidiTimeAxisView::extend_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1008 dynamic_cast<MidiRegionView*>(rv)->select_matching_notes (note, chn_mask, true, true);
1011 void
1012 MidiTimeAxisView::toggle_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1014 dynamic_cast<MidiRegionView*>(rv)->toggle_matching_notes (note, chn_mask);
1017 void
1018 MidiTimeAxisView::set_channel_mode (ChannelMode, uint16_t)
1020 /* hide all automation tracks that use the wrong channel(s) and show all those that use
1021 the right ones.
1024 uint16_t selected_channels = _channel_selector.get_selected_channels();
1025 bool changed = false;
1027 no_redraw = true;
1029 for (uint32_t ctl = 0; ctl < 127; ++ctl) {
1031 for (uint32_t chn = 0; chn < 16; ++chn) {
1032 Evoral::Parameter fully_qualified_param (MidiCCAutomation, chn, ctl);
1033 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
1035 if (!track) {
1036 continue;
1039 if ((selected_channels & (0x0001 << chn)) == 0) {
1040 /* channel not in use. hiding it will trigger RouteTimeAxisView::automation_track_hidden()
1041 which will cause a redraw. We don't want one per channel, so block that with no_redraw.
1043 changed = track->set_marked_for_display (false) || changed;
1044 } else {
1045 changed = track->set_marked_for_display (true) || changed;
1050 no_redraw = false;
1052 /* TODO: Bender, Pressure */
1054 /* invalidate the controller menu, so that we rebuild it next time */
1055 _controller_menu_map.clear ();
1056 delete controller_menu;
1057 controller_menu = 0;
1059 if (changed) {
1060 request_redraw ();
1064 Gtk::CheckMenuItem*
1065 MidiTimeAxisView::automation_child_menu_item (Evoral::Parameter param)
1067 Gtk::CheckMenuItem* m = RouteTimeAxisView::automation_child_menu_item (param);
1068 if (m) {
1069 return m;
1072 ParameterMenuMap::iterator i = _controller_menu_map.find (param);
1073 if (i != _controller_menu_map.end()) {
1074 return i->second;
1077 i = _channel_command_menu_map.find (param);
1078 if (i != _channel_command_menu_map.end()) {
1079 return i->second;
1082 return 0;
1085 boost::shared_ptr<MidiRegion>
1086 MidiTimeAxisView::add_region (framepos_t pos, framecnt_t length, bool commit)
1088 Editor* real_editor = dynamic_cast<Editor*> (&_editor);
1090 real_editor->begin_reversible_command (Operations::create_region);
1091 playlist()->clear_changes ();
1093 real_editor->snap_to (pos, 0);
1095 boost::shared_ptr<Source> src = _session->create_midi_source_for_session (view()->trackview().track().get(),
1096 view()->trackview().track()->name());
1097 PropertyList plist;
1099 plist.add (ARDOUR::Properties::start, 0);
1100 plist.add (ARDOUR::Properties::length, length);
1101 plist.add (ARDOUR::Properties::name, PBD::basename_nosuffix(src->name()));
1103 boost::shared_ptr<Region> region = (RegionFactory::create (src, plist));
1105 playlist()->add_region (region, pos);
1106 _session->add_command (new StatefulDiffCommand (playlist()));
1108 if (commit) {
1109 real_editor->commit_reversible_command ();
1112 return boost::dynamic_pointer_cast<MidiRegion>(region);
1115 void
1116 MidiTimeAxisView::ensure_step_editor ()
1118 if (!_step_editor) {
1119 _step_editor = new StepEditor (_editor, midi_track(), *this);
1123 void
1124 MidiTimeAxisView::start_step_editing ()
1126 ensure_step_editor ();
1127 _step_editor->start_step_editing ();
1130 void
1131 MidiTimeAxisView::stop_step_editing ()
1133 if (_step_editor) {
1134 _step_editor->stop_step_editing ();
1139 /** @return channel (counted from 0) to add an event to, based on the current setting
1140 * of the channel selector.
1142 uint8_t
1143 MidiTimeAxisView::get_channel_for_add () const
1145 uint16_t const chn_mask = _channel_selector.get_selected_channels ();
1146 int chn_cnt = 0;
1147 uint8_t channel = 0;
1149 /* pick the highest selected channel, unless all channels are selected,
1150 which is interpreted to mean channel 1 (zero)
1153 for (uint16_t i = 0; i < 16; ++i) {
1154 if (chn_mask & (1<<i)) {
1155 channel = i;
1156 chn_cnt++;
1160 if (chn_cnt == 16) {
1161 channel = 0;
1164 return channel;