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.
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"
68 #include "ghostregion.h"
69 #include "gui_thread.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"
81 #include "region_view.h"
82 #include "rgb_macros.h"
83 #include "selection.h"
84 #include "step_editor.h"
85 #include "simplerect.h"
88 #include "ardour/midi_track.h"
92 using namespace ARDOUR
;
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)
108 , _piano_roll_header(0)
109 , _note_mode(Sustained
)
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 , controller_menu (0)
121 subplugin_menu
.set_name ("ArdourContextMenu");
123 _view
= new MidiStreamView (*this);
125 ignore_toggle
= false;
127 mute_button
->set_active (false);
128 solo_button
->set_active (false);
130 if (is_midi_track()) {
131 controls_ebox
.set_name ("MidiTimeAxisViewControlsBaseUnselected");
132 _note_mode
= midi_track()->note_mode();
133 } else { // MIDI bus (which doesn't exist yet..)
134 controls_ebox
.set_name ("MidiBusControlsBaseUnselected");
137 /* map current state of the route */
139 processors_changed (RouteProcessorChange ());
143 set_state (*xml_node
, Stateful::loading_state_version
);
145 _route
->processors_changed
.connect (*this, invalidator (*this), ui_bind (&MidiTimeAxisView::processors_changed
, this, _1
), gui_context());
148 _piano_roll_header
= new PianoRollHeader(*midi_view());
150 _piano_roll_header
->AddNoteSelection
.connect (sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection
));
151 _piano_roll_header
->ExtendNoteSelection
.connect (sigc::mem_fun (*this, &MidiTimeAxisView::extend_note_selection
));
152 _piano_roll_header
->ToggleNoteSelection
.connect (sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection
));
154 _range_scroomer
= new MidiScroomer(midi_view()->note_range_adjustment
);
156 /* Suspend updates of the StreamView during scroomer drags to speed things up */
157 _range_scroomer
->DragStarting
.connect (sigc::mem_fun (*midi_view(), &MidiStreamView::suspend_updates
));
158 _range_scroomer
->DragFinishing
.connect (sigc::mem_fun (*midi_view(), &MidiStreamView::resume_updates
));
160 controls_hbox
.pack_start(*_range_scroomer
);
161 controls_hbox
.pack_start(*_piano_roll_header
);
163 controls_ebox
.set_name ("MidiTrackControlsBaseUnselected");
164 controls_base_selected_name
= "MidiTrackControlsBaseSelected";
165 controls_base_unselected_name
= "MidiTrackControlsBaseUnselected";
167 midi_view()->NoteRangeChanged
.connect (sigc::mem_fun(*this, &MidiTimeAxisView::update_range
));
169 /* ask for notifications of any new RegionViews */
170 _view
->RegionViewAdded
.connect (sigc::mem_fun(*this, &MidiTimeAxisView::region_view_added
));
172 if (!_editor
.have_idled()) {
173 /* first idle will do what we need */
179 HBox
* midi_controls_hbox
= manage(new HBox());
181 MIDI::Name::MidiPatchManager
& patch_manager
= MIDI::Name::MidiPatchManager::instance();
183 MIDI::Name::MasterDeviceNames::Models::const_iterator m
= patch_manager
.all_models().begin();
184 for (; m
!= patch_manager
.all_models().end(); ++m
) {
185 _model_selector
.append_text(m
->c_str());
188 _model_selector
.signal_changed().connect(sigc::mem_fun(*this, &MidiTimeAxisView::model_changed
));
190 _custom_device_mode_selector
.signal_changed().connect(
191 sigc::mem_fun(*this, &MidiTimeAxisView::custom_device_mode_changed
));
193 // TODO: persist the choice
194 // this initializes the comboboxes and sends out the signal
195 _model_selector
.set_active(0);
197 midi_controls_hbox
->pack_start(_channel_selector
, true, false);
198 if (!patch_manager
.all_models().empty()) {
199 _midi_controls_box
.pack_start(_model_selector
, true, false);
200 _midi_controls_box
.pack_start(_custom_device_mode_selector
, true, false);
203 _midi_controls_box
.pack_start(*midi_controls_hbox
, true, true);
205 controls_vbox
.pack_start(_midi_controls_box
, false, false);
207 // restore channel selector settings
208 _channel_selector
.set_channel_mode(midi_track()->get_channel_mode(), midi_track()->get_channel_mask());
209 _channel_selector
.mode_changed
.connect(
210 sigc::mem_fun(*midi_track(), &MidiTrack::set_channel_mode
));
211 _channel_selector
.mode_changed
.connect(
212 sigc::mem_fun(*this, &MidiTimeAxisView::set_channel_mode
));
215 if ((prop
= xml_node
->property ("color-mode")) != 0) {
216 _color_mode
= ColorMode (string_2_enum(prop
->value(), _color_mode
));
217 if (_color_mode
== ChannelColors
) {
218 _channel_selector
.set_channel_colors(CanvasNoteEvent::midi_channel_colors
);
222 if ((prop
= xml_node
->property ("note-mode")) != 0) {
223 _note_mode
= NoteMode (string_2_enum(prop
->value(), _note_mode
));
224 if (_percussion_mode_item
) {
225 _percussion_mode_item
->set_active (_note_mode
== Percussive
);
229 set_color_mode (_color_mode
, true, false);
233 MidiTimeAxisView::first_idle ()
240 MidiTimeAxisView::~MidiTimeAxisView ()
242 delete _piano_roll_header
;
243 _piano_roll_header
= 0;
245 delete _range_scroomer
;
248 delete controller_menu
;
253 MidiTimeAxisView::enter_internal_edit_mode ()
256 midi_view()->enter_internal_edit_mode ();
261 MidiTimeAxisView::leave_internal_edit_mode ()
264 midi_view()->leave_internal_edit_mode ();
269 MidiTimeAxisView::check_step_edit ()
271 ensure_step_editor ();
272 _step_editor
->check_step_edit ();
276 MidiTimeAxisView::model_changed()
278 std::list
<std::string
> device_modes
= MIDI::Name::MidiPatchManager::instance()
279 .custom_device_mode_names_by_model(_model_selector
.get_active_text());
281 _custom_device_mode_selector
.clear_items();
283 for (std::list
<std::string
>::const_iterator i
= device_modes
.begin();
284 i
!= device_modes
.end(); ++i
) {
285 cerr
<< "found custom device mode " << *i
<< " thread_id: " << pthread_self() << endl
;
286 _custom_device_mode_selector
.append_text(*i
);
289 _custom_device_mode_selector
.set_active(0);
292 void MidiTimeAxisView::custom_device_mode_changed()
294 _midi_patch_settings_changed
.emit(_model_selector
.get_active_text(),
295 _custom_device_mode_selector
.get_active_text());
299 MidiTimeAxisView::midi_view()
301 return dynamic_cast<MidiStreamView
*>(_view
);
305 MidiTimeAxisView::show_at (double y
, int& nth
, Gtk::VBox
*parent
)
308 xml_node
->add_property ("shown-editor", "yes");
310 guint32 ret
= TimeAxisView::show_at (y
, nth
, parent
);
315 MidiTimeAxisView::hide ()
318 xml_node
->add_property ("shown-editor", "no");
320 TimeAxisView::hide ();
324 MidiTimeAxisView::set_height (uint32_t h
)
326 RouteTimeAxisView::set_height (h
);
328 if (height
>= MIDI_CONTROLS_BOX_MIN_HEIGHT
) {
329 _midi_controls_box
.show_all ();
331 _midi_controls_box
.hide();
334 if (height
>= KEYBOARD_MIN_HEIGHT
) {
335 if (is_track() && _range_scroomer
)
336 _range_scroomer
->show();
337 if (is_track() && _piano_roll_header
)
338 _piano_roll_header
->show();
340 if (is_track() && _range_scroomer
)
341 _range_scroomer
->hide();
342 if (is_track() && _piano_roll_header
)
343 _piano_roll_header
->hide();
348 MidiTimeAxisView::append_extra_display_menu_items ()
350 using namespace Menu_Helpers
;
352 MenuList
& items
= display_menu
->items();
355 Menu
*range_menu
= manage(new Menu
);
356 MenuList
& range_items
= range_menu
->items();
357 range_menu
->set_name ("ArdourContextMenu");
359 range_items
.push_back (MenuElem (_("Show Full Range"), sigc::bind (
360 sigc::mem_fun(*this, &MidiTimeAxisView::set_note_range
),
361 MidiStreamView::FullRange
)));
363 range_items
.push_back (MenuElem (_("Fit Contents"), sigc::bind (
364 sigc::mem_fun(*this, &MidiTimeAxisView::set_note_range
),
365 MidiStreamView::ContentsRange
)));
367 items
.push_back (MenuElem (_("Note Range"), *range_menu
));
368 items
.push_back (MenuElem (_("Note Mode"), *build_note_mode_menu()));
370 items
.push_back (CheckMenuElem (_("MIDI Thru"), sigc::mem_fun(*this, &MidiTimeAxisView::toggle_midi_thru
)));
371 _midi_thru_item
= dynamic_cast<CheckMenuItem
*>(&items
.back());
373 items
.push_back (SeparatorElem ());
377 MidiTimeAxisView::toggle_midi_thru ()
379 if (!_midi_thru_item
) {
383 bool view_yn
= _midi_thru_item
->get_active();
384 if (view_yn
!= midi_track()->midi_thru()) {
385 midi_track()->set_midi_thru (view_yn
);
390 MidiTimeAxisView::build_automation_action_menu (bool for_selection
)
392 using namespace Menu_Helpers
;
394 /* If we have a controller menu, we need to detach it before
395 RouteTimeAxis::build_automation_action_menu destroys the
396 menu it is attached to. Otherwise GTK destroys
397 controller_menu's gobj, meaning that it can't be reattached
398 below. See bug #3134.
401 if (controller_menu
) {
402 detach_menu (*controller_menu
);
405 _channel_command_menu_map
.clear ();
406 RouteTimeAxisView::build_automation_action_menu (for_selection
);
408 MenuList
& automation_items
= automation_action_menu
->items();
410 uint16_t selected_channels
= _channel_selector
.get_selected_channels();
412 if (selected_channels
!= 0) {
414 automation_items
.push_back (SeparatorElem());
416 /* these 2 MIDI "command" types are semantically more like automation than note data,
417 but they are not MIDI controllers. We give them special status in this menu, since
418 they will not show up in the controller list and anyone who actually knows
419 something about MIDI (!) would not expect to find them there.
422 add_channel_command_menu_item (automation_items
, _("Bender"), MidiPitchBenderAutomation
, 0);
423 automation_items
.back().set_sensitive (!for_selection
|| _editor
.get_selection().tracks
.size() == 1);
424 add_channel_command_menu_item (automation_items
, _("Pressure"), MidiChannelPressureAutomation
, 0);
425 automation_items
.back().set_sensitive (!for_selection
|| _editor
.get_selection().tracks
.size() == 1);
427 /* now all MIDI controllers. Always offer the possibility that we will rebuild the controllers menu
428 since it might need to be updated after a channel mode change or other change. Also detach it
429 first in case it has been used anywhere else.
432 build_controller_menu ();
434 automation_items
.push_back (SeparatorElem());
435 automation_items
.push_back (MenuElem (_("Controllers"), *controller_menu
));
436 automation_items
.back().set_sensitive (!for_selection
|| _editor
.get_selection().tracks
.size() == 1);
438 automation_items
.push_back (MenuElem (string_compose ("<i>%1</i>", _("No MIDI Channels selected"))));
439 dynamic_cast<Label
*> (automation_items
.back().get_child())->set_use_markup (true);
445 MidiTimeAxisView::change_all_channel_tracks_visibility (bool yn
, Evoral::Parameter param
)
447 uint16_t selected_channels
= _channel_selector
.get_selected_channels();
449 for (uint8_t chn
= 0; chn
< 16; chn
++) {
450 if (selected_channels
& (0x0001 << chn
)) {
452 Evoral::Parameter
fully_qualified_param (param
.type(), chn
, param
.id());
453 Gtk::CheckMenuItem
* menu
= automation_child_menu_item (fully_qualified_param
);
456 menu
->set_active (yn
);
463 MidiTimeAxisView::add_channel_command_menu_item (Menu_Helpers::MenuList
& items
, const string
& label
, AutomationType auto_type
, uint8_t cmd
)
465 using namespace Menu_Helpers
;
467 /* count the number of selected channels because we will build a different menu structure if there is more than 1 selected.
470 uint16_t selected_channels
= _channel_selector
.get_selected_channels();
473 for (uint8_t chn
= 0; chn
< 16; chn
++) {
474 if (selected_channels
& (0x0001 << chn
)) {
483 /* multiple channels - create a submenu, with 1 item per channel */
485 Menu
* chn_menu
= manage (new Menu
);
486 MenuList
& chn_items (chn_menu
->items());
487 Evoral::Parameter
param_without_channel (auto_type
, 0, cmd
);
489 /* add a couple of items to hide/show all of them */
491 chn_items
.push_back (MenuElem (_("Hide all channels"),
492 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility
),
493 false, param_without_channel
)));
494 chn_items
.push_back (MenuElem (_("Show all channels"),
495 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility
),
496 true, param_without_channel
)));
498 for (uint8_t chn
= 0; chn
< 16; chn
++) {
499 if (selected_channels
& (0x0001 << chn
)) {
501 /* for each selected channel, add a menu item for this controller */
503 Evoral::Parameter
fully_qualified_param (auto_type
, chn
, cmd
);
504 chn_items
.push_back (CheckMenuElem (string_compose (_("Channel %1"), chn
+1),
505 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track
),
506 fully_qualified_param
)));
508 boost::shared_ptr
<AutomationTimeAxisView
> track
= automation_child (fully_qualified_param
);
509 bool visible
= false;
512 if (track
->marked_for_display()) {
517 CheckMenuItem
* cmi
= static_cast<CheckMenuItem
*>(&chn_items
.back());
518 _channel_command_menu_map
[fully_qualified_param
] = cmi
;
519 cmi
->set_active (visible
);
523 /* now create an item in the parent menu that has the per-channel list as a submenu */
525 items
.push_back (MenuElem (label
, *chn_menu
));
529 /* just one channel - create a single menu item for this command+channel combination*/
531 for (uint8_t chn
= 0; chn
< 16; chn
++) {
532 if (selected_channels
& (0x0001 << chn
)) {
534 Evoral::Parameter
fully_qualified_param (auto_type
, chn
, cmd
);
535 items
.push_back (CheckMenuElem (label
,
536 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track
),
537 fully_qualified_param
)));
539 boost::shared_ptr
<AutomationTimeAxisView
> track
= automation_child (fully_qualified_param
);
540 bool visible
= false;
543 if (track
->marked_for_display()) {
548 CheckMenuItem
* cmi
= static_cast<CheckMenuItem
*>(&items
.back());
549 _channel_command_menu_map
[fully_qualified_param
] = cmi
;
550 cmi
->set_active (visible
);
552 /* one channel only */
560 MidiTimeAxisView::build_controller_menu ()
562 using namespace Menu_Helpers
;
564 if (controller_menu
) {
565 /* it exists and has not been invalidated by a channel mode change, so just return it */
569 controller_menu
= new Menu
; // explicitly managed by us
570 MenuList
& items (controller_menu
->items());
572 /* create several "top level" menu items for sets of controllers (16 at a time), and populate each one with a submenu
573 for each controller+channel combination covering the currently selected channels for this track
576 uint16_t selected_channels
= _channel_selector
.get_selected_channels();
578 /* count the number of selected channels because we will build a different menu structure if there is more than 1 selected.
583 for (uint8_t chn
= 0; chn
< 16; chn
++) {
584 if (selected_channels
& (0x0001 << chn
)) {
591 /* loop over all 127 MIDI controllers, in groups of 16; except don't offer
592 bank select controllers, as they are handled by the `patch' code */
594 for (int i
= 0; i
< 127; i
+= 16) {
596 Menu
* ctl_menu
= manage (new Menu
);
597 MenuList
& ctl_items (ctl_menu
->items());
600 /* for each controller, consider whether to create a submenu or a single item */
602 for (int ctl
= i
; ctl
< i
+16; ++ctl
) {
604 if (ctl
== MIDI_CTL_MSB_BANK
|| ctl
== MIDI_CTL_LSB_BANK
) {
610 /* multiple channels - create a submenu, with 1 item per channel */
612 Menu
* chn_menu
= manage (new Menu
);
613 MenuList
& chn_items (chn_menu
->items());
615 /* add a couple of items to hide/show this controller on all channels */
617 Evoral::Parameter
param_without_channel (MidiCCAutomation
, 0, ctl
);
618 chn_items
.push_back (MenuElem (_("Hide all channels"),
619 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility
),
620 false, param_without_channel
)));
621 chn_items
.push_back (MenuElem (_("Show all channels"),
622 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility
),
623 true, param_without_channel
)));
625 for (uint8_t chn
= 0; chn
< 16; chn
++) {
626 if (selected_channels
& (0x0001 << chn
)) {
628 /* for each selected channel, add a menu item for this controller */
630 Evoral::Parameter
fully_qualified_param (MidiCCAutomation
, chn
, ctl
);
631 chn_items
.push_back (CheckMenuElem (string_compose (_("Channel %1"), chn
+1),
632 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track
),
633 fully_qualified_param
)));
635 boost::shared_ptr
<AutomationTimeAxisView
> track
= automation_child (fully_qualified_param
);
636 bool visible
= false;
639 if (track
->marked_for_display()) {
644 CheckMenuItem
* cmi
= static_cast<CheckMenuItem
*>(&chn_items
.back());
645 _controller_menu_map
[fully_qualified_param
] = cmi
;
646 cmi
->set_active (visible
);
650 /* add the per-channel menu to the list of controllers, with the name of the controller */
651 ctl_items
.push_back (MenuElem (string_compose ("<b>%1</b>: %2", ctl
, midi_name (ctl
)), *chn_menu
));
652 dynamic_cast<Label
*> (ctl_items
.back().get_child())->set_use_markup (true);
656 /* just one channel - create a single menu item for this ctl+channel combination*/
658 for (uint8_t chn
= 0; chn
< 16; chn
++) {
659 if (selected_channels
& (0x0001 << chn
)) {
661 Evoral::Parameter
fully_qualified_param (MidiCCAutomation
, chn
, ctl
);
662 ctl_items
.push_back (
664 string_compose ("<b>%1</b>: %2 [%3]", ctl
, midi_name (ctl
), int (chn
)),
665 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track
),
666 fully_qualified_param
)
669 dynamic_cast<Label
*> (ctl_items
.back().get_child())->set_use_markup (true);
671 boost::shared_ptr
<AutomationTimeAxisView
> track
= automation_child (fully_qualified_param
);
672 bool visible
= false;
675 if (track
->marked_for_display()) {
680 CheckMenuItem
* cmi
= static_cast<CheckMenuItem
*>(&ctl_items
.back());
681 _controller_menu_map
[fully_qualified_param
] = cmi
;
682 cmi
->set_active (visible
);
684 /* one channel only */
691 /* add the menu for this block of controllers to the overall controller menu */
693 items
.push_back (MenuElem (string_compose (_("Controllers %1-%2"), i
, i
+15), *ctl_menu
));
698 MidiTimeAxisView::build_note_mode_menu()
700 using namespace Menu_Helpers
;
702 Menu
* mode_menu
= manage (new Menu
);
703 MenuList
& items
= mode_menu
->items();
704 mode_menu
->set_name ("ArdourContextMenu");
706 RadioMenuItem::Group mode_group
;
707 items
.push_back (RadioMenuElem (mode_group
, _("Sustained"),
708 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_mode
), Sustained
)));
709 _note_mode_item
= dynamic_cast<RadioMenuItem
*>(&items
.back());
710 _note_mode_item
->set_active(_note_mode
== Sustained
);
712 items
.push_back (RadioMenuElem (mode_group
, _("Percussive"),
713 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_mode
), Percussive
)));
714 _percussion_mode_item
= dynamic_cast<RadioMenuItem
*>(&items
.back());
715 _percussion_mode_item
->set_active(_note_mode
== Percussive
);
721 MidiTimeAxisView::build_color_mode_menu()
723 using namespace Menu_Helpers
;
725 Menu
* mode_menu
= manage (new Menu
);
726 MenuList
& items
= mode_menu
->items();
727 mode_menu
->set_name ("ArdourContextMenu");
729 RadioMenuItem::Group mode_group
;
730 items
.push_back (RadioMenuElem (mode_group
, _("Meter Colors"),
731 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode
),
732 MeterColors
, false, true)));
733 _meter_color_mode_item
= dynamic_cast<RadioMenuItem
*>(&items
.back());
734 _meter_color_mode_item
->set_active(_color_mode
== MeterColors
);
736 items
.push_back (RadioMenuElem (mode_group
, _("Channel Colors"),
737 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode
),
738 ChannelColors
, false, true)));
739 _channel_color_mode_item
= dynamic_cast<RadioMenuItem
*>(&items
.back());
740 _channel_color_mode_item
->set_active(_color_mode
== ChannelColors
);
742 items
.push_back (RadioMenuElem (mode_group
, _("Track Color"),
743 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode
),
744 TrackColor
, false, true)));
745 _channel_color_mode_item
= dynamic_cast<RadioMenuItem
*>(&items
.back());
746 _channel_color_mode_item
->set_active(_color_mode
== TrackColor
);
752 MidiTimeAxisView::set_note_mode(NoteMode mode
)
754 if (_note_mode
!= mode
|| midi_track()->note_mode() != mode
) {
756 midi_track()->set_note_mode(mode
);
757 xml_node
->add_property ("note-mode", enum_2_string(_note_mode
));
758 _view
->redisplay_track();
763 MidiTimeAxisView::set_color_mode (ColorMode mode
, bool force
, bool redisplay
)
765 if (_color_mode
== mode
&& !force
) {
769 if (mode
== ChannelColors
) {
770 _channel_selector
.set_channel_colors(CanvasNoteEvent::midi_channel_colors
);
772 _channel_selector
.set_default_channel_color();
776 xml_node
->add_property ("color-mode", enum_2_string(_color_mode
));
778 _view
->redisplay_track();
783 MidiTimeAxisView::set_note_range(MidiStreamView::VisibleNoteRange range
)
785 if (!_ignore_signals
)
786 midi_view()->set_note_range(range
);
791 MidiTimeAxisView::update_range()
793 MidiGhostRegion
* mgr
;
795 for(list
<GhostRegion
*>::iterator i
= ghosts
.begin(); i
!= ghosts
.end(); ++i
) {
796 if ((mgr
= dynamic_cast<MidiGhostRegion
*>(*i
)) != 0) {
803 MidiTimeAxisView::show_all_automation (bool apply_to_selection
)
805 if (apply_to_selection
) {
806 _editor
.get_selection().tracks
.foreach_midi_time_axis (boost::bind (&MidiTimeAxisView::show_all_automation
, _1
, false));
809 const set
<Evoral::Parameter
> params
= midi_track()->midi_playlist()->contained_automation();
811 for (set
<Evoral::Parameter
>::const_iterator i
= params
.begin(); i
!= params
.end(); ++i
) {
812 create_automation_child(*i
, true);
816 RouteTimeAxisView::show_all_automation ();
821 MidiTimeAxisView::show_existing_automation (bool apply_to_selection
)
823 if (apply_to_selection
) {
824 _editor
.get_selection().tracks
.foreach_midi_time_axis (boost::bind (&MidiTimeAxisView::show_existing_automation
, _1
, false));
827 const set
<Evoral::Parameter
> params
= midi_track()->midi_playlist()->contained_automation();
829 for (set
<Evoral::Parameter
>::const_iterator i
= params
.begin(); i
!= params
.end(); ++i
) {
830 create_automation_child(*i
, true);
834 RouteTimeAxisView::show_existing_automation ();
838 /** Create an automation track for the given parameter (pitch bend, channel pressure).
841 MidiTimeAxisView::create_automation_child (const Evoral::Parameter
& param
, bool show
)
843 if (param
.type() == NullAutomation
) {
844 cerr
<< "WARNING: Attempt to create NullAutomation child, ignoring" << endl
;
848 AutomationTracks::iterator existing
= _automation_tracks
.find (param
);
849 if (existing
!= _automation_tracks
.end()) {
853 if (param
.type() == GainAutomation
) {
854 create_gain_automation_child (param
, show
);
857 /* These controllers are region "automation", so we do not create
858 * an AutomationList/Line for the track */
860 boost::shared_ptr
<AutomationTimeAxisView
> track (
861 new AutomationTimeAxisView (
864 boost::shared_ptr
<Automatable
> (),
865 boost::shared_ptr
<AutomationControl
> (),
871 _route
->describe_parameter(param
)
876 _view
->foreach_regionview (sigc::mem_fun (*track
.get(), &TimeAxisView::add_ghost
));
879 add_automation_child (param
, track
, show
);
885 MidiTimeAxisView::route_active_changed ()
887 RouteUI::route_active_changed ();
890 if (_route
->active()) {
891 controls_ebox
.set_name ("MidiTrackControlsBaseUnselected");
892 controls_base_selected_name
= "MidiTrackControlsBaseSelected";
893 controls_base_unselected_name
= "MidiTrackControlsBaseUnselected";
895 controls_ebox
.set_name ("MidiTrackControlsBaseInactiveUnselected");
896 controls_base_selected_name
= "MidiTrackControlsBaseInactiveSelected";
897 controls_base_unselected_name
= "MidiTrackControlsBaseInactiveUnselected";
903 if (_route
->active()) {
904 controls_ebox
.set_name ("BusControlsBaseUnselected");
905 controls_base_selected_name
= "BusControlsBaseSelected";
906 controls_base_unselected_name
= "BusControlsBaseUnselected";
908 controls_ebox
.set_name ("BusControlsBaseInactiveUnselected");
909 controls_base_selected_name
= "BusControlsBaseInactiveSelected";
910 controls_base_unselected_name
= "BusControlsBaseInactiveUnselected";
918 MidiTimeAxisView::add_note_selection (uint8_t note
)
920 if (!_editor
.internal_editing()) {
924 uint16_t chn_mask
= _channel_selector
.get_selected_channels();
926 if (_view
->num_selected_regionviews() == 0) {
927 _view
->foreach_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection_region_view
), note
, chn_mask
));
929 _view
->foreach_selected_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection_region_view
), note
, chn_mask
));
934 MidiTimeAxisView::extend_note_selection (uint8_t note
)
936 if (!_editor
.internal_editing()) {
940 uint16_t chn_mask
= _channel_selector
.get_selected_channels();
942 if (_view
->num_selected_regionviews() == 0) {
943 _view
->foreach_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::extend_note_selection_region_view
), note
, chn_mask
));
945 _view
->foreach_selected_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::extend_note_selection_region_view
), note
, chn_mask
));
950 MidiTimeAxisView::toggle_note_selection (uint8_t note
)
952 if (!_editor
.internal_editing()) {
956 uint16_t chn_mask
= _channel_selector
.get_selected_channels();
958 if (_view
->num_selected_regionviews() == 0) {
959 _view
->foreach_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection_region_view
), note
, chn_mask
));
961 _view
->foreach_selected_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection_region_view
), note
, chn_mask
));
966 MidiTimeAxisView::add_note_selection_region_view (RegionView
* rv
, uint8_t note
, uint16_t chn_mask
)
968 dynamic_cast<MidiRegionView
*>(rv
)->select_matching_notes (note
, chn_mask
, false, false);
972 MidiTimeAxisView::extend_note_selection_region_view (RegionView
* rv
, uint8_t note
, uint16_t chn_mask
)
974 dynamic_cast<MidiRegionView
*>(rv
)->select_matching_notes (note
, chn_mask
, true, true);
978 MidiTimeAxisView::toggle_note_selection_region_view (RegionView
* rv
, uint8_t note
, uint16_t chn_mask
)
980 dynamic_cast<MidiRegionView
*>(rv
)->toggle_matching_notes (note
, chn_mask
);
984 MidiTimeAxisView::set_channel_mode (ChannelMode
, uint16_t)
986 /* hide all automation tracks that use the wrong channel(s) and show all those that use
990 uint16_t selected_channels
= _channel_selector
.get_selected_channels();
991 bool changed
= false;
995 for (uint32_t ctl
= 0; ctl
< 127; ++ctl
) {
997 for (uint32_t chn
= 0; chn
< 16; ++chn
) {
998 Evoral::Parameter
fully_qualified_param (MidiCCAutomation
, chn
, ctl
);
999 boost::shared_ptr
<AutomationTimeAxisView
> track
= automation_child (fully_qualified_param
);
1005 if ((selected_channels
& (0x0001 << chn
)) == 0) {
1006 /* channel not in use. hiding it will trigger RouteTimeAxisView::automation_track_hidden()
1007 which will cause a redraw. We don't want one per channel, so block that with no_redraw.
1009 changed
= track
->set_visibility (false) || changed
;
1011 changed
= track
->set_visibility (true) || changed
;
1018 /* TODO: Bender, Pressure */
1020 /* invalidate the controller menu, so that we rebuild it next time */
1021 _controller_menu_map
.clear ();
1022 delete controller_menu
;
1023 controller_menu
= 0;
1026 _route
->gui_changed ("track_height", this);
1031 MidiTimeAxisView::automation_child_menu_item (Evoral::Parameter param
)
1033 Gtk::CheckMenuItem
* m
= RouteTimeAxisView::automation_child_menu_item (param
);
1038 ParameterMenuMap::iterator i
= _controller_menu_map
.find (param
);
1039 if (i
!= _controller_menu_map
.end()) {
1043 i
= _channel_command_menu_map
.find (param
);
1044 if (i
!= _channel_command_menu_map
.end()) {
1051 boost::shared_ptr
<MidiRegion
>
1052 MidiTimeAxisView::add_region (framepos_t pos
, framecnt_t length
, bool commit
)
1054 Editor
* real_editor
= dynamic_cast<Editor
*> (&_editor
);
1056 real_editor
->begin_reversible_command (Operations::create_region
);
1057 playlist()->clear_changes ();
1059 real_editor
->snap_to (pos
, 0);
1061 boost::shared_ptr
<Source
> src
= _session
->create_midi_source_for_session (view()->trackview().track().get(),
1062 view()->trackview().track()->name());
1065 plist
.add (ARDOUR::Properties::start
, 0);
1066 plist
.add (ARDOUR::Properties::length
, length
);
1067 plist
.add (ARDOUR::Properties::name
, PBD::basename_nosuffix(src
->name()));
1069 boost::shared_ptr
<Region
> region
= (RegionFactory::create (src
, plist
));
1071 playlist()->add_region (region
, pos
);
1072 _session
->add_command (new StatefulDiffCommand (playlist()));
1075 real_editor
->commit_reversible_command ();
1078 return boost::dynamic_pointer_cast
<MidiRegion
>(region
);
1082 MidiTimeAxisView::ensure_step_editor ()
1084 if (!_step_editor
) {
1085 _step_editor
= new StepEditor (_editor
, midi_track(), *this);
1090 MidiTimeAxisView::start_step_editing ()
1092 ensure_step_editor ();
1093 _step_editor
->start_step_editing ();
1097 MidiTimeAxisView::stop_step_editing ()
1100 _step_editor
->stop_step_editing ();
1105 /** @return channel (counted from 0) to add an event to, based on the current setting
1106 * of the channel selector.
1109 MidiTimeAxisView::get_channel_for_add () const
1111 uint16_t const chn_mask
= _channel_selector
.get_selected_channels ();
1113 uint8_t channel
= 0;
1115 /* pick the highest selected channel, unless all channels are selected,
1116 which is interpreted to mean channel 1 (zero)
1119 for (uint16_t i
= 0; i
< 16; ++i
) {
1120 if (chn_mask
& (1<<i
)) {
1126 if (chn_cnt
== 16) {