its about that time
[ardour2.git] / gtk2_ardour / midi_time_axis.cc
blobbf0adfd3613615a89fb41410a0cb008fd4ef946f
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 controls_hbox.pack_start(*_range_scroomer);
158 controls_hbox.pack_start(*_piano_roll_header);
160 controls_ebox.set_name ("MidiTrackControlsBaseUnselected");
161 controls_base_selected_name = "MidiTrackControlsBaseSelected";
162 controls_base_unselected_name = "MidiTrackControlsBaseUnselected";
164 midi_view()->NoteRangeChanged.connect (sigc::mem_fun(*this, &MidiTimeAxisView::update_range));
166 /* ask for notifications of any new RegionViews */
167 _view->RegionViewAdded.connect (sigc::mem_fun(*this, &MidiTimeAxisView::region_view_added));
169 if (!_editor.have_idled()) {
170 /* first idle will do what we need */
171 } else {
172 first_idle ();
176 HBox* midi_controls_hbox = manage(new HBox());
178 MIDI::Name::MidiPatchManager& patch_manager = MIDI::Name::MidiPatchManager::instance();
180 MIDI::Name::MasterDeviceNames::Models::const_iterator m = patch_manager.all_models().begin();
181 for (; m != patch_manager.all_models().end(); ++m) {
182 _model_selector.append_text(m->c_str());
185 _model_selector.signal_changed().connect(sigc::mem_fun(*this, &MidiTimeAxisView::model_changed));
187 _custom_device_mode_selector.signal_changed().connect(
188 sigc::mem_fun(*this, &MidiTimeAxisView::custom_device_mode_changed));
190 // TODO: persist the choice
191 // this initializes the comboboxes and sends out the signal
192 _model_selector.set_active(0);
194 midi_controls_hbox->pack_start(_channel_selector, true, false);
195 if (!patch_manager.all_models().empty()) {
196 _midi_controls_box.pack_start(_model_selector, true, false);
197 _midi_controls_box.pack_start(_custom_device_mode_selector, true, false);
200 _midi_controls_box.pack_start(*midi_controls_hbox, true, true);
202 controls_vbox.pack_start(_midi_controls_box, false, false);
204 // restore channel selector settings
205 _channel_selector.set_channel_mode(midi_track()->get_channel_mode(), midi_track()->get_channel_mask());
206 _channel_selector.mode_changed.connect(
207 sigc::mem_fun(*midi_track(), &MidiTrack::set_channel_mode));
208 _channel_selector.mode_changed.connect(
209 sigc::mem_fun(*this, &MidiTimeAxisView::set_channel_mode));
211 XMLProperty *prop;
212 if ((prop = xml_node->property ("color-mode")) != 0) {
213 _color_mode = ColorMode (string_2_enum(prop->value(), _color_mode));
214 if (_color_mode == ChannelColors) {
215 _channel_selector.set_channel_colors(CanvasNoteEvent::midi_channel_colors);
219 if ((prop = xml_node->property ("note-mode")) != 0) {
220 _note_mode = NoteMode (string_2_enum(prop->value(), _note_mode));
221 if (_percussion_mode_item) {
222 _percussion_mode_item->set_active (_note_mode == Percussive);
227 void
228 MidiTimeAxisView::first_idle ()
230 if (is_track ()) {
231 _view->attach ();
235 MidiTimeAxisView::~MidiTimeAxisView ()
237 delete _piano_roll_header;
238 _piano_roll_header = 0;
240 delete _range_scroomer;
241 _range_scroomer = 0;
243 delete controller_menu;
244 delete _step_editor;
247 void
248 MidiTimeAxisView::enter_internal_edit_mode ()
250 if (midi_view()) {
251 midi_view()->enter_internal_edit_mode ();
255 void
256 MidiTimeAxisView::leave_internal_edit_mode ()
258 if (midi_view()) {
259 midi_view()->leave_internal_edit_mode ();
263 void
264 MidiTimeAxisView::check_step_edit ()
266 ensure_step_editor ();
267 _step_editor->check_step_edit ();
270 void
271 MidiTimeAxisView::model_changed()
273 std::list<std::string> device_modes = MIDI::Name::MidiPatchManager::instance()
274 .custom_device_mode_names_by_model(_model_selector.get_active_text());
276 _custom_device_mode_selector.clear_items();
278 for (std::list<std::string>::const_iterator i = device_modes.begin();
279 i != device_modes.end(); ++i) {
280 cerr << "found custom device mode " << *i << " thread_id: " << pthread_self() << endl;
281 _custom_device_mode_selector.append_text(*i);
284 _custom_device_mode_selector.set_active(0);
287 void MidiTimeAxisView::custom_device_mode_changed()
289 _midi_patch_settings_changed.emit(_model_selector.get_active_text(),
290 _custom_device_mode_selector.get_active_text());
293 MidiStreamView*
294 MidiTimeAxisView::midi_view()
296 return dynamic_cast<MidiStreamView*>(_view);
299 guint32
300 MidiTimeAxisView::show_at (double y, int& nth, Gtk::VBox *parent)
302 ensure_xml_node ();
303 xml_node->add_property ("shown-editor", "yes");
305 guint32 ret = TimeAxisView::show_at (y, nth, parent);
306 return ret;
309 void
310 MidiTimeAxisView::hide ()
312 ensure_xml_node ();
313 xml_node->add_property ("shown-editor", "no");
315 TimeAxisView::hide ();
318 void
319 MidiTimeAxisView::set_height (uint32_t h)
321 RouteTimeAxisView::set_height (h);
323 if (height >= MIDI_CONTROLS_BOX_MIN_HEIGHT) {
324 _midi_controls_box.show_all ();
325 } else {
326 _midi_controls_box.hide();
329 if (height >= KEYBOARD_MIN_HEIGHT) {
330 if (is_track() && _range_scroomer)
331 _range_scroomer->show();
332 if (is_track() && _piano_roll_header)
333 _piano_roll_header->show();
334 } else {
335 if (is_track() && _range_scroomer)
336 _range_scroomer->hide();
337 if (is_track() && _piano_roll_header)
338 _piano_roll_header->hide();
342 void
343 MidiTimeAxisView::append_extra_display_menu_items ()
345 using namespace Menu_Helpers;
347 MenuList& items = display_menu->items();
349 // Note range
350 Menu *range_menu = manage(new Menu);
351 MenuList& range_items = range_menu->items();
352 range_menu->set_name ("ArdourContextMenu");
354 range_items.push_back (MenuElem (_("Show Full Range"), sigc::bind (
355 sigc::mem_fun(*this, &MidiTimeAxisView::set_note_range),
356 MidiStreamView::FullRange)));
358 range_items.push_back (MenuElem (_("Fit Contents"), sigc::bind (
359 sigc::mem_fun(*this, &MidiTimeAxisView::set_note_range),
360 MidiStreamView::ContentsRange)));
362 items.push_back (MenuElem (_("Note range"), *range_menu));
363 items.push_back (MenuElem (_("Note mode"), *build_note_mode_menu()));
364 items.push_back (MenuElem (_("Default Channel"), *build_def_channel_menu()));
366 items.push_back (CheckMenuElem (_("MIDI Thru"), sigc::mem_fun(*this, &MidiTimeAxisView::toggle_midi_thru)));
367 _midi_thru_item = dynamic_cast<CheckMenuItem*>(&items.back());
369 items.push_back (SeparatorElem ());
372 Gtk::Menu*
373 MidiTimeAxisView::build_def_channel_menu ()
375 using namespace Menu_Helpers;
377 default_channel_menu = manage (new Menu ());
379 uint8_t defchn = midi_track()->default_channel();
380 MenuList& def_channel_items = default_channel_menu->items();
381 RadioMenuItem* item;
382 RadioMenuItem::Group dc_group;
384 for (int i = 0; i < 16; ++i) {
385 char buf[4];
386 snprintf (buf, sizeof (buf), "%d", i+1);
388 def_channel_items.push_back (RadioMenuElem (dc_group, buf,
389 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_default_channel), i)));
390 item = dynamic_cast<RadioMenuItem*>(&def_channel_items.back());
391 item->set_active ((i == defchn));
394 return default_channel_menu;
397 void
398 MidiTimeAxisView::set_default_channel (int chn)
400 midi_track()->set_default_channel (chn);
403 void
404 MidiTimeAxisView::toggle_midi_thru ()
406 if (!_midi_thru_item) {
407 return;
410 bool view_yn = _midi_thru_item->get_active();
411 if (view_yn != midi_track()->midi_thru()) {
412 midi_track()->set_midi_thru (view_yn);
416 void
417 MidiTimeAxisView::build_automation_action_menu (bool for_selection)
419 using namespace Menu_Helpers;
421 /* If we have a controller menu, we need to detach it before
422 RouteTimeAxis::build_automation_action_menu destroys the
423 menu it is attached to. Otherwise GTK destroys
424 controller_menu's gobj, meaning that it can't be reattached
425 below. See bug #3134.
428 if (controller_menu) {
429 detach_menu (*controller_menu);
432 _channel_command_menu_map.clear ();
433 RouteTimeAxisView::build_automation_action_menu (for_selection);
435 MenuList& automation_items = automation_action_menu->items();
437 uint16_t selected_channels = _channel_selector.get_selected_channels();
439 if (selected_channels != 0) {
441 automation_items.push_back (SeparatorElem());
443 /* these 2 MIDI "command" types are semantically more like automation than note data,
444 but they are not MIDI controllers. We give them special status in this menu, since
445 they will not show up in the controller list and anyone who actually knows
446 something about MIDI (!) would not expect to find them there.
449 add_channel_command_menu_item (automation_items, _("Bender"), MidiPitchBenderAutomation, 0);
450 automation_items.back().set_sensitive (!for_selection || _editor.get_selection().tracks.size() == 1);
451 add_channel_command_menu_item (automation_items, _("Pressure"), MidiChannelPressureAutomation, 0);
452 automation_items.back().set_sensitive (!for_selection || _editor.get_selection().tracks.size() == 1);
454 /* now all MIDI controllers. Always offer the possibility that we will rebuild the controllers menu
455 since it might need to be updated after a channel mode change or other change. Also detach it
456 first in case it has been used anywhere else.
459 build_controller_menu ();
461 automation_items.push_back (SeparatorElem());
462 automation_items.push_back (MenuElem (_("Controllers"), *controller_menu));
463 automation_items.back().set_sensitive (!for_selection || _editor.get_selection().tracks.size() == 1);
464 } else {
465 automation_items.push_back (MenuElem (string_compose ("<i>%1</i>", _("No MIDI Channels selected"))));
466 dynamic_cast<Label*> (automation_items.back().get_child())->set_use_markup (true);
471 void
472 MidiTimeAxisView::change_all_channel_tracks_visibility (bool yn, Evoral::Parameter param)
474 uint16_t selected_channels = _channel_selector.get_selected_channels();
476 for (uint8_t chn = 0; chn < 16; chn++) {
477 if (selected_channels & (0x0001 << chn)) {
479 Evoral::Parameter fully_qualified_param (param.type(), chn, param.id());
480 Gtk::CheckMenuItem* menu = automation_child_menu_item (fully_qualified_param);
482 if (menu) {
483 menu->set_active (yn);
489 void
490 MidiTimeAxisView::add_channel_command_menu_item (Menu_Helpers::MenuList& items, const string& label, AutomationType auto_type, uint8_t cmd)
492 using namespace Menu_Helpers;
494 /* count the number of selected channels because we will build a different menu structure if there is more than 1 selected.
497 uint16_t selected_channels = _channel_selector.get_selected_channels();
498 int chn_cnt = 0;
500 for (uint8_t chn = 0; chn < 16; chn++) {
501 if (selected_channels & (0x0001 << chn)) {
502 if (++chn_cnt > 1) {
503 break;
508 if (chn_cnt > 1) {
510 /* multiple channels - create a submenu, with 1 item per channel */
512 Menu* chn_menu = manage (new Menu);
513 MenuList& chn_items (chn_menu->items());
514 Evoral::Parameter param_without_channel (auto_type, 0, cmd);
516 /* add a couple of items to hide/show all of them */
518 chn_items.push_back (MenuElem (_("Hide all channels"),
519 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
520 false, param_without_channel)));
521 chn_items.push_back (MenuElem (_("Show all channels"),
522 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
523 true, param_without_channel)));
525 for (uint8_t chn = 0; chn < 16; chn++) {
526 if (selected_channels & (0x0001 << chn)) {
528 /* for each selected channel, add a menu item for this controller */
530 Evoral::Parameter fully_qualified_param (auto_type, chn, cmd);
531 chn_items.push_back (CheckMenuElem (string_compose (_("Channel %1"), chn+1),
532 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
533 fully_qualified_param)));
535 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
536 bool visible = false;
538 if (track) {
539 if (track->marked_for_display()) {
540 visible = true;
544 CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&chn_items.back());
545 _channel_command_menu_map[fully_qualified_param] = cmi;
546 cmi->set_active (visible);
550 /* now create an item in the parent menu that has the per-channel list as a submenu */
552 items.push_back (MenuElem (label, *chn_menu));
554 } else {
556 /* just one channel - create a single menu item for this command+channel combination*/
558 for (uint8_t chn = 0; chn < 16; chn++) {
559 if (selected_channels & (0x0001 << chn)) {
561 Evoral::Parameter fully_qualified_param (auto_type, chn, cmd);
562 items.push_back (CheckMenuElem (label,
563 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
564 fully_qualified_param)));
566 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
567 bool visible = false;
569 if (track) {
570 if (track->marked_for_display()) {
571 visible = true;
575 CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&items.back());
576 _channel_command_menu_map[fully_qualified_param] = cmi;
577 cmi->set_active (visible);
579 /* one channel only */
580 break;
586 void
587 MidiTimeAxisView::build_controller_menu ()
589 using namespace Menu_Helpers;
591 if (controller_menu) {
592 /* it exists and has not been invalidated by a channel mode change, so just return it */
593 return;
596 controller_menu = new Menu; // explicitly managed by us
597 MenuList& items (controller_menu->items());
599 /* create several "top level" menu items for sets of controllers (16 at a time), and populate each one with a submenu
600 for each controller+channel combination covering the currently selected channels for this track
603 uint16_t selected_channels = _channel_selector.get_selected_channels();
605 /* count the number of selected channels because we will build a different menu structure if there is more than 1 selected.
608 int chn_cnt = 0;
610 for (uint8_t chn = 0; chn < 16; chn++) {
611 if (selected_channels & (0x0001 << chn)) {
612 if (++chn_cnt > 1) {
613 break;
618 /* loop over all 127 MIDI controllers, in groups of 16; except don't offer
619 bank select controllers, as they are handled by the `patch' code */
621 for (int i = 0; i < 127; i += 16) {
623 Menu* ctl_menu = manage (new Menu);
624 MenuList& ctl_items (ctl_menu->items());
627 /* for each controller, consider whether to create a submenu or a single item */
629 for (int ctl = i; ctl < i+16; ++ctl) {
631 if (ctl == MIDI_CTL_MSB_BANK || ctl == MIDI_CTL_LSB_BANK) {
632 continue;
635 if (chn_cnt > 1) {
637 /* multiple channels - create a submenu, with 1 item per channel */
639 Menu* chn_menu = manage (new Menu);
640 MenuList& chn_items (chn_menu->items());
642 /* add a couple of items to hide/show this controller on all channels */
644 Evoral::Parameter param_without_channel (MidiCCAutomation, 0, ctl);
645 chn_items.push_back (MenuElem (_("Hide all channels"),
646 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
647 false, param_without_channel)));
648 chn_items.push_back (MenuElem (_("Show all channels"),
649 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
650 true, param_without_channel)));
652 for (uint8_t chn = 0; chn < 16; chn++) {
653 if (selected_channels & (0x0001 << chn)) {
655 /* for each selected channel, add a menu item for this controller */
657 Evoral::Parameter fully_qualified_param (MidiCCAutomation, chn, ctl);
658 chn_items.push_back (CheckMenuElem (string_compose (_("Channel %1"), chn+1),
659 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
660 fully_qualified_param)));
662 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
663 bool visible = false;
665 if (track) {
666 if (track->marked_for_display()) {
667 visible = true;
671 CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&chn_items.back());
672 _controller_menu_map[fully_qualified_param] = cmi;
673 cmi->set_active (visible);
677 /* add the per-channel menu to the list of controllers, with the name of the controller */
678 ctl_items.push_back (MenuElem (string_compose ("<b>%1</b>: %2", ctl, midi_name (ctl)), *chn_menu));
679 dynamic_cast<Label*> (ctl_items.back().get_child())->set_use_markup (true);
681 } else {
683 /* just one channel - create a single menu item for this ctl+channel combination*/
685 for (uint8_t chn = 0; chn < 16; chn++) {
686 if (selected_channels & (0x0001 << chn)) {
688 Evoral::Parameter fully_qualified_param (MidiCCAutomation, chn, ctl);
689 ctl_items.push_back (
690 CheckMenuElem (
691 string_compose ("<b>%1</b>: %2 [%3]", ctl, midi_name (ctl), int (chn)),
692 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
693 fully_qualified_param)
696 dynamic_cast<Label*> (ctl_items.back().get_child())->set_use_markup (true);
698 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
699 bool visible = false;
701 if (track) {
702 if (track->marked_for_display()) {
703 visible = true;
707 CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&ctl_items.back());
708 _controller_menu_map[fully_qualified_param] = cmi;
709 cmi->set_active (visible);
711 /* one channel only */
712 break;
718 /* add the menu for this block of controllers to the overall controller menu */
720 items.push_back (MenuElem (string_compose (_("Controllers %1-%2"), i, i+15), *ctl_menu));
724 Gtk::Menu*
725 MidiTimeAxisView::build_note_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, _("Sustained"),
735 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_mode), Sustained)));
736 _note_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
737 _note_mode_item->set_active(_note_mode == Sustained);
739 items.push_back (RadioMenuElem (mode_group, _("Percussive"),
740 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_mode), Percussive)));
741 _percussion_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
742 _percussion_mode_item->set_active(_note_mode == Percussive);
744 return mode_menu;
747 Gtk::Menu*
748 MidiTimeAxisView::build_color_mode_menu()
750 using namespace Menu_Helpers;
752 Menu* mode_menu = manage (new Menu);
753 MenuList& items = mode_menu->items();
754 mode_menu->set_name ("ArdourContextMenu");
756 RadioMenuItem::Group mode_group;
757 items.push_back (RadioMenuElem (mode_group, _("Meter Colors"),
758 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode), MeterColors)));
759 _meter_color_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
760 _meter_color_mode_item->set_active(_color_mode == MeterColors);
762 items.push_back (RadioMenuElem (mode_group, _("Channel Colors"),
763 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode), ChannelColors)));
764 _channel_color_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
765 _channel_color_mode_item->set_active(_color_mode == ChannelColors);
767 items.push_back (RadioMenuElem (mode_group, _("Track Color"),
768 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode), TrackColor)));
769 _channel_color_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
770 _channel_color_mode_item->set_active(_color_mode == TrackColor);
772 return mode_menu;
775 void
776 MidiTimeAxisView::set_note_mode(NoteMode mode)
778 if (_note_mode != mode || midi_track()->note_mode() != mode) {
779 _note_mode = mode;
780 midi_track()->set_note_mode(mode);
781 xml_node->add_property ("note-mode", enum_2_string(_note_mode));
782 _view->redisplay_track();
786 void
787 MidiTimeAxisView::set_color_mode(ColorMode mode)
789 if (_color_mode != mode) {
790 if (mode == ChannelColors) {
791 _channel_selector.set_channel_colors(CanvasNoteEvent::midi_channel_colors);
792 } else {
793 _channel_selector.set_default_channel_color();
796 _color_mode = mode;
797 xml_node->add_property ("color-mode", enum_2_string(_color_mode));
798 _view->redisplay_track();
802 void
803 MidiTimeAxisView::set_note_range(MidiStreamView::VisibleNoteRange range)
805 if (!_ignore_signals)
806 midi_view()->set_note_range(range);
810 void
811 MidiTimeAxisView::update_range()
813 MidiGhostRegion* mgr;
815 for(list<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
816 if ((mgr = dynamic_cast<MidiGhostRegion*>(*i)) != 0) {
817 mgr->update_range();
822 void
823 MidiTimeAxisView::show_all_automation (bool apply_to_selection)
825 if (apply_to_selection) {
826 _editor.get_selection().tracks.foreach_midi_time_axis (boost::bind (&MidiTimeAxisView::show_all_automation, _1, false));
827 } else {
828 if (midi_track()) {
829 const set<Evoral::Parameter> params = midi_track()->midi_playlist()->contained_automation();
831 for (set<Evoral::Parameter>::const_iterator i = params.begin(); i != params.end(); ++i) {
832 create_automation_child(*i, true);
836 RouteTimeAxisView::show_all_automation ();
840 void
841 MidiTimeAxisView::show_existing_automation (bool apply_to_selection)
843 if (apply_to_selection) {
844 _editor.get_selection().tracks.foreach_midi_time_axis (boost::bind (&MidiTimeAxisView::show_existing_automation, _1, false));
845 } else {
846 if (midi_track()) {
847 const set<Evoral::Parameter> params = midi_track()->midi_playlist()->contained_automation();
849 for (set<Evoral::Parameter>::const_iterator i = params.begin(); i != params.end(); ++i) {
850 create_automation_child(*i, true);
854 RouteTimeAxisView::show_existing_automation ();
858 /** Create an automation track for the given parameter (pitch bend, channel pressure).
860 void
861 MidiTimeAxisView::create_automation_child (const Evoral::Parameter& param, bool show)
863 if (param.type() == NullAutomation) {
864 cerr << "WARNING: Attempt to create NullAutomation child, ignoring" << endl;
865 return;
868 AutomationTracks::iterator existing = _automation_tracks.find (param);
869 if (existing != _automation_tracks.end()) {
870 return;
873 if (param.type() == GainAutomation) {
874 create_gain_automation_child (param, show);
875 } else {
877 /* These controllers are region "automation", so we do not create
878 * an AutomationList/Line for the track */
880 boost::shared_ptr<AutomationTimeAxisView> track (
881 new AutomationTimeAxisView (
882 _session,
883 _route,
884 boost::shared_ptr<Automatable> (),
885 boost::shared_ptr<AutomationControl> (),
886 param,
887 _editor,
888 *this,
889 true,
890 parent_canvas,
891 _route->describe_parameter(param)
895 if (_view) {
896 _view->foreach_regionview (sigc::mem_fun (*track.get(), &TimeAxisView::add_ghost));
899 add_automation_child (param, track, show);
904 void
905 MidiTimeAxisView::route_active_changed ()
907 RouteUI::route_active_changed ();
909 if (is_track()) {
910 if (_route->active()) {
911 controls_ebox.set_name ("MidiTrackControlsBaseUnselected");
912 controls_base_selected_name = "MidiTrackControlsBaseSelected";
913 controls_base_unselected_name = "MidiTrackControlsBaseUnselected";
914 } else {
915 controls_ebox.set_name ("MidiTrackControlsBaseInactiveUnselected");
916 controls_base_selected_name = "MidiTrackControlsBaseInactiveSelected";
917 controls_base_unselected_name = "MidiTrackControlsBaseInactiveUnselected";
919 } else {
921 throw; // wha?
923 if (_route->active()) {
924 controls_ebox.set_name ("BusControlsBaseUnselected");
925 controls_base_selected_name = "BusControlsBaseSelected";
926 controls_base_unselected_name = "BusControlsBaseUnselected";
927 } else {
928 controls_ebox.set_name ("BusControlsBaseInactiveUnselected");
929 controls_base_selected_name = "BusControlsBaseInactiveSelected";
930 controls_base_unselected_name = "BusControlsBaseInactiveUnselected";
937 void
938 MidiTimeAxisView::add_note_selection (uint8_t note)
940 if (!_editor.internal_editing()) {
941 return;
944 uint16_t chn_mask = _channel_selector.get_selected_channels();
946 if (_view->num_selected_regionviews() == 0) {
947 _view->foreach_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection_region_view), note, chn_mask));
948 } else {
949 _view->foreach_selected_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection_region_view), note, chn_mask));
953 void
954 MidiTimeAxisView::extend_note_selection (uint8_t note)
956 if (!_editor.internal_editing()) {
957 return;
960 uint16_t chn_mask = _channel_selector.get_selected_channels();
962 if (_view->num_selected_regionviews() == 0) {
963 _view->foreach_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::extend_note_selection_region_view), note, chn_mask));
964 } else {
965 _view->foreach_selected_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::extend_note_selection_region_view), note, chn_mask));
969 void
970 MidiTimeAxisView::toggle_note_selection (uint8_t note)
972 if (!_editor.internal_editing()) {
973 return;
976 uint16_t chn_mask = _channel_selector.get_selected_channels();
978 if (_view->num_selected_regionviews() == 0) {
979 _view->foreach_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection_region_view), note, chn_mask));
980 } else {
981 _view->foreach_selected_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection_region_view), note, chn_mask));
985 void
986 MidiTimeAxisView::add_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
988 dynamic_cast<MidiRegionView*>(rv)->select_matching_notes (note, chn_mask, false, false);
991 void
992 MidiTimeAxisView::extend_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
994 dynamic_cast<MidiRegionView*>(rv)->select_matching_notes (note, chn_mask, true, true);
997 void
998 MidiTimeAxisView::toggle_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1000 dynamic_cast<MidiRegionView*>(rv)->toggle_matching_notes (note, chn_mask);
1003 void
1004 MidiTimeAxisView::set_channel_mode (ChannelMode, uint16_t)
1006 /* hide all automation tracks that use the wrong channel(s) and show all those that use
1007 the right ones.
1010 uint16_t selected_channels = _channel_selector.get_selected_channels();
1011 bool changed = false;
1013 no_redraw = true;
1015 for (uint32_t ctl = 0; ctl < 127; ++ctl) {
1017 for (uint32_t chn = 0; chn < 16; ++chn) {
1018 Evoral::Parameter fully_qualified_param (MidiCCAutomation, chn, ctl);
1019 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
1021 if (!track) {
1022 continue;
1025 if ((selected_channels & (0x0001 << chn)) == 0) {
1026 /* channel not in use. hiding it will trigger RouteTimeAxisView::automation_track_hidden()
1027 which will cause a redraw. We don't want one per channel, so block that with no_redraw.
1029 changed = track->set_visibility (false) || changed;
1030 } else {
1031 changed = track->set_visibility (true) || changed;
1036 no_redraw = false;
1038 /* TODO: Bender, Pressure */
1040 /* invalidate the controller menu, so that we rebuild it next time */
1041 _controller_menu_map.clear ();
1042 delete controller_menu;
1043 controller_menu = 0;
1045 if (changed) {
1046 _route->gui_changed ("track_height", this);
1050 Gtk::CheckMenuItem*
1051 MidiTimeAxisView::automation_child_menu_item (Evoral::Parameter param)
1053 Gtk::CheckMenuItem* m = RouteTimeAxisView::automation_child_menu_item (param);
1054 if (m) {
1055 return m;
1058 ParameterMenuMap::iterator i = _controller_menu_map.find (param);
1059 if (i != _controller_menu_map.end()) {
1060 return i->second;
1063 i = _channel_command_menu_map.find (param);
1064 if (i != _channel_command_menu_map.end()) {
1065 return i->second;
1068 return 0;
1071 boost::shared_ptr<MidiRegion>
1072 MidiTimeAxisView::add_region (framepos_t pos, framecnt_t length, bool commit)
1074 Editor* real_editor = dynamic_cast<Editor*> (&_editor);
1076 real_editor->begin_reversible_command (Operations::create_region);
1077 playlist()->clear_changes ();
1079 real_editor->snap_to (pos, 0);
1081 boost::shared_ptr<Source> src = _session->create_midi_source_for_session (view()->trackview().track().get(),
1082 view()->trackview().track()->name());
1083 PropertyList plist;
1085 plist.add (ARDOUR::Properties::start, 0);
1086 plist.add (ARDOUR::Properties::length, length);
1087 plist.add (ARDOUR::Properties::name, PBD::basename_nosuffix(src->name()));
1089 boost::shared_ptr<Region> region = (RegionFactory::create (src, plist));
1091 playlist()->add_region (region, pos);
1092 _session->add_command (new StatefulDiffCommand (playlist()));
1094 if (commit) {
1095 real_editor->commit_reversible_command ();
1098 return boost::dynamic_pointer_cast<MidiRegion>(region);
1101 void
1102 MidiTimeAxisView::ensure_step_editor ()
1104 if (!_step_editor) {
1105 _step_editor = new StepEditor (_editor, midi_track(), *this);
1109 void
1110 MidiTimeAxisView::start_step_editing ()
1112 ensure_step_editor ();
1113 _step_editor->start_step_editing ();
1116 void
1117 MidiTimeAxisView::stop_step_editing ()
1119 if (_step_editor) {
1120 _step_editor->stop_step_editing ();