Add a couple of missing attach_buffers() calls after _ports has been changed. I...
[ardour2.git] / gtk2_ardour / midi_time_axis.cc
blob8ebe40c864ab0a7e54cd29cf66fb7e3a8a60cca6
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);
235 void
236 MidiTimeAxisView::first_idle ()
238 if (is_track ()) {
239 _view->attach ();
243 MidiTimeAxisView::~MidiTimeAxisView ()
245 delete _piano_roll_header;
246 _piano_roll_header = 0;
248 delete _range_scroomer;
249 _range_scroomer = 0;
251 delete controller_menu;
252 delete _step_editor;
255 void
256 MidiTimeAxisView::enter_internal_edit_mode ()
258 if (midi_view()) {
259 midi_view()->enter_internal_edit_mode ();
263 void
264 MidiTimeAxisView::leave_internal_edit_mode ()
266 if (midi_view()) {
267 midi_view()->leave_internal_edit_mode ();
271 void
272 MidiTimeAxisView::check_step_edit ()
274 ensure_step_editor ();
275 _step_editor->check_step_edit ();
278 void
279 MidiTimeAxisView::model_changed()
281 std::list<std::string> device_modes = MIDI::Name::MidiPatchManager::instance()
282 .custom_device_mode_names_by_model(_model_selector.get_active_text());
284 _custom_device_mode_selector.clear_items();
286 for (std::list<std::string>::const_iterator i = device_modes.begin();
287 i != device_modes.end(); ++i) {
288 cerr << "found custom device mode " << *i << " thread_id: " << pthread_self() << endl;
289 _custom_device_mode_selector.append_text(*i);
292 _custom_device_mode_selector.set_active(0);
295 void MidiTimeAxisView::custom_device_mode_changed()
297 _midi_patch_settings_changed.emit(_model_selector.get_active_text(),
298 _custom_device_mode_selector.get_active_text());
301 MidiStreamView*
302 MidiTimeAxisView::midi_view()
304 return dynamic_cast<MidiStreamView*>(_view);
307 void
308 MidiTimeAxisView::set_height (uint32_t h)
310 RouteTimeAxisView::set_height (h);
312 if (height >= MIDI_CONTROLS_BOX_MIN_HEIGHT) {
313 _midi_controls_box.show_all ();
314 } else {
315 _midi_controls_box.hide();
318 if (height >= KEYBOARD_MIN_HEIGHT) {
319 if (is_track() && _range_scroomer) {
320 _range_scroomer->show();
322 if (is_track() && _piano_roll_header) {
323 _piano_roll_header->show();
325 } else {
326 if (is_track() && _range_scroomer) {
327 _range_scroomer->hide();
329 if (is_track() && _piano_roll_header) {
330 _piano_roll_header->hide();
335 void
336 MidiTimeAxisView::append_extra_display_menu_items ()
338 using namespace Menu_Helpers;
340 MenuList& items = display_menu->items();
342 // Note range
343 Menu *range_menu = manage(new Menu);
344 MenuList& range_items = range_menu->items();
345 range_menu->set_name ("ArdourContextMenu");
347 range_items.push_back (MenuElem (_("Show Full Range"), sigc::bind (
348 sigc::mem_fun(*this, &MidiTimeAxisView::set_note_range),
349 MidiStreamView::FullRange)));
351 range_items.push_back (MenuElem (_("Fit Contents"), sigc::bind (
352 sigc::mem_fun(*this, &MidiTimeAxisView::set_note_range),
353 MidiStreamView::ContentsRange)));
355 items.push_back (MenuElem (_("Note Range"), *range_menu));
356 items.push_back (MenuElem (_("Note Mode"), *build_note_mode_menu()));
358 items.push_back (CheckMenuElem (_("MIDI Thru"), sigc::mem_fun(*this, &MidiTimeAxisView::toggle_midi_thru)));
359 _midi_thru_item = dynamic_cast<CheckMenuItem*>(&items.back());
361 items.push_back (SeparatorElem ());
364 void
365 MidiTimeAxisView::toggle_midi_thru ()
367 if (!_midi_thru_item) {
368 return;
371 bool view_yn = _midi_thru_item->get_active();
372 if (view_yn != midi_track()->midi_thru()) {
373 midi_track()->set_midi_thru (view_yn);
377 void
378 MidiTimeAxisView::build_automation_action_menu (bool for_selection)
380 using namespace Menu_Helpers;
382 /* If we have a controller menu, we need to detach it before
383 RouteTimeAxis::build_automation_action_menu destroys the
384 menu it is attached to. Otherwise GTK destroys
385 controller_menu's gobj, meaning that it can't be reattached
386 below. See bug #3134.
389 if (controller_menu) {
390 detach_menu (*controller_menu);
393 _channel_command_menu_map.clear ();
394 RouteTimeAxisView::build_automation_action_menu (for_selection);
396 MenuList& automation_items = automation_action_menu->items();
398 uint16_t selected_channels = _channel_selector.get_selected_channels();
400 if (selected_channels != 0) {
402 automation_items.push_back (SeparatorElem());
404 /* these 2 MIDI "command" types are semantically more like automation than note data,
405 but they are not MIDI controllers. We give them special status in this menu, since
406 they will not show up in the controller list and anyone who actually knows
407 something about MIDI (!) would not expect to find them there.
410 add_channel_command_menu_item (automation_items, _("Bender"), MidiPitchBenderAutomation, 0);
411 automation_items.back().set_sensitive (!for_selection || _editor.get_selection().tracks.size() == 1);
412 add_channel_command_menu_item (automation_items, _("Pressure"), MidiChannelPressureAutomation, 0);
413 automation_items.back().set_sensitive (!for_selection || _editor.get_selection().tracks.size() == 1);
415 /* now all MIDI controllers. Always offer the possibility that we will rebuild the controllers menu
416 since it might need to be updated after a channel mode change or other change. Also detach it
417 first in case it has been used anywhere else.
420 build_controller_menu ();
422 automation_items.push_back (SeparatorElem());
423 automation_items.push_back (MenuElem (_("Controllers"), *controller_menu));
424 automation_items.back().set_sensitive (!for_selection || _editor.get_selection().tracks.size() == 1);
425 } else {
426 automation_items.push_back (MenuElem (string_compose ("<i>%1</i>", _("No MIDI Channels selected"))));
427 dynamic_cast<Label*> (automation_items.back().get_child())->set_use_markup (true);
432 void
433 MidiTimeAxisView::change_all_channel_tracks_visibility (bool yn, Evoral::Parameter param)
435 uint16_t selected_channels = _channel_selector.get_selected_channels();
437 for (uint8_t chn = 0; chn < 16; chn++) {
438 if (selected_channels & (0x0001 << chn)) {
440 Evoral::Parameter fully_qualified_param (param.type(), chn, param.id());
441 Gtk::CheckMenuItem* menu = automation_child_menu_item (fully_qualified_param);
443 if (menu) {
444 menu->set_active (yn);
450 void
451 MidiTimeAxisView::add_channel_command_menu_item (Menu_Helpers::MenuList& items, const string& label, AutomationType auto_type, uint8_t cmd)
453 using namespace Menu_Helpers;
455 /* count the number of selected channels because we will build a different menu structure if there is more than 1 selected.
458 uint16_t selected_channels = _channel_selector.get_selected_channels();
459 int chn_cnt = 0;
461 for (uint8_t chn = 0; chn < 16; chn++) {
462 if (selected_channels & (0x0001 << chn)) {
463 if (++chn_cnt > 1) {
464 break;
469 if (chn_cnt > 1) {
471 /* multiple channels - create a submenu, with 1 item per channel */
473 Menu* chn_menu = manage (new Menu);
474 MenuList& chn_items (chn_menu->items());
475 Evoral::Parameter param_without_channel (auto_type, 0, cmd);
477 /* add a couple of items to hide/show all of them */
479 chn_items.push_back (MenuElem (_("Hide all channels"),
480 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
481 false, param_without_channel)));
482 chn_items.push_back (MenuElem (_("Show all channels"),
483 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
484 true, param_without_channel)));
486 for (uint8_t chn = 0; chn < 16; chn++) {
487 if (selected_channels & (0x0001 << chn)) {
489 /* for each selected channel, add a menu item for this controller */
491 Evoral::Parameter fully_qualified_param (auto_type, chn, cmd);
492 chn_items.push_back (CheckMenuElem (string_compose (_("Channel %1"), chn+1),
493 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
494 fully_qualified_param)));
496 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
497 bool visible = false;
499 if (track) {
500 if (track->marked_for_display()) {
501 visible = true;
505 CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&chn_items.back());
506 _channel_command_menu_map[fully_qualified_param] = cmi;
507 cmi->set_active (visible);
511 /* now create an item in the parent menu that has the per-channel list as a submenu */
513 items.push_back (MenuElem (label, *chn_menu));
515 } else {
517 /* just one channel - create a single menu item for this command+channel combination*/
519 for (uint8_t chn = 0; chn < 16; chn++) {
520 if (selected_channels & (0x0001 << chn)) {
522 Evoral::Parameter fully_qualified_param (auto_type, chn, cmd);
523 items.push_back (CheckMenuElem (label,
524 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
525 fully_qualified_param)));
527 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
528 bool visible = false;
530 if (track) {
531 if (track->marked_for_display()) {
532 visible = true;
536 CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&items.back());
537 _channel_command_menu_map[fully_qualified_param] = cmi;
538 cmi->set_active (visible);
540 /* one channel only */
541 break;
547 void
548 MidiTimeAxisView::build_controller_menu ()
550 using namespace Menu_Helpers;
552 if (controller_menu) {
553 /* it exists and has not been invalidated by a channel mode change, so just return it */
554 return;
557 controller_menu = new Menu; // explicitly managed by us
558 MenuList& items (controller_menu->items());
560 /* create several "top level" menu items for sets of controllers (16 at a time), and populate each one with a submenu
561 for each controller+channel combination covering the currently selected channels for this track
564 uint16_t selected_channels = _channel_selector.get_selected_channels();
566 /* count the number of selected channels because we will build a different menu structure if there is more than 1 selected.
569 int chn_cnt = 0;
571 for (uint8_t chn = 0; chn < 16; chn++) {
572 if (selected_channels & (0x0001 << chn)) {
573 if (++chn_cnt > 1) {
574 break;
579 /* loop over all 127 MIDI controllers, in groups of 16; except don't offer
580 bank select controllers, as they are handled by the `patch' code */
582 for (int i = 0; i < 127; i += 16) {
584 Menu* ctl_menu = manage (new Menu);
585 MenuList& ctl_items (ctl_menu->items());
588 /* for each controller, consider whether to create a submenu or a single item */
590 for (int ctl = i; ctl < i+16; ++ctl) {
592 if (ctl == MIDI_CTL_MSB_BANK || ctl == MIDI_CTL_LSB_BANK) {
593 continue;
596 if (chn_cnt > 1) {
598 /* multiple channels - create a submenu, with 1 item per channel */
600 Menu* chn_menu = manage (new Menu);
601 MenuList& chn_items (chn_menu->items());
603 /* add a couple of items to hide/show this controller on all channels */
605 Evoral::Parameter param_without_channel (MidiCCAutomation, 0, ctl);
606 chn_items.push_back (MenuElem (_("Hide all channels"),
607 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
608 false, param_without_channel)));
609 chn_items.push_back (MenuElem (_("Show all channels"),
610 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
611 true, param_without_channel)));
613 for (uint8_t chn = 0; chn < 16; chn++) {
614 if (selected_channels & (0x0001 << chn)) {
616 /* for each selected channel, add a menu item for this controller */
618 Evoral::Parameter fully_qualified_param (MidiCCAutomation, chn, ctl);
619 chn_items.push_back (CheckMenuElem (string_compose (_("Channel %1"), chn+1),
620 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
621 fully_qualified_param)));
623 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
624 bool visible = false;
626 if (track) {
627 if (track->marked_for_display()) {
628 visible = true;
632 CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&chn_items.back());
633 _controller_menu_map[fully_qualified_param] = cmi;
634 cmi->set_active (visible);
638 /* add the per-channel menu to the list of controllers, with the name of the controller */
639 ctl_items.push_back (MenuElem (string_compose ("<b>%1</b>: %2", ctl, midi_name (ctl)), *chn_menu));
640 dynamic_cast<Label*> (ctl_items.back().get_child())->set_use_markup (true);
642 } else {
644 /* just one channel - create a single menu item for this ctl+channel combination*/
646 for (uint8_t chn = 0; chn < 16; chn++) {
647 if (selected_channels & (0x0001 << chn)) {
649 Evoral::Parameter fully_qualified_param (MidiCCAutomation, chn, ctl);
650 ctl_items.push_back (
651 CheckMenuElem (
652 string_compose ("<b>%1</b>: %2 [%3]", ctl, midi_name (ctl), int (chn)),
653 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
654 fully_qualified_param)
657 dynamic_cast<Label*> (ctl_items.back().get_child())->set_use_markup (true);
659 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
660 bool visible = false;
662 if (track) {
663 if (track->marked_for_display()) {
664 visible = true;
668 CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&ctl_items.back());
669 _controller_menu_map[fully_qualified_param] = cmi;
670 cmi->set_active (visible);
672 /* one channel only */
673 break;
679 /* add the menu for this block of controllers to the overall controller menu */
681 items.push_back (MenuElem (string_compose (_("Controllers %1-%2"), i, i+15), *ctl_menu));
685 Gtk::Menu*
686 MidiTimeAxisView::build_note_mode_menu()
688 using namespace Menu_Helpers;
690 Menu* mode_menu = manage (new Menu);
691 MenuList& items = mode_menu->items();
692 mode_menu->set_name ("ArdourContextMenu");
694 RadioMenuItem::Group mode_group;
695 items.push_back (RadioMenuElem (mode_group, _("Sustained"),
696 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_mode), Sustained)));
697 _note_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
698 _note_mode_item->set_active(_note_mode == Sustained);
700 items.push_back (RadioMenuElem (mode_group, _("Percussive"),
701 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_mode), Percussive)));
702 _percussion_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
703 _percussion_mode_item->set_active(_note_mode == Percussive);
705 return mode_menu;
708 Gtk::Menu*
709 MidiTimeAxisView::build_color_mode_menu()
711 using namespace Menu_Helpers;
713 Menu* mode_menu = manage (new Menu);
714 MenuList& items = mode_menu->items();
715 mode_menu->set_name ("ArdourContextMenu");
717 RadioMenuItem::Group mode_group;
718 items.push_back (RadioMenuElem (mode_group, _("Meter Colors"),
719 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode),
720 MeterColors, false, true)));
721 _meter_color_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
722 _meter_color_mode_item->set_active(_color_mode == MeterColors);
724 items.push_back (RadioMenuElem (mode_group, _("Channel Colors"),
725 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode),
726 ChannelColors, false, true)));
727 _channel_color_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
728 _channel_color_mode_item->set_active(_color_mode == ChannelColors);
730 items.push_back (RadioMenuElem (mode_group, _("Track Color"),
731 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode),
732 TrackColor, false, true)));
733 _channel_color_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
734 _channel_color_mode_item->set_active(_color_mode == TrackColor);
736 return mode_menu;
739 void
740 MidiTimeAxisView::set_note_mode(NoteMode mode)
742 if (_note_mode != mode || midi_track()->note_mode() != mode) {
743 _note_mode = mode;
744 midi_track()->set_note_mode(mode);
745 set_gui_property ("note-mode", enum_2_string(_note_mode));
746 _view->redisplay_track();
750 void
751 MidiTimeAxisView::set_color_mode (ColorMode mode, bool force, bool redisplay)
753 if (_color_mode == mode && !force) {
754 return;
757 if (mode == ChannelColors) {
758 _channel_selector.set_channel_colors(CanvasNoteEvent::midi_channel_colors);
759 } else {
760 _channel_selector.set_default_channel_color();
763 _color_mode = mode;
764 set_gui_property ("color-mode", enum_2_string(_color_mode));
765 if (redisplay) {
766 _view->redisplay_track();
770 void
771 MidiTimeAxisView::set_note_range(MidiStreamView::VisibleNoteRange range)
773 if (!_ignore_signals)
774 midi_view()->set_note_range(range);
778 void
779 MidiTimeAxisView::update_range()
781 MidiGhostRegion* mgr;
783 for(list<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
784 if ((mgr = dynamic_cast<MidiGhostRegion*>(*i)) != 0) {
785 mgr->update_range();
790 void
791 MidiTimeAxisView::show_all_automation (bool apply_to_selection)
793 if (apply_to_selection) {
794 _editor.get_selection().tracks.foreach_midi_time_axis (boost::bind (&MidiTimeAxisView::show_all_automation, _1, false));
795 } else {
796 if (midi_track()) {
797 const set<Evoral::Parameter> params = midi_track()->midi_playlist()->contained_automation();
799 for (set<Evoral::Parameter>::const_iterator i = params.begin(); i != params.end(); ++i) {
800 create_automation_child(*i, true);
804 RouteTimeAxisView::show_all_automation ();
808 void
809 MidiTimeAxisView::show_existing_automation (bool apply_to_selection)
811 if (apply_to_selection) {
812 _editor.get_selection().tracks.foreach_midi_time_axis (boost::bind (&MidiTimeAxisView::show_existing_automation, _1, false));
813 } else {
814 if (midi_track()) {
815 const set<Evoral::Parameter> params = midi_track()->midi_playlist()->contained_automation();
817 for (set<Evoral::Parameter>::const_iterator i = params.begin(); i != params.end(); ++i) {
818 create_automation_child (*i, true);
822 RouteTimeAxisView::show_existing_automation ();
826 /** Create an automation track for the given parameter (pitch bend, channel pressure).
828 void
829 MidiTimeAxisView::create_automation_child (const Evoral::Parameter& param, bool show)
831 if (param.type() == NullAutomation) {
832 cerr << "WARNING: Attempt to create NullAutomation child, ignoring" << endl;
833 return;
836 AutomationTracks::iterator existing = _automation_tracks.find (param);
838 if (existing != _automation_tracks.end()) {
840 /* automation track created because we had existing data for
841 * the processor, but visibility may need to be controlled
842 * since it will have been set visible by default.
845 cerr << "show existing auto track: " << show << " noredraw " << no_redraw << endl;
847 if (existing->second->set_marked_for_display (show) && !no_redraw) {
848 request_redraw ();
851 return;
854 boost::shared_ptr<AutomationTimeAxisView> track;
856 switch (param.type()) {
858 case GainAutomation:
859 create_gain_automation_child (param, show);
860 break;
862 case PluginAutomation:
863 /* handled elsewhere */
864 break;
866 case MidiCCAutomation:
867 case MidiPgmChangeAutomation:
868 case MidiPitchBenderAutomation:
869 case MidiChannelPressureAutomation:
870 case MidiSystemExclusiveAutomation:
871 /* These controllers are region "automation" - they are owned
872 * by regions (and their MidiModels), not by the track. As a
873 * result we do not create an AutomationList/Line for the track
874 * ... except here we are doing something!! XXX
877 track.reset (new AutomationTimeAxisView (
878 _session,
879 _route,
880 boost::shared_ptr<Automatable> (),
881 boost::shared_ptr<AutomationControl> (),
882 param,
883 _editor,
884 *this,
885 true,
886 parent_canvas,
887 _route->describe_parameter(param)
890 if (_view) {
891 _view->foreach_regionview (sigc::mem_fun (*track.get(), &TimeAxisView::add_ghost));
894 add_automation_child (param, track, show);
895 break;
897 default:
898 error << "MidiTimeAxisView: unknown automation child " << EventTypeMap::instance().to_symbol(param) << endmsg;
902 void
903 MidiTimeAxisView::route_active_changed ()
905 RouteUI::route_active_changed ();
907 if (is_track()) {
908 if (_route->active()) {
909 controls_ebox.set_name ("MidiTrackControlsBaseUnselected");
910 controls_base_selected_name = "MidiTrackControlsBaseSelected";
911 controls_base_unselected_name = "MidiTrackControlsBaseUnselected";
912 } else {
913 controls_ebox.set_name ("MidiTrackControlsBaseInactiveUnselected");
914 controls_base_selected_name = "MidiTrackControlsBaseInactiveSelected";
915 controls_base_unselected_name = "MidiTrackControlsBaseInactiveUnselected";
917 } else {
919 throw; // wha?
921 if (_route->active()) {
922 controls_ebox.set_name ("BusControlsBaseUnselected");
923 controls_base_selected_name = "BusControlsBaseSelected";
924 controls_base_unselected_name = "BusControlsBaseUnselected";
925 } else {
926 controls_ebox.set_name ("BusControlsBaseInactiveUnselected");
927 controls_base_selected_name = "BusControlsBaseInactiveSelected";
928 controls_base_unselected_name = "BusControlsBaseInactiveUnselected";
935 void
936 MidiTimeAxisView::add_note_selection (uint8_t note)
938 if (!_editor.internal_editing()) {
939 return;
942 uint16_t chn_mask = _channel_selector.get_selected_channels();
944 if (_view->num_selected_regionviews() == 0) {
945 _view->foreach_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection_region_view), note, chn_mask));
946 } else {
947 _view->foreach_selected_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection_region_view), note, chn_mask));
951 void
952 MidiTimeAxisView::extend_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::extend_note_selection_region_view), note, chn_mask));
962 } else {
963 _view->foreach_selected_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::extend_note_selection_region_view), note, chn_mask));
967 void
968 MidiTimeAxisView::toggle_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::toggle_note_selection_region_view), note, chn_mask));
978 } else {
979 _view->foreach_selected_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection_region_view), note, chn_mask));
983 void
984 MidiTimeAxisView::add_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
986 dynamic_cast<MidiRegionView*>(rv)->select_matching_notes (note, chn_mask, false, false);
989 void
990 MidiTimeAxisView::extend_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
992 dynamic_cast<MidiRegionView*>(rv)->select_matching_notes (note, chn_mask, true, true);
995 void
996 MidiTimeAxisView::toggle_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
998 dynamic_cast<MidiRegionView*>(rv)->toggle_matching_notes (note, chn_mask);
1001 void
1002 MidiTimeAxisView::set_channel_mode (ChannelMode, uint16_t)
1004 /* hide all automation tracks that use the wrong channel(s) and show all those that use
1005 the right ones.
1008 uint16_t selected_channels = _channel_selector.get_selected_channels();
1009 bool changed = false;
1011 no_redraw = true;
1013 for (uint32_t ctl = 0; ctl < 127; ++ctl) {
1015 for (uint32_t chn = 0; chn < 16; ++chn) {
1016 Evoral::Parameter fully_qualified_param (MidiCCAutomation, chn, ctl);
1017 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
1019 if (!track) {
1020 continue;
1023 if ((selected_channels & (0x0001 << chn)) == 0) {
1024 /* channel not in use. hiding it will trigger RouteTimeAxisView::automation_track_hidden()
1025 which will cause a redraw. We don't want one per channel, so block that with no_redraw.
1027 changed = track->set_marked_for_display (false) || changed;
1028 } else {
1029 changed = track->set_marked_for_display (true) || changed;
1034 no_redraw = false;
1036 /* TODO: Bender, Pressure */
1038 /* invalidate the controller menu, so that we rebuild it next time */
1039 _controller_menu_map.clear ();
1040 delete controller_menu;
1041 controller_menu = 0;
1043 if (changed) {
1044 request_redraw ();
1048 Gtk::CheckMenuItem*
1049 MidiTimeAxisView::automation_child_menu_item (Evoral::Parameter param)
1051 Gtk::CheckMenuItem* m = RouteTimeAxisView::automation_child_menu_item (param);
1052 if (m) {
1053 return m;
1056 ParameterMenuMap::iterator i = _controller_menu_map.find (param);
1057 if (i != _controller_menu_map.end()) {
1058 return i->second;
1061 i = _channel_command_menu_map.find (param);
1062 if (i != _channel_command_menu_map.end()) {
1063 return i->second;
1066 return 0;
1069 boost::shared_ptr<MidiRegion>
1070 MidiTimeAxisView::add_region (framepos_t pos, framecnt_t length, bool commit)
1072 Editor* real_editor = dynamic_cast<Editor*> (&_editor);
1074 real_editor->begin_reversible_command (Operations::create_region);
1075 playlist()->clear_changes ();
1077 real_editor->snap_to (pos, 0);
1079 boost::shared_ptr<Source> src = _session->create_midi_source_for_session (view()->trackview().track().get(),
1080 view()->trackview().track()->name());
1081 PropertyList plist;
1083 plist.add (ARDOUR::Properties::start, 0);
1084 plist.add (ARDOUR::Properties::length, length);
1085 plist.add (ARDOUR::Properties::name, PBD::basename_nosuffix(src->name()));
1087 boost::shared_ptr<Region> region = (RegionFactory::create (src, plist));
1089 playlist()->add_region (region, pos);
1090 _session->add_command (new StatefulDiffCommand (playlist()));
1092 if (commit) {
1093 real_editor->commit_reversible_command ();
1096 return boost::dynamic_pointer_cast<MidiRegion>(region);
1099 void
1100 MidiTimeAxisView::ensure_step_editor ()
1102 if (!_step_editor) {
1103 _step_editor = new StepEditor (_editor, midi_track(), *this);
1107 void
1108 MidiTimeAxisView::start_step_editing ()
1110 ensure_step_editor ();
1111 _step_editor->start_step_editing ();
1114 void
1115 MidiTimeAxisView::stop_step_editing ()
1117 if (_step_editor) {
1118 _step_editor->stop_step_editing ();
1123 /** @return channel (counted from 0) to add an event to, based on the current setting
1124 * of the channel selector.
1126 uint8_t
1127 MidiTimeAxisView::get_channel_for_add () const
1129 uint16_t const chn_mask = _channel_selector.get_selected_channels ();
1130 int chn_cnt = 0;
1131 uint8_t channel = 0;
1133 /* pick the highest selected channel, unless all channels are selected,
1134 which is interpreted to mean channel 1 (zero)
1137 for (uint16_t i = 0; i < 16; ++i) {
1138 if (chn_mask & (1<<i)) {
1139 channel = i;
1140 chn_cnt++;
1144 if (chn_cnt == 16) {
1145 channel = 0;
1148 return channel;