Fix corruption of follow playhead state on quit (#4048).
[ardour2.git] / gtk2_ardour / midi_time_axis.cc
blob2f06fdb127ff7a4753a18294d9b6cc0b367b5b93
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,
103 boost::shared_ptr<Route> rt, Canvas& canvas)
104 : AxisView(sess) // virtually inherited
105 , RouteTimeAxisView(ed, sess, rt, canvas)
106 , _ignore_signals(false)
107 , _range_scroomer(0)
108 , _piano_roll_header(0)
109 , _note_mode(Sustained)
110 , _note_mode_item(0)
111 , _percussion_mode_item(0)
112 , _color_mode(MeterColors)
113 , _meter_color_mode_item(0)
114 , _channel_color_mode_item(0)
115 , _track_color_mode_item(0)
116 , _step_edit_item (0)
117 , _midi_thru_item (0)
118 , default_channel_menu (0)
119 , controller_menu (0)
120 , _step_editor (0)
122 subplugin_menu.set_name ("ArdourContextMenu");
124 _view = new MidiStreamView (*this);
126 ignore_toggle = false;
128 mute_button->set_active (false);
129 solo_button->set_active (false);
131 if (is_midi_track()) {
132 controls_ebox.set_name ("MidiTimeAxisViewControlsBaseUnselected");
133 _note_mode = midi_track()->note_mode();
134 } else { // MIDI bus (which doesn't exist yet..)
135 controls_ebox.set_name ("MidiBusControlsBaseUnselected");
138 /* map current state of the route */
140 processors_changed (RouteProcessorChange ());
142 ensure_xml_node ();
144 set_state (*xml_node, Stateful::loading_state_version);
146 _route->processors_changed.connect (*this, invalidator (*this), ui_bind (&MidiTimeAxisView::processors_changed, this, _1), gui_context());
148 if (is_track()) {
149 _piano_roll_header = new PianoRollHeader(*midi_view());
151 _piano_roll_header->AddNoteSelection.connect (sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection));
152 _piano_roll_header->ExtendNoteSelection.connect (sigc::mem_fun (*this, &MidiTimeAxisView::extend_note_selection));
153 _piano_roll_header->ToggleNoteSelection.connect (sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection));
155 _range_scroomer = new MidiScroomer(midi_view()->note_range_adjustment);
157 /* Suspend updates of the StreamView during scroomer drags to speed things up */
158 _range_scroomer->DragStarting.connect (sigc::mem_fun (*midi_view(), &MidiStreamView::suspend_updates));
159 _range_scroomer->DragFinishing.connect (sigc::mem_fun (*midi_view(), &MidiStreamView::resume_updates));
161 controls_hbox.pack_start(*_range_scroomer);
162 controls_hbox.pack_start(*_piano_roll_header);
164 controls_ebox.set_name ("MidiTrackControlsBaseUnselected");
165 controls_base_selected_name = "MidiTrackControlsBaseSelected";
166 controls_base_unselected_name = "MidiTrackControlsBaseUnselected";
168 midi_view()->NoteRangeChanged.connect (sigc::mem_fun(*this, &MidiTimeAxisView::update_range));
170 /* ask for notifications of any new RegionViews */
171 _view->RegionViewAdded.connect (sigc::mem_fun(*this, &MidiTimeAxisView::region_view_added));
173 if (!_editor.have_idled()) {
174 /* first idle will do what we need */
175 } else {
176 first_idle ();
180 HBox* midi_controls_hbox = manage(new HBox());
182 MIDI::Name::MidiPatchManager& patch_manager = MIDI::Name::MidiPatchManager::instance();
184 MIDI::Name::MasterDeviceNames::Models::const_iterator m = patch_manager.all_models().begin();
185 for (; m != patch_manager.all_models().end(); ++m) {
186 _model_selector.append_text(m->c_str());
189 _model_selector.signal_changed().connect(sigc::mem_fun(*this, &MidiTimeAxisView::model_changed));
191 _custom_device_mode_selector.signal_changed().connect(
192 sigc::mem_fun(*this, &MidiTimeAxisView::custom_device_mode_changed));
194 // TODO: persist the choice
195 // this initializes the comboboxes and sends out the signal
196 _model_selector.set_active(0);
198 midi_controls_hbox->pack_start(_channel_selector, true, false);
199 if (!patch_manager.all_models().empty()) {
200 _midi_controls_box.pack_start(_model_selector, true, false);
201 _midi_controls_box.pack_start(_custom_device_mode_selector, true, false);
204 _midi_controls_box.pack_start(*midi_controls_hbox, true, true);
206 controls_vbox.pack_start(_midi_controls_box, false, false);
208 // restore channel selector settings
209 _channel_selector.set_channel_mode(midi_track()->get_channel_mode(), midi_track()->get_channel_mask());
210 _channel_selector.mode_changed.connect(
211 sigc::mem_fun(*midi_track(), &MidiTrack::set_channel_mode));
212 _channel_selector.mode_changed.connect(
213 sigc::mem_fun(*this, &MidiTimeAxisView::set_channel_mode));
215 XMLProperty *prop;
216 if ((prop = xml_node->property ("color-mode")) != 0) {
217 _color_mode = ColorMode (string_2_enum(prop->value(), _color_mode));
218 if (_color_mode == ChannelColors) {
219 _channel_selector.set_channel_colors(CanvasNoteEvent::midi_channel_colors);
223 if ((prop = xml_node->property ("note-mode")) != 0) {
224 _note_mode = NoteMode (string_2_enum(prop->value(), _note_mode));
225 if (_percussion_mode_item) {
226 _percussion_mode_item->set_active (_note_mode == Percussive);
230 set_color_mode (_color_mode, true);
233 void
234 MidiTimeAxisView::first_idle ()
236 if (is_track ()) {
237 _view->attach ();
241 MidiTimeAxisView::~MidiTimeAxisView ()
243 delete _piano_roll_header;
244 _piano_roll_header = 0;
246 delete _range_scroomer;
247 _range_scroomer = 0;
249 delete controller_menu;
250 delete _step_editor;
253 void
254 MidiTimeAxisView::enter_internal_edit_mode ()
256 if (midi_view()) {
257 midi_view()->enter_internal_edit_mode ();
261 void
262 MidiTimeAxisView::leave_internal_edit_mode ()
264 if (midi_view()) {
265 midi_view()->leave_internal_edit_mode ();
269 void
270 MidiTimeAxisView::check_step_edit ()
272 ensure_step_editor ();
273 _step_editor->check_step_edit ();
276 void
277 MidiTimeAxisView::model_changed()
279 std::list<std::string> device_modes = MIDI::Name::MidiPatchManager::instance()
280 .custom_device_mode_names_by_model(_model_selector.get_active_text());
282 _custom_device_mode_selector.clear_items();
284 for (std::list<std::string>::const_iterator i = device_modes.begin();
285 i != device_modes.end(); ++i) {
286 cerr << "found custom device mode " << *i << " thread_id: " << pthread_self() << endl;
287 _custom_device_mode_selector.append_text(*i);
290 _custom_device_mode_selector.set_active(0);
293 void MidiTimeAxisView::custom_device_mode_changed()
295 _midi_patch_settings_changed.emit(_model_selector.get_active_text(),
296 _custom_device_mode_selector.get_active_text());
299 MidiStreamView*
300 MidiTimeAxisView::midi_view()
302 return dynamic_cast<MidiStreamView*>(_view);
305 guint32
306 MidiTimeAxisView::show_at (double y, int& nth, Gtk::VBox *parent)
308 ensure_xml_node ();
309 xml_node->add_property ("shown-editor", "yes");
311 guint32 ret = TimeAxisView::show_at (y, nth, parent);
312 return ret;
315 void
316 MidiTimeAxisView::hide ()
318 ensure_xml_node ();
319 xml_node->add_property ("shown-editor", "no");
321 TimeAxisView::hide ();
324 void
325 MidiTimeAxisView::set_height (uint32_t h)
327 RouteTimeAxisView::set_height (h);
329 if (height >= MIDI_CONTROLS_BOX_MIN_HEIGHT) {
330 _midi_controls_box.show_all ();
331 } else {
332 _midi_controls_box.hide();
335 if (height >= KEYBOARD_MIN_HEIGHT) {
336 if (is_track() && _range_scroomer)
337 _range_scroomer->show();
338 if (is_track() && _piano_roll_header)
339 _piano_roll_header->show();
340 } else {
341 if (is_track() && _range_scroomer)
342 _range_scroomer->hide();
343 if (is_track() && _piano_roll_header)
344 _piano_roll_header->hide();
348 void
349 MidiTimeAxisView::append_extra_display_menu_items ()
351 using namespace Menu_Helpers;
353 MenuList& items = display_menu->items();
355 // Note range
356 Menu *range_menu = manage(new Menu);
357 MenuList& range_items = range_menu->items();
358 range_menu->set_name ("ArdourContextMenu");
360 range_items.push_back (MenuElem (_("Show Full Range"), sigc::bind (
361 sigc::mem_fun(*this, &MidiTimeAxisView::set_note_range),
362 MidiStreamView::FullRange)));
364 range_items.push_back (MenuElem (_("Fit Contents"), sigc::bind (
365 sigc::mem_fun(*this, &MidiTimeAxisView::set_note_range),
366 MidiStreamView::ContentsRange)));
368 items.push_back (MenuElem (_("Note Range"), *range_menu));
369 items.push_back (MenuElem (_("Note Mode"), *build_note_mode_menu()));
370 items.push_back (MenuElem (_("Default Channel"), *build_def_channel_menu()));
372 items.push_back (CheckMenuElem (_("MIDI Thru"), sigc::mem_fun(*this, &MidiTimeAxisView::toggle_midi_thru)));
373 _midi_thru_item = dynamic_cast<CheckMenuItem*>(&items.back());
375 items.push_back (SeparatorElem ());
378 Gtk::Menu*
379 MidiTimeAxisView::build_def_channel_menu ()
381 using namespace Menu_Helpers;
383 default_channel_menu = manage (new Menu ());
385 uint8_t defchn = midi_track()->default_channel();
386 MenuList& def_channel_items = default_channel_menu->items();
387 RadioMenuItem* item;
388 RadioMenuItem::Group dc_group;
390 for (int i = 0; i < 16; ++i) {
391 char buf[4];
392 snprintf (buf, sizeof (buf), "%d", i+1);
394 def_channel_items.push_back (RadioMenuElem (dc_group, buf,
395 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_default_channel), i)));
396 item = dynamic_cast<RadioMenuItem*>(&def_channel_items.back());
397 item->set_active ((i == defchn));
400 return default_channel_menu;
403 void
404 MidiTimeAxisView::set_default_channel (int chn)
406 midi_track()->set_default_channel (chn);
409 void
410 MidiTimeAxisView::toggle_midi_thru ()
412 if (!_midi_thru_item) {
413 return;
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);
422 void
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);
470 } else {
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);
477 void
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);
488 if (menu) {
489 menu->set_active (yn);
495 void
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();
504 int chn_cnt = 0;
506 for (uint8_t chn = 0; chn < 16; chn++) {
507 if (selected_channels & (0x0001 << chn)) {
508 if (++chn_cnt > 1) {
509 break;
514 if (chn_cnt > 1) {
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;
544 if (track) {
545 if (track->marked_for_display()) {
546 visible = true;
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));
560 } else {
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;
575 if (track) {
576 if (track->marked_for_display()) {
577 visible = true;
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 */
586 break;
592 void
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 */
599 return;
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.
614 int chn_cnt = 0;
616 for (uint8_t chn = 0; chn < 16; chn++) {
617 if (selected_channels & (0x0001 << chn)) {
618 if (++chn_cnt > 1) {
619 break;
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) {
638 continue;
641 if (chn_cnt > 1) {
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;
671 if (track) {
672 if (track->marked_for_display()) {
673 visible = true;
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);
687 } else {
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 (
696 CheckMenuElem (
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;
707 if (track) {
708 if (track->marked_for_display()) {
709 visible = true;
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 */
718 break;
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));
730 Gtk::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)));
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)));
747 _percussion_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
748 _percussion_mode_item->set_active(_note_mode == Percussive);
750 return mode_menu;
753 Gtk::Menu*
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), MeterColors, false)));
765 _meter_color_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
766 _meter_color_mode_item->set_active(_color_mode == MeterColors);
768 items.push_back (RadioMenuElem (mode_group, _("Channel Colors"),
769 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode), ChannelColors, false)));
770 _channel_color_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
771 _channel_color_mode_item->set_active(_color_mode == ChannelColors);
773 items.push_back (RadioMenuElem (mode_group, _("Track Color"),
774 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode), TrackColor, false)));
775 _channel_color_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
776 _channel_color_mode_item->set_active(_color_mode == TrackColor);
778 return mode_menu;
781 void
782 MidiTimeAxisView::set_note_mode(NoteMode mode)
784 if (_note_mode != mode || midi_track()->note_mode() != mode) {
785 _note_mode = mode;
786 midi_track()->set_note_mode(mode);
787 xml_node->add_property ("note-mode", enum_2_string(_note_mode));
788 _view->redisplay_track();
792 void
793 MidiTimeAxisView::set_color_mode (ColorMode mode, bool force)
795 if (_color_mode == mode && !force) {
796 return;
799 if (mode == ChannelColors) {
800 _channel_selector.set_channel_colors(CanvasNoteEvent::midi_channel_colors);
801 } else {
802 _channel_selector.set_default_channel_color();
805 _color_mode = mode;
806 xml_node->add_property ("color-mode", enum_2_string(_color_mode));
807 _view->redisplay_track();
810 void
811 MidiTimeAxisView::set_note_range(MidiStreamView::VisibleNoteRange range)
813 if (!_ignore_signals)
814 midi_view()->set_note_range(range);
818 void
819 MidiTimeAxisView::update_range()
821 MidiGhostRegion* mgr;
823 for(list<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
824 if ((mgr = dynamic_cast<MidiGhostRegion*>(*i)) != 0) {
825 mgr->update_range();
830 void
831 MidiTimeAxisView::show_all_automation (bool apply_to_selection)
833 if (apply_to_selection) {
834 _editor.get_selection().tracks.foreach_midi_time_axis (boost::bind (&MidiTimeAxisView::show_all_automation, _1, false));
835 } else {
836 if (midi_track()) {
837 const set<Evoral::Parameter> params = midi_track()->midi_playlist()->contained_automation();
839 for (set<Evoral::Parameter>::const_iterator i = params.begin(); i != params.end(); ++i) {
840 create_automation_child(*i, true);
844 RouteTimeAxisView::show_all_automation ();
848 void
849 MidiTimeAxisView::show_existing_automation (bool apply_to_selection)
851 if (apply_to_selection) {
852 _editor.get_selection().tracks.foreach_midi_time_axis (boost::bind (&MidiTimeAxisView::show_existing_automation, _1, false));
853 } else {
854 if (midi_track()) {
855 const set<Evoral::Parameter> params = midi_track()->midi_playlist()->contained_automation();
857 for (set<Evoral::Parameter>::const_iterator i = params.begin(); i != params.end(); ++i) {
858 create_automation_child(*i, true);
862 RouteTimeAxisView::show_existing_automation ();
866 /** Create an automation track for the given parameter (pitch bend, channel pressure).
868 void
869 MidiTimeAxisView::create_automation_child (const Evoral::Parameter& param, bool show)
871 if (param.type() == NullAutomation) {
872 cerr << "WARNING: Attempt to create NullAutomation child, ignoring" << endl;
873 return;
876 AutomationTracks::iterator existing = _automation_tracks.find (param);
877 if (existing != _automation_tracks.end()) {
878 return;
881 if (param.type() == GainAutomation) {
882 create_gain_automation_child (param, show);
883 } else {
885 /* These controllers are region "automation", so we do not create
886 * an AutomationList/Line for the track */
888 boost::shared_ptr<AutomationTimeAxisView> track (
889 new AutomationTimeAxisView (
890 _session,
891 _route,
892 boost::shared_ptr<Automatable> (),
893 boost::shared_ptr<AutomationControl> (),
894 param,
895 _editor,
896 *this,
897 true,
898 parent_canvas,
899 _route->describe_parameter(param)
903 if (_view) {
904 _view->foreach_regionview (sigc::mem_fun (*track.get(), &TimeAxisView::add_ghost));
907 add_automation_child (param, track, show);
912 void
913 MidiTimeAxisView::route_active_changed ()
915 RouteUI::route_active_changed ();
917 if (is_track()) {
918 if (_route->active()) {
919 controls_ebox.set_name ("MidiTrackControlsBaseUnselected");
920 controls_base_selected_name = "MidiTrackControlsBaseSelected";
921 controls_base_unselected_name = "MidiTrackControlsBaseUnselected";
922 } else {
923 controls_ebox.set_name ("MidiTrackControlsBaseInactiveUnselected");
924 controls_base_selected_name = "MidiTrackControlsBaseInactiveSelected";
925 controls_base_unselected_name = "MidiTrackControlsBaseInactiveUnselected";
927 } else {
929 throw; // wha?
931 if (_route->active()) {
932 controls_ebox.set_name ("BusControlsBaseUnselected");
933 controls_base_selected_name = "BusControlsBaseSelected";
934 controls_base_unselected_name = "BusControlsBaseUnselected";
935 } else {
936 controls_ebox.set_name ("BusControlsBaseInactiveUnselected");
937 controls_base_selected_name = "BusControlsBaseInactiveSelected";
938 controls_base_unselected_name = "BusControlsBaseInactiveUnselected";
945 void
946 MidiTimeAxisView::add_note_selection (uint8_t note)
948 if (!_editor.internal_editing()) {
949 return;
952 uint16_t chn_mask = _channel_selector.get_selected_channels();
954 if (_view->num_selected_regionviews() == 0) {
955 _view->foreach_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection_region_view), note, chn_mask));
956 } else {
957 _view->foreach_selected_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection_region_view), note, chn_mask));
961 void
962 MidiTimeAxisView::extend_note_selection (uint8_t note)
964 if (!_editor.internal_editing()) {
965 return;
968 uint16_t chn_mask = _channel_selector.get_selected_channels();
970 if (_view->num_selected_regionviews() == 0) {
971 _view->foreach_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::extend_note_selection_region_view), note, chn_mask));
972 } else {
973 _view->foreach_selected_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::extend_note_selection_region_view), note, chn_mask));
977 void
978 MidiTimeAxisView::toggle_note_selection (uint8_t note)
980 if (!_editor.internal_editing()) {
981 return;
984 uint16_t chn_mask = _channel_selector.get_selected_channels();
986 if (_view->num_selected_regionviews() == 0) {
987 _view->foreach_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection_region_view), note, chn_mask));
988 } else {
989 _view->foreach_selected_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection_region_view), note, chn_mask));
993 void
994 MidiTimeAxisView::add_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
996 dynamic_cast<MidiRegionView*>(rv)->select_matching_notes (note, chn_mask, false, false);
999 void
1000 MidiTimeAxisView::extend_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1002 dynamic_cast<MidiRegionView*>(rv)->select_matching_notes (note, chn_mask, true, true);
1005 void
1006 MidiTimeAxisView::toggle_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1008 dynamic_cast<MidiRegionView*>(rv)->toggle_matching_notes (note, chn_mask);
1011 void
1012 MidiTimeAxisView::set_channel_mode (ChannelMode, uint16_t)
1014 /* hide all automation tracks that use the wrong channel(s) and show all those that use
1015 the right ones.
1018 uint16_t selected_channels = _channel_selector.get_selected_channels();
1019 bool changed = false;
1021 no_redraw = true;
1023 for (uint32_t ctl = 0; ctl < 127; ++ctl) {
1025 for (uint32_t chn = 0; chn < 16; ++chn) {
1026 Evoral::Parameter fully_qualified_param (MidiCCAutomation, chn, ctl);
1027 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
1029 if (!track) {
1030 continue;
1033 if ((selected_channels & (0x0001 << chn)) == 0) {
1034 /* channel not in use. hiding it will trigger RouteTimeAxisView::automation_track_hidden()
1035 which will cause a redraw. We don't want one per channel, so block that with no_redraw.
1037 changed = track->set_visibility (false) || changed;
1038 } else {
1039 changed = track->set_visibility (true) || changed;
1044 no_redraw = false;
1046 /* TODO: Bender, Pressure */
1048 /* invalidate the controller menu, so that we rebuild it next time */
1049 _controller_menu_map.clear ();
1050 delete controller_menu;
1051 controller_menu = 0;
1053 if (changed) {
1054 _route->gui_changed ("track_height", this);
1058 Gtk::CheckMenuItem*
1059 MidiTimeAxisView::automation_child_menu_item (Evoral::Parameter param)
1061 Gtk::CheckMenuItem* m = RouteTimeAxisView::automation_child_menu_item (param);
1062 if (m) {
1063 return m;
1066 ParameterMenuMap::iterator i = _controller_menu_map.find (param);
1067 if (i != _controller_menu_map.end()) {
1068 return i->second;
1071 i = _channel_command_menu_map.find (param);
1072 if (i != _channel_command_menu_map.end()) {
1073 return i->second;
1076 return 0;
1079 boost::shared_ptr<MidiRegion>
1080 MidiTimeAxisView::add_region (framepos_t pos, framecnt_t length, bool commit)
1082 Editor* real_editor = dynamic_cast<Editor*> (&_editor);
1084 real_editor->begin_reversible_command (Operations::create_region);
1085 playlist()->clear_changes ();
1087 real_editor->snap_to (pos, 0);
1089 boost::shared_ptr<Source> src = _session->create_midi_source_for_session (view()->trackview().track().get(),
1090 view()->trackview().track()->name());
1091 PropertyList plist;
1093 plist.add (ARDOUR::Properties::start, 0);
1094 plist.add (ARDOUR::Properties::length, length);
1095 plist.add (ARDOUR::Properties::name, PBD::basename_nosuffix(src->name()));
1097 boost::shared_ptr<Region> region = (RegionFactory::create (src, plist));
1099 playlist()->add_region (region, pos);
1100 _session->add_command (new StatefulDiffCommand (playlist()));
1102 if (commit) {
1103 real_editor->commit_reversible_command ();
1106 return boost::dynamic_pointer_cast<MidiRegion>(region);
1109 void
1110 MidiTimeAxisView::ensure_step_editor ()
1112 if (!_step_editor) {
1113 _step_editor = new StepEditor (_editor, midi_track(), *this);
1117 void
1118 MidiTimeAxisView::start_step_editing ()
1120 ensure_step_editor ();
1121 _step_editor->start_step_editing ();
1124 void
1125 MidiTimeAxisView::stop_step_editing ()
1127 if (_step_editor) {
1128 _step_editor->stop_step_editing ();