various fixes to MidiRegionView selection handling, key handling, drawing of ghost...
[ardour2.git] / gtk2_ardour / midi_time_axis.cc
blob63f92f71b060444257df4ed5c87cc1c452a6faee
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);
130 if (!gui_property ("note-range-min").empty ()) {
131 midi_view()->apply_note_range (atoi (gui_property ("note-range-min").c_str()), atoi (gui_property ("note-range-max").c_str()), true);
133 midi_view()->NoteRangeChanged.connect (sigc::mem_fun (*this, &MidiTimeAxisView::note_range_changed));
135 ignore_toggle = false;
137 mute_button->set_active (false);
138 solo_button->set_active (false);
140 if (is_midi_track()) {
141 controls_ebox.set_name ("MidiTimeAxisViewControlsBaseUnselected");
142 _note_mode = midi_track()->note_mode();
143 } else { // MIDI bus (which doesn't exist yet..)
144 controls_ebox.set_name ("MidiBusControlsBaseUnselected");
147 /* map current state of the route */
149 processors_changed (RouteProcessorChange ());
151 _route->processors_changed.connect (*this, invalidator (*this), ui_bind (&MidiTimeAxisView::processors_changed, this, _1), gui_context());
153 if (is_track()) {
154 _piano_roll_header = new PianoRollHeader(*midi_view());
156 _piano_roll_header->AddNoteSelection.connect (sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection));
157 _piano_roll_header->ExtendNoteSelection.connect (sigc::mem_fun (*this, &MidiTimeAxisView::extend_note_selection));
158 _piano_roll_header->ToggleNoteSelection.connect (sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection));
160 _range_scroomer = new MidiScroomer(midi_view()->note_range_adjustment);
162 /* Suspend updates of the StreamView during scroomer drags to speed things up */
163 _range_scroomer->DragStarting.connect (sigc::mem_fun (*midi_view(), &MidiStreamView::suspend_updates));
164 _range_scroomer->DragFinishing.connect (sigc::mem_fun (*midi_view(), &MidiStreamView::resume_updates));
166 controls_hbox.pack_start(*_range_scroomer);
167 controls_hbox.pack_start(*_piano_roll_header);
169 controls_ebox.set_name ("MidiTrackControlsBaseUnselected");
170 controls_base_selected_name = "MidiTrackControlsBaseSelected";
171 controls_base_unselected_name = "MidiTrackControlsBaseUnselected";
173 midi_view()->NoteRangeChanged.connect (sigc::mem_fun(*this, &MidiTimeAxisView::update_range));
175 /* ask for notifications of any new RegionViews */
176 _view->RegionViewAdded.connect (sigc::mem_fun(*this, &MidiTimeAxisView::region_view_added));
178 if (!_editor.have_idled()) {
179 /* first idle will do what we need */
180 } else {
181 first_idle ();
185 HBox* midi_controls_hbox = manage(new HBox());
187 MIDI::Name::MidiPatchManager& patch_manager = MIDI::Name::MidiPatchManager::instance();
189 MIDI::Name::MasterDeviceNames::Models::const_iterator m = patch_manager.all_models().begin();
190 for (; m != patch_manager.all_models().end(); ++m) {
191 _model_selector.append_text(m->c_str());
194 _model_selector.signal_changed().connect(sigc::mem_fun(*this, &MidiTimeAxisView::model_changed));
196 _custom_device_mode_selector.signal_changed().connect(
197 sigc::mem_fun(*this, &MidiTimeAxisView::custom_device_mode_changed));
199 // TODO: persist the choice
200 // this initializes the comboboxes and sends out the signal
201 _model_selector.set_active(0);
203 midi_controls_hbox->pack_start(_channel_selector, true, false);
204 if (!patch_manager.all_models().empty()) {
205 _midi_controls_box.pack_start(_model_selector, true, false);
206 _midi_controls_box.pack_start(_custom_device_mode_selector, true, false);
209 _midi_controls_box.pack_start(*midi_controls_hbox, true, true);
211 controls_vbox.pack_start(_midi_controls_box, false, false);
213 // restore channel selector settings
214 _channel_selector.set_channel_mode(midi_track()->get_channel_mode(), midi_track()->get_channel_mask());
215 _channel_selector.mode_changed.connect(
216 sigc::mem_fun(*midi_track(), &MidiTrack::set_channel_mode));
217 _channel_selector.mode_changed.connect(
218 sigc::mem_fun(*this, &MidiTimeAxisView::set_channel_mode));
220 string prop = gui_property ("color-mode");
221 if (!prop.empty()) {
222 _color_mode = ColorMode (string_2_enum(prop, _color_mode));
223 if (_color_mode == ChannelColors) {
224 _channel_selector.set_channel_colors(CanvasNoteEvent::midi_channel_colors);
228 set_color_mode (_color_mode, true, false);
230 prop = gui_property ("note-mode");
231 if (!prop.empty()) {
232 _note_mode = NoteMode (string_2_enum (prop, _note_mode));
233 if (_percussion_mode_item) {
234 _percussion_mode_item->set_active (_note_mode == Percussive);
238 /* Look for any GUI object state nodes that represent automation children that should exist, and create
239 * the children.
242 GUIObjectState& gui_state = gui_object_state ();
243 for (GUIObjectState::StringPropertyMap::const_iterator i = gui_state.begin(); i != gui_state.end(); ++i) {
244 PBD::ID route_id;
245 bool has_parameter;
246 Evoral::Parameter parameter (0, 0, 0);
248 bool const p = AutomationTimeAxisView::parse_state_id (i->first, route_id, has_parameter, parameter);
249 if (p && route_id == _route->id () && has_parameter) {
250 create_automation_child (parameter, string_is_affirmative (gui_object_state().get_string (i->first, X_("visible"))));
255 void
256 MidiTimeAxisView::first_idle ()
258 if (is_track ()) {
259 _view->attach ();
263 MidiTimeAxisView::~MidiTimeAxisView ()
265 delete _piano_roll_header;
266 _piano_roll_header = 0;
268 delete _range_scroomer;
269 _range_scroomer = 0;
271 delete controller_menu;
272 delete _step_editor;
275 void
276 MidiTimeAxisView::enter_internal_edit_mode ()
278 if (midi_view()) {
279 midi_view()->enter_internal_edit_mode ();
283 void
284 MidiTimeAxisView::leave_internal_edit_mode ()
286 if (midi_view()) {
287 midi_view()->leave_internal_edit_mode ();
291 void
292 MidiTimeAxisView::check_step_edit ()
294 ensure_step_editor ();
295 _step_editor->check_step_edit ();
298 void
299 MidiTimeAxisView::model_changed()
301 std::list<std::string> device_modes = MIDI::Name::MidiPatchManager::instance()
302 .custom_device_mode_names_by_model(_model_selector.get_active_text());
304 _custom_device_mode_selector.clear_items();
306 for (std::list<std::string>::const_iterator i = device_modes.begin();
307 i != device_modes.end(); ++i) {
308 cerr << "found custom device mode " << *i << " thread_id: " << pthread_self() << endl;
309 _custom_device_mode_selector.append_text(*i);
312 _custom_device_mode_selector.set_active(0);
315 void MidiTimeAxisView::custom_device_mode_changed()
317 _midi_patch_settings_changed.emit(_model_selector.get_active_text(),
318 _custom_device_mode_selector.get_active_text());
321 MidiStreamView*
322 MidiTimeAxisView::midi_view()
324 return dynamic_cast<MidiStreamView*>(_view);
327 void
328 MidiTimeAxisView::set_height (uint32_t h)
330 RouteTimeAxisView::set_height (h);
332 if (height >= MIDI_CONTROLS_BOX_MIN_HEIGHT) {
333 _midi_controls_box.show_all ();
334 } else {
335 _midi_controls_box.hide();
338 if (height >= KEYBOARD_MIN_HEIGHT) {
339 if (is_track() && _range_scroomer) {
340 _range_scroomer->show();
342 if (is_track() && _piano_roll_header) {
343 _piano_roll_header->show();
345 } else {
346 if (is_track() && _range_scroomer) {
347 _range_scroomer->hide();
349 if (is_track() && _piano_roll_header) {
350 _piano_roll_header->hide();
355 void
356 MidiTimeAxisView::append_extra_display_menu_items ()
358 using namespace Menu_Helpers;
360 MenuList& items = display_menu->items();
362 // Note range
363 Menu *range_menu = manage(new Menu);
364 MenuList& range_items = range_menu->items();
365 range_menu->set_name ("ArdourContextMenu");
367 range_items.push_back (MenuElem (_("Show Full Range"), sigc::bind (
368 sigc::mem_fun(*this, &MidiTimeAxisView::set_note_range),
369 MidiStreamView::FullRange)));
371 range_items.push_back (MenuElem (_("Fit Contents"), sigc::bind (
372 sigc::mem_fun(*this, &MidiTimeAxisView::set_note_range),
373 MidiStreamView::ContentsRange)));
375 items.push_back (MenuElem (_("Note Range"), *range_menu));
376 items.push_back (MenuElem (_("Note Mode"), *build_note_mode_menu()));
378 items.push_back (CheckMenuElem (_("MIDI Thru"), sigc::mem_fun(*this, &MidiTimeAxisView::toggle_midi_thru)));
379 _midi_thru_item = dynamic_cast<CheckMenuItem*>(&items.back());
381 items.push_back (SeparatorElem ());
384 void
385 MidiTimeAxisView::toggle_midi_thru ()
387 if (!_midi_thru_item) {
388 return;
391 bool view_yn = _midi_thru_item->get_active();
392 if (view_yn != midi_track()->midi_thru()) {
393 midi_track()->set_midi_thru (view_yn);
397 void
398 MidiTimeAxisView::build_automation_action_menu (bool for_selection)
400 using namespace Menu_Helpers;
402 /* If we have a controller menu, we need to detach it before
403 RouteTimeAxis::build_automation_action_menu destroys the
404 menu it is attached to. Otherwise GTK destroys
405 controller_menu's gobj, meaning that it can't be reattached
406 below. See bug #3134.
409 if (controller_menu) {
410 detach_menu (*controller_menu);
413 _channel_command_menu_map.clear ();
414 RouteTimeAxisView::build_automation_action_menu (for_selection);
416 MenuList& automation_items = automation_action_menu->items();
418 uint16_t selected_channels = _channel_selector.get_selected_channels();
420 if (selected_channels != 0) {
422 automation_items.push_back (SeparatorElem());
424 /* these 2 MIDI "command" types are semantically more like automation than note data,
425 but they are not MIDI controllers. We give them special status in this menu, since
426 they will not show up in the controller list and anyone who actually knows
427 something about MIDI (!) would not expect to find them there.
430 add_channel_command_menu_item (automation_items, _("Bender"), MidiPitchBenderAutomation, 0);
431 automation_items.back().set_sensitive (!for_selection || _editor.get_selection().tracks.size() == 1);
432 add_channel_command_menu_item (automation_items, _("Pressure"), MidiChannelPressureAutomation, 0);
433 automation_items.back().set_sensitive (!for_selection || _editor.get_selection().tracks.size() == 1);
435 /* now all MIDI controllers. Always offer the possibility that we will rebuild the controllers menu
436 since it might need to be updated after a channel mode change or other change. Also detach it
437 first in case it has been used anywhere else.
440 build_controller_menu ();
442 automation_items.push_back (SeparatorElem());
443 automation_items.push_back (MenuElem (_("Controllers"), *controller_menu));
444 automation_items.back().set_sensitive (!for_selection || _editor.get_selection().tracks.size() == 1);
445 } else {
446 automation_items.push_back (MenuElem (string_compose ("<i>%1</i>", _("No MIDI Channels selected"))));
447 dynamic_cast<Label*> (automation_items.back().get_child())->set_use_markup (true);
452 void
453 MidiTimeAxisView::change_all_channel_tracks_visibility (bool yn, Evoral::Parameter param)
455 uint16_t selected_channels = _channel_selector.get_selected_channels();
457 for (uint8_t chn = 0; chn < 16; chn++) {
458 if (selected_channels & (0x0001 << chn)) {
460 Evoral::Parameter fully_qualified_param (param.type(), chn, param.id());
461 Gtk::CheckMenuItem* menu = automation_child_menu_item (fully_qualified_param);
463 if (menu) {
464 menu->set_active (yn);
470 void
471 MidiTimeAxisView::add_channel_command_menu_item (Menu_Helpers::MenuList& items, const string& label, AutomationType auto_type, uint8_t cmd)
473 using namespace Menu_Helpers;
475 /* count the number of selected channels because we will build a different menu structure if there is more than 1 selected.
478 uint16_t selected_channels = _channel_selector.get_selected_channels();
479 int chn_cnt = 0;
481 for (uint8_t chn = 0; chn < 16; chn++) {
482 if (selected_channels & (0x0001 << chn)) {
483 if (++chn_cnt > 1) {
484 break;
489 if (chn_cnt > 1) {
491 /* multiple channels - create a submenu, with 1 item per channel */
493 Menu* chn_menu = manage (new Menu);
494 MenuList& chn_items (chn_menu->items());
495 Evoral::Parameter param_without_channel (auto_type, 0, cmd);
497 /* add a couple of items to hide/show all of them */
499 chn_items.push_back (MenuElem (_("Hide all channels"),
500 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
501 false, param_without_channel)));
502 chn_items.push_back (MenuElem (_("Show all channels"),
503 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
504 true, param_without_channel)));
506 for (uint8_t chn = 0; chn < 16; chn++) {
507 if (selected_channels & (0x0001 << chn)) {
509 /* for each selected channel, add a menu item for this controller */
511 Evoral::Parameter fully_qualified_param (auto_type, chn, cmd);
512 chn_items.push_back (CheckMenuElem (string_compose (_("Channel %1"), chn+1),
513 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
514 fully_qualified_param)));
516 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
517 bool visible = false;
519 if (track) {
520 if (track->marked_for_display()) {
521 visible = true;
525 CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&chn_items.back());
526 _channel_command_menu_map[fully_qualified_param] = cmi;
527 cmi->set_active (visible);
531 /* now create an item in the parent menu that has the per-channel list as a submenu */
533 items.push_back (MenuElem (label, *chn_menu));
535 } else {
537 /* just one channel - create a single menu item for this command+channel combination*/
539 for (uint8_t chn = 0; chn < 16; chn++) {
540 if (selected_channels & (0x0001 << chn)) {
542 Evoral::Parameter fully_qualified_param (auto_type, chn, cmd);
543 items.push_back (CheckMenuElem (label,
544 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
545 fully_qualified_param)));
547 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
548 bool visible = false;
550 if (track) {
551 if (track->marked_for_display()) {
552 visible = true;
556 CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&items.back());
557 _channel_command_menu_map[fully_qualified_param] = cmi;
558 cmi->set_active (visible);
560 /* one channel only */
561 break;
567 void
568 MidiTimeAxisView::build_controller_menu ()
570 using namespace Menu_Helpers;
572 if (controller_menu) {
573 /* it exists and has not been invalidated by a channel mode change, so just return it */
574 return;
577 controller_menu = new Menu; // explicitly managed by us
578 MenuList& items (controller_menu->items());
580 /* create several "top level" menu items for sets of controllers (16 at a time), and populate each one with a submenu
581 for each controller+channel combination covering the currently selected channels for this track
584 uint16_t selected_channels = _channel_selector.get_selected_channels();
586 /* count the number of selected channels because we will build a different menu structure if there is more than 1 selected.
589 int chn_cnt = 0;
591 for (uint8_t chn = 0; chn < 16; chn++) {
592 if (selected_channels & (0x0001 << chn)) {
593 if (++chn_cnt > 1) {
594 break;
599 /* loop over all 127 MIDI controllers, in groups of 16; except don't offer
600 bank select controllers, as they are handled by the `patch' code */
602 for (int i = 0; i < 127; i += 16) {
604 Menu* ctl_menu = manage (new Menu);
605 MenuList& ctl_items (ctl_menu->items());
608 /* for each controller, consider whether to create a submenu or a single item */
610 for (int ctl = i; ctl < i+16; ++ctl) {
612 if (ctl == MIDI_CTL_MSB_BANK || ctl == MIDI_CTL_LSB_BANK) {
613 continue;
616 if (chn_cnt > 1) {
618 /* multiple channels - create a submenu, with 1 item per channel */
620 Menu* chn_menu = manage (new Menu);
621 MenuList& chn_items (chn_menu->items());
623 /* add a couple of items to hide/show this controller on all channels */
625 Evoral::Parameter param_without_channel (MidiCCAutomation, 0, ctl);
626 chn_items.push_back (MenuElem (_("Hide all channels"),
627 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
628 false, param_without_channel)));
629 chn_items.push_back (MenuElem (_("Show all channels"),
630 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
631 true, param_without_channel)));
633 for (uint8_t chn = 0; chn < 16; chn++) {
634 if (selected_channels & (0x0001 << chn)) {
636 /* for each selected channel, add a menu item for this controller */
638 Evoral::Parameter fully_qualified_param (MidiCCAutomation, chn, ctl);
639 chn_items.push_back (CheckMenuElem (string_compose (_("Channel %1"), chn+1),
640 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
641 fully_qualified_param)));
643 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
644 bool visible = false;
646 if (track) {
647 if (track->marked_for_display()) {
648 visible = true;
652 CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&chn_items.back());
653 _controller_menu_map[fully_qualified_param] = cmi;
654 cmi->set_active (visible);
658 /* add the per-channel menu to the list of controllers, with the name of the controller */
659 ctl_items.push_back (MenuElem (string_compose ("<b>%1</b>: %2", ctl, midi_name (ctl)), *chn_menu));
660 dynamic_cast<Label*> (ctl_items.back().get_child())->set_use_markup (true);
662 } else {
664 /* just one channel - create a single menu item for this ctl+channel combination*/
666 for (uint8_t chn = 0; chn < 16; chn++) {
667 if (selected_channels & (0x0001 << chn)) {
669 Evoral::Parameter fully_qualified_param (MidiCCAutomation, chn, ctl);
670 ctl_items.push_back (
671 CheckMenuElem (
672 string_compose ("<b>%1</b>: %2 [%3]", ctl, midi_name (ctl), int (chn)),
673 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
674 fully_qualified_param)
677 dynamic_cast<Label*> (ctl_items.back().get_child())->set_use_markup (true);
679 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
680 bool visible = false;
682 if (track) {
683 if (track->marked_for_display()) {
684 visible = true;
688 CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&ctl_items.back());
689 _controller_menu_map[fully_qualified_param] = cmi;
690 cmi->set_active (visible);
692 /* one channel only */
693 break;
699 /* add the menu for this block of controllers to the overall controller menu */
701 items.push_back (MenuElem (string_compose (_("Controllers %1-%2"), i, i+15), *ctl_menu));
705 Gtk::Menu*
706 MidiTimeAxisView::build_note_mode_menu()
708 using namespace Menu_Helpers;
710 Menu* mode_menu = manage (new Menu);
711 MenuList& items = mode_menu->items();
712 mode_menu->set_name ("ArdourContextMenu");
714 RadioMenuItem::Group mode_group;
715 items.push_back (RadioMenuElem (mode_group, _("Sustained"),
716 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_mode), Sustained)));
717 _note_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
718 _note_mode_item->set_active(_note_mode == Sustained);
720 items.push_back (RadioMenuElem (mode_group, _("Percussive"),
721 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_mode), Percussive)));
722 _percussion_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
723 _percussion_mode_item->set_active(_note_mode == Percussive);
725 return mode_menu;
728 Gtk::Menu*
729 MidiTimeAxisView::build_color_mode_menu()
731 using namespace Menu_Helpers;
733 Menu* mode_menu = manage (new Menu);
734 MenuList& items = mode_menu->items();
735 mode_menu->set_name ("ArdourContextMenu");
737 RadioMenuItem::Group mode_group;
738 items.push_back (RadioMenuElem (mode_group, _("Meter Colors"),
739 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode),
740 MeterColors, false, true)));
741 _meter_color_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
742 _meter_color_mode_item->set_active(_color_mode == MeterColors);
744 items.push_back (RadioMenuElem (mode_group, _("Channel Colors"),
745 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode),
746 ChannelColors, false, true)));
747 _channel_color_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
748 _channel_color_mode_item->set_active(_color_mode == ChannelColors);
750 items.push_back (RadioMenuElem (mode_group, _("Track Color"),
751 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode),
752 TrackColor, false, true)));
753 _channel_color_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
754 _channel_color_mode_item->set_active(_color_mode == TrackColor);
756 return mode_menu;
759 void
760 MidiTimeAxisView::set_note_mode(NoteMode mode)
762 if (_note_mode != mode || midi_track()->note_mode() != mode) {
763 _note_mode = mode;
764 midi_track()->set_note_mode(mode);
765 set_gui_property ("note-mode", enum_2_string(_note_mode));
766 _view->redisplay_track();
770 void
771 MidiTimeAxisView::set_color_mode (ColorMode mode, bool force, bool redisplay)
773 if (_color_mode == mode && !force) {
774 return;
777 if (mode == ChannelColors) {
778 _channel_selector.set_channel_colors(CanvasNoteEvent::midi_channel_colors);
779 } else {
780 _channel_selector.set_default_channel_color();
783 _color_mode = mode;
784 set_gui_property ("color-mode", enum_2_string(_color_mode));
785 if (redisplay) {
786 _view->redisplay_track();
790 void
791 MidiTimeAxisView::set_note_range(MidiStreamView::VisibleNoteRange range)
793 if (!_ignore_signals)
794 midi_view()->set_note_range(range);
798 void
799 MidiTimeAxisView::update_range()
801 MidiGhostRegion* mgr;
803 for(list<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
804 if ((mgr = dynamic_cast<MidiGhostRegion*>(*i)) != 0) {
805 mgr->update_range();
810 void
811 MidiTimeAxisView::show_all_automation (bool apply_to_selection)
813 if (apply_to_selection) {
814 _editor.get_selection().tracks.foreach_midi_time_axis (boost::bind (&MidiTimeAxisView::show_all_automation, _1, false));
815 } else {
816 if (midi_track()) {
817 const set<Evoral::Parameter> params = midi_track()->midi_playlist()->contained_automation();
819 for (set<Evoral::Parameter>::const_iterator i = params.begin(); i != params.end(); ++i) {
820 create_automation_child(*i, true);
824 RouteTimeAxisView::show_all_automation ();
828 void
829 MidiTimeAxisView::show_existing_automation (bool apply_to_selection)
831 if (apply_to_selection) {
832 _editor.get_selection().tracks.foreach_midi_time_axis (boost::bind (&MidiTimeAxisView::show_existing_automation, _1, false));
833 } else {
834 if (midi_track()) {
835 const set<Evoral::Parameter> params = midi_track()->midi_playlist()->contained_automation();
837 for (set<Evoral::Parameter>::const_iterator i = params.begin(); i != params.end(); ++i) {
838 create_automation_child (*i, true);
842 RouteTimeAxisView::show_existing_automation ();
846 /** Create an automation track for the given parameter (pitch bend, channel pressure).
848 void
849 MidiTimeAxisView::create_automation_child (const Evoral::Parameter& param, bool show)
851 if (param.type() == NullAutomation) {
852 cerr << "WARNING: Attempt to create NullAutomation child, ignoring" << endl;
853 return;
856 AutomationTracks::iterator existing = _automation_tracks.find (param);
858 if (existing != _automation_tracks.end()) {
860 /* automation track created because we had existing data for
861 * the processor, but visibility may need to be controlled
862 * since it will have been set visible by default.
865 cerr << "show existing auto track: " << show << " noredraw " << no_redraw << endl;
867 if (existing->second->set_marked_for_display (show) && !no_redraw) {
868 request_redraw ();
871 return;
874 boost::shared_ptr<AutomationTimeAxisView> track;
876 switch (param.type()) {
878 case GainAutomation:
879 create_gain_automation_child (param, show);
880 break;
882 case PluginAutomation:
883 /* handled elsewhere */
884 break;
886 case MidiCCAutomation:
887 case MidiPgmChangeAutomation:
888 case MidiPitchBenderAutomation:
889 case MidiChannelPressureAutomation:
890 case MidiSystemExclusiveAutomation:
891 /* These controllers are region "automation" - they are owned
892 * by regions (and their MidiModels), not by the track. As a
893 * result we do not create an AutomationList/Line for the track
894 * ... except here we are doing something!! XXX
897 track.reset (new AutomationTimeAxisView (
898 _session,
899 _route,
900 boost::shared_ptr<Automatable> (),
901 boost::shared_ptr<AutomationControl> (),
902 param,
903 _editor,
904 *this,
905 true,
906 parent_canvas,
907 _route->describe_parameter(param)
910 if (_view) {
911 _view->foreach_regionview (sigc::mem_fun (*track.get(), &TimeAxisView::add_ghost));
914 add_automation_child (param, track, show);
915 break;
917 default:
918 error << "MidiTimeAxisView: unknown automation child " << EventTypeMap::instance().to_symbol(param) << endmsg;
922 void
923 MidiTimeAxisView::route_active_changed ()
925 RouteUI::route_active_changed ();
927 if (is_track()) {
928 if (_route->active()) {
929 controls_ebox.set_name ("MidiTrackControlsBaseUnselected");
930 controls_base_selected_name = "MidiTrackControlsBaseSelected";
931 controls_base_unselected_name = "MidiTrackControlsBaseUnselected";
932 } else {
933 controls_ebox.set_name ("MidiTrackControlsBaseInactiveUnselected");
934 controls_base_selected_name = "MidiTrackControlsBaseInactiveSelected";
935 controls_base_unselected_name = "MidiTrackControlsBaseInactiveUnselected";
937 } else {
939 throw; // wha?
941 if (_route->active()) {
942 controls_ebox.set_name ("BusControlsBaseUnselected");
943 controls_base_selected_name = "BusControlsBaseSelected";
944 controls_base_unselected_name = "BusControlsBaseUnselected";
945 } else {
946 controls_ebox.set_name ("BusControlsBaseInactiveUnselected");
947 controls_base_selected_name = "BusControlsBaseInactiveSelected";
948 controls_base_unselected_name = "BusControlsBaseInactiveUnselected";
955 void
956 MidiTimeAxisView::add_note_selection (uint8_t note)
958 if (!_editor.internal_editing()) {
959 return;
962 uint16_t chn_mask = _channel_selector.get_selected_channels();
964 if (_view->num_selected_regionviews() == 0) {
965 _view->foreach_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection_region_view), note, chn_mask));
966 } else {
967 _view->foreach_selected_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection_region_view), note, chn_mask));
971 void
972 MidiTimeAxisView::extend_note_selection (uint8_t note)
974 if (!_editor.internal_editing()) {
975 return;
978 uint16_t chn_mask = _channel_selector.get_selected_channels();
980 if (_view->num_selected_regionviews() == 0) {
981 _view->foreach_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::extend_note_selection_region_view), note, chn_mask));
982 } else {
983 _view->foreach_selected_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::extend_note_selection_region_view), note, chn_mask));
987 void
988 MidiTimeAxisView::toggle_note_selection (uint8_t note)
990 if (!_editor.internal_editing()) {
991 return;
994 uint16_t chn_mask = _channel_selector.get_selected_channels();
996 if (_view->num_selected_regionviews() == 0) {
997 _view->foreach_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection_region_view), note, chn_mask));
998 } else {
999 _view->foreach_selected_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection_region_view), note, chn_mask));
1003 void
1004 MidiTimeAxisView::add_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1006 dynamic_cast<MidiRegionView*>(rv)->select_matching_notes (note, chn_mask, false, false);
1009 void
1010 MidiTimeAxisView::extend_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1012 dynamic_cast<MidiRegionView*>(rv)->select_matching_notes (note, chn_mask, true, true);
1015 void
1016 MidiTimeAxisView::toggle_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1018 dynamic_cast<MidiRegionView*>(rv)->toggle_matching_notes (note, chn_mask);
1021 void
1022 MidiTimeAxisView::set_channel_mode (ChannelMode, uint16_t)
1024 /* hide all automation tracks that use the wrong channel(s) and show all those that use
1025 the right ones.
1028 uint16_t selected_channels = _channel_selector.get_selected_channels();
1029 bool changed = false;
1031 no_redraw = true;
1033 for (uint32_t ctl = 0; ctl < 127; ++ctl) {
1035 for (uint32_t chn = 0; chn < 16; ++chn) {
1036 Evoral::Parameter fully_qualified_param (MidiCCAutomation, chn, ctl);
1037 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
1039 if (!track) {
1040 continue;
1043 if ((selected_channels & (0x0001 << chn)) == 0) {
1044 /* channel not in use. hiding it will trigger RouteTimeAxisView::automation_track_hidden()
1045 which will cause a redraw. We don't want one per channel, so block that with no_redraw.
1047 changed = track->set_marked_for_display (false) || changed;
1048 } else {
1049 changed = track->set_marked_for_display (true) || changed;
1054 no_redraw = false;
1056 /* TODO: Bender, Pressure */
1058 /* invalidate the controller menu, so that we rebuild it next time */
1059 _controller_menu_map.clear ();
1060 delete controller_menu;
1061 controller_menu = 0;
1063 if (changed) {
1064 request_redraw ();
1068 Gtk::CheckMenuItem*
1069 MidiTimeAxisView::automation_child_menu_item (Evoral::Parameter param)
1071 Gtk::CheckMenuItem* m = RouteTimeAxisView::automation_child_menu_item (param);
1072 if (m) {
1073 return m;
1076 ParameterMenuMap::iterator i = _controller_menu_map.find (param);
1077 if (i != _controller_menu_map.end()) {
1078 return i->second;
1081 i = _channel_command_menu_map.find (param);
1082 if (i != _channel_command_menu_map.end()) {
1083 return i->second;
1086 return 0;
1089 boost::shared_ptr<MidiRegion>
1090 MidiTimeAxisView::add_region (framepos_t pos, framecnt_t length, bool commit)
1092 Editor* real_editor = dynamic_cast<Editor*> (&_editor);
1094 real_editor->begin_reversible_command (Operations::create_region);
1095 playlist()->clear_changes ();
1097 real_editor->snap_to (pos, 0);
1099 boost::shared_ptr<Source> src = _session->create_midi_source_for_session (view()->trackview().track().get(),
1100 view()->trackview().track()->name());
1101 PropertyList plist;
1103 plist.add (ARDOUR::Properties::start, 0);
1104 plist.add (ARDOUR::Properties::length, length);
1105 plist.add (ARDOUR::Properties::name, PBD::basename_nosuffix(src->name()));
1107 boost::shared_ptr<Region> region = (RegionFactory::create (src, plist));
1109 playlist()->add_region (region, pos);
1110 _session->add_command (new StatefulDiffCommand (playlist()));
1112 if (commit) {
1113 real_editor->commit_reversible_command ();
1116 return boost::dynamic_pointer_cast<MidiRegion>(region);
1119 void
1120 MidiTimeAxisView::ensure_step_editor ()
1122 if (!_step_editor) {
1123 _step_editor = new StepEditor (_editor, midi_track(), *this);
1127 void
1128 MidiTimeAxisView::start_step_editing ()
1130 ensure_step_editor ();
1131 _step_editor->start_step_editing ();
1134 void
1135 MidiTimeAxisView::stop_step_editing ()
1137 if (_step_editor) {
1138 _step_editor->stop_step_editing ();
1143 /** @return channel (counted from 0) to add an event to, based on the current setting
1144 * of the channel selector.
1146 uint8_t
1147 MidiTimeAxisView::get_channel_for_add () const
1149 uint16_t const chn_mask = _channel_selector.get_selected_channels ();
1150 int chn_cnt = 0;
1151 uint8_t channel = 0;
1153 /* pick the highest selected channel, unless all channels are selected,
1154 which is interpreted to mean channel 1 (zero)
1157 for (uint16_t i = 0; i < 16; ++i) {
1158 if (chn_mask & (1<<i)) {
1159 channel = i;
1160 chn_cnt++;
1164 if (chn_cnt == 16) {
1165 channel = 0;
1168 return channel;
1171 void
1172 MidiTimeAxisView::note_range_changed ()
1174 set_gui_property ("note-range-min", (int) midi_view()->lowest_note ());
1175 set_gui_property ("note-range-max", (int) midi_view()->highest_note ());