introduce GUIObjectState; massive, pervasive changes in visibility and height managem...
[ardour.git] / gtk2_ardour / midi_time_axis.cc
1 /*
2     Copyright (C) 2000 Paul Davis
3
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.
8
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.
13
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.
17 */
18
19 #include <cstdlib>
20 #include <cmath>
21
22 #include <algorithm>
23 #include <string>
24 #include <vector>
25
26 #include <sigc++/bind.h>
27
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"
35
36 #include "gtkmm2ext/gtk_ui.h"
37 #include "gtkmm2ext/selector.h"
38 #include "gtkmm2ext/bindable_button.h"
39 #include "gtkmm2ext/utils.h"
40
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"
56
57 #include "midi++/names.h"
58
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"
87
88 #include "ardour/midi_track.h"
89
90 #include "i18n.h"
91
92 using namespace ARDOUR;
93 using namespace PBD;
94 using namespace Gtk;
95 using namespace Gtkmm2ext;
96 using namespace Editing;
97
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;
101
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)
119 {
120 }
121
122 void
123 MidiTimeAxisView::set_route (boost::shared_ptr<Route> rt)
124 {
125         RouteTimeAxisView::set_route (rt);
126
127         subplugin_menu.set_name ("ArdourContextMenu");
128
129         _view = new MidiStreamView (*this);
130
131         ignore_toggle = false;
132
133         mute_button->set_active (false);
134         solo_button->set_active (false);
135
136         if (is_midi_track()) {
137                 controls_ebox.set_name ("MidiTimeAxisViewControlsBaseUnselected");
138                 _note_mode = midi_track()->note_mode();
139         } else { // MIDI bus (which doesn't exist yet..)
140                 controls_ebox.set_name ("MidiBusControlsBaseUnselected");
141         }
142
143         /* map current state of the route */
144
145         processors_changed (RouteProcessorChange ());
146
147         _route->processors_changed.connect (*this, invalidator (*this), ui_bind (&MidiTimeAxisView::processors_changed, this, _1), gui_context());
148
149         if (is_track()) {
150                 _piano_roll_header = new PianoRollHeader(*midi_view());
151
152                 _piano_roll_header->AddNoteSelection.connect (sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection));
153                 _piano_roll_header->ExtendNoteSelection.connect (sigc::mem_fun (*this, &MidiTimeAxisView::extend_note_selection));
154                 _piano_roll_header->ToggleNoteSelection.connect (sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection));
155
156                 _range_scroomer = new MidiScroomer(midi_view()->note_range_adjustment);
157
158                 /* Suspend updates of the StreamView during scroomer drags to speed things up */
159                 _range_scroomer->DragStarting.connect (sigc::mem_fun (*midi_view(), &MidiStreamView::suspend_updates));
160                 _range_scroomer->DragFinishing.connect (sigc::mem_fun (*midi_view(), &MidiStreamView::resume_updates));
161
162                 controls_hbox.pack_start(*_range_scroomer);
163                 controls_hbox.pack_start(*_piano_roll_header);
164
165                 controls_ebox.set_name ("MidiTrackControlsBaseUnselected");
166                 controls_base_selected_name = "MidiTrackControlsBaseSelected";
167                 controls_base_unselected_name = "MidiTrackControlsBaseUnselected";
168
169                 midi_view()->NoteRangeChanged.connect (sigc::mem_fun(*this, &MidiTimeAxisView::update_range));
170
171                 /* ask for notifications of any new RegionViews */
172                 _view->RegionViewAdded.connect (sigc::mem_fun(*this, &MidiTimeAxisView::region_view_added));
173
174                 if (!_editor.have_idled()) {
175                         /* first idle will do what we need */
176                 } else {
177                         first_idle ();
178                 }
179         }
180
181         HBox* midi_controls_hbox = manage(new HBox());
182
183         MIDI::Name::MidiPatchManager& patch_manager = MIDI::Name::MidiPatchManager::instance();
184
185         MIDI::Name::MasterDeviceNames::Models::const_iterator m = patch_manager.all_models().begin();
186         for (; m != patch_manager.all_models().end(); ++m) {
187                 _model_selector.append_text(m->c_str());
188         }
189
190         _model_selector.signal_changed().connect(sigc::mem_fun(*this, &MidiTimeAxisView::model_changed));
191
192         _custom_device_mode_selector.signal_changed().connect(
193                         sigc::mem_fun(*this, &MidiTimeAxisView::custom_device_mode_changed));
194
195         // TODO: persist the choice
196         // this initializes the comboboxes and sends out the signal
197         _model_selector.set_active(0);
198
199         midi_controls_hbox->pack_start(_channel_selector, true, false);
200         if (!patch_manager.all_models().empty()) {
201                 _midi_controls_box.pack_start(_model_selector, true, false);
202                 _midi_controls_box.pack_start(_custom_device_mode_selector, true, false);
203         }
204
205         _midi_controls_box.pack_start(*midi_controls_hbox, true, true);
206
207         controls_vbox.pack_start(_midi_controls_box, false, false);
208
209         // restore channel selector settings
210         _channel_selector.set_channel_mode(midi_track()->get_channel_mode(), midi_track()->get_channel_mask());
211         _channel_selector.mode_changed.connect(
212                 sigc::mem_fun(*midi_track(), &MidiTrack::set_channel_mode));
213         _channel_selector.mode_changed.connect(
214                 sigc::mem_fun(*this, &MidiTimeAxisView::set_channel_mode));
215
216         string prop = gui_property ("color-mode");
217         if (!prop.empty()) {
218                 _color_mode = ColorMode (string_2_enum(prop, _color_mode));
219                 if (_color_mode == ChannelColors) {
220                         _channel_selector.set_channel_colors(CanvasNoteEvent::midi_channel_colors);
221                 }
222         }
223
224         set_color_mode (_color_mode, true, false);
225
226         prop = gui_property ("note-mode");
227         if (!prop.empty()) {
228                 _note_mode = NoteMode (string_2_enum (prop, _note_mode));
229                 if (_percussion_mode_item) {
230                         _percussion_mode_item->set_active (_note_mode == Percussive);
231                 }
232         }
233 }
234
235 void
236 MidiTimeAxisView::first_idle ()
237 {
238         if (is_track ()) {
239                 _view->attach ();
240         }
241 }
242
243 MidiTimeAxisView::~MidiTimeAxisView ()
244 {
245         delete _piano_roll_header;
246         _piano_roll_header = 0;
247
248         delete _range_scroomer;
249         _range_scroomer = 0;
250
251         delete controller_menu;
252         delete _step_editor;
253 }
254
255 void
256 MidiTimeAxisView::enter_internal_edit_mode ()
257 {
258         if (midi_view()) {
259                 midi_view()->enter_internal_edit_mode ();
260         }
261 }
262
263 void
264 MidiTimeAxisView::leave_internal_edit_mode ()
265 {
266         if (midi_view()) {
267                 midi_view()->leave_internal_edit_mode ();
268         }
269 }
270
271 void
272 MidiTimeAxisView::check_step_edit ()
273 {
274         ensure_step_editor ();
275         _step_editor->check_step_edit ();
276 }
277
278 void
279 MidiTimeAxisView::model_changed()
280 {
281         std::list<std::string> device_modes = MIDI::Name::MidiPatchManager::instance()
282                 .custom_device_mode_names_by_model(_model_selector.get_active_text());
283
284         _custom_device_mode_selector.clear_items();
285
286         for (std::list<std::string>::const_iterator i = device_modes.begin();
287                         i != device_modes.end(); ++i) {
288                 cerr << "found custom device mode " << *i << " thread_id: " << pthread_self() << endl;
289                 _custom_device_mode_selector.append_text(*i);
290         }
291
292         _custom_device_mode_selector.set_active(0);
293 }
294
295 void MidiTimeAxisView::custom_device_mode_changed()
296 {
297         _midi_patch_settings_changed.emit(_model_selector.get_active_text(),
298                         _custom_device_mode_selector.get_active_text());
299 }
300
301 MidiStreamView*
302 MidiTimeAxisView::midi_view()
303 {
304         return dynamic_cast<MidiStreamView*>(_view);
305 }
306
307 void
308 MidiTimeAxisView::set_height (uint32_t h)
309 {
310         RouteTimeAxisView::set_height (h);
311
312         if (height >= MIDI_CONTROLS_BOX_MIN_HEIGHT) {
313                 _midi_controls_box.show_all ();
314         } else {
315                 _midi_controls_box.hide();
316         }
317         
318         if (height >= KEYBOARD_MIN_HEIGHT) {
319                 if (is_track() && _range_scroomer)
320                         _range_scroomer->show();
321                 if (is_track() && _piano_roll_header)
322                         _piano_roll_header->show();
323         } else {
324                 if (is_track() && _range_scroomer)
325                         _range_scroomer->hide();
326                 if (is_track() && _piano_roll_header)
327                         _piano_roll_header->hide();
328         }
329 }
330
331 void
332 MidiTimeAxisView::append_extra_display_menu_items ()
333 {
334         using namespace Menu_Helpers;
335
336         MenuList& items = display_menu->items();
337
338         // Note range
339         Menu *range_menu = manage(new Menu);
340         MenuList& range_items = range_menu->items();
341         range_menu->set_name ("ArdourContextMenu");
342
343         range_items.push_back (MenuElem (_("Show Full Range"), sigc::bind (
344                         sigc::mem_fun(*this, &MidiTimeAxisView::set_note_range),
345                         MidiStreamView::FullRange)));
346
347         range_items.push_back (MenuElem (_("Fit Contents"), sigc::bind (
348                         sigc::mem_fun(*this, &MidiTimeAxisView::set_note_range),
349                         MidiStreamView::ContentsRange)));
350
351         items.push_back (MenuElem (_("Note Range"), *range_menu));
352         items.push_back (MenuElem (_("Note Mode"), *build_note_mode_menu()));
353
354         items.push_back (CheckMenuElem (_("MIDI Thru"), sigc::mem_fun(*this, &MidiTimeAxisView::toggle_midi_thru)));
355         _midi_thru_item = dynamic_cast<CheckMenuItem*>(&items.back());
356
357         items.push_back (SeparatorElem ());
358 }
359
360 void
361 MidiTimeAxisView::toggle_midi_thru ()
362 {
363         if (!_midi_thru_item) {
364                 return;
365         }
366
367         bool view_yn = _midi_thru_item->get_active();
368         if (view_yn != midi_track()->midi_thru()) {
369                 midi_track()->set_midi_thru (view_yn);
370         }
371 }
372
373 void
374 MidiTimeAxisView::build_automation_action_menu (bool for_selection)
375 {
376         using namespace Menu_Helpers;
377
378         /* If we have a controller menu, we need to detach it before
379            RouteTimeAxis::build_automation_action_menu destroys the
380            menu it is attached to.  Otherwise GTK destroys
381            controller_menu's gobj, meaning that it can't be reattached
382            below.  See bug #3134.
383         */
384
385         if (controller_menu) {
386                 detach_menu (*controller_menu);
387         }
388
389         _channel_command_menu_map.clear ();
390         RouteTimeAxisView::build_automation_action_menu (for_selection);
391
392         MenuList& automation_items = automation_action_menu->items();
393
394         uint16_t selected_channels = _channel_selector.get_selected_channels();
395
396         if (selected_channels !=  0) {
397
398                 automation_items.push_back (SeparatorElem());
399
400                 /* these 2 MIDI "command" types are semantically more like automation than note data,
401                    but they are not MIDI controllers. We give them special status in this menu, since
402                    they will not show up in the controller list and anyone who actually knows
403                    something about MIDI (!) would not expect to find them there.
404                 */
405
406                 add_channel_command_menu_item (automation_items, _("Bender"), MidiPitchBenderAutomation, 0);
407                 automation_items.back().set_sensitive (!for_selection || _editor.get_selection().tracks.size() == 1);
408                 add_channel_command_menu_item (automation_items, _("Pressure"), MidiChannelPressureAutomation, 0);
409                 automation_items.back().set_sensitive (!for_selection || _editor.get_selection().tracks.size() == 1);
410
411                 /* now all MIDI controllers. Always offer the possibility that we will rebuild the controllers menu
412                    since it might need to be updated after a channel mode change or other change. Also detach it
413                    first in case it has been used anywhere else.
414                 */
415
416                 build_controller_menu ();
417
418                 automation_items.push_back (SeparatorElem());
419                 automation_items.push_back (MenuElem (_("Controllers"), *controller_menu));
420                 automation_items.back().set_sensitive (!for_selection || _editor.get_selection().tracks.size() == 1);
421         } else {
422                 automation_items.push_back (MenuElem (string_compose ("<i>%1</i>", _("No MIDI Channels selected"))));
423                 dynamic_cast<Label*> (automation_items.back().get_child())->set_use_markup (true);
424         }
425
426 }
427
428 void
429 MidiTimeAxisView::change_all_channel_tracks_visibility (bool yn, Evoral::Parameter param)
430 {
431         uint16_t selected_channels = _channel_selector.get_selected_channels();
432
433         for (uint8_t chn = 0; chn < 16; chn++) {
434                 if (selected_channels & (0x0001 << chn)) {
435
436                         Evoral::Parameter fully_qualified_param (param.type(), chn, param.id());
437                         Gtk::CheckMenuItem* menu = automation_child_menu_item (fully_qualified_param);
438
439                         if (menu) {
440                                 menu->set_active (yn);
441                         }
442                 }
443         }
444 }
445
446 void
447 MidiTimeAxisView::add_channel_command_menu_item (Menu_Helpers::MenuList& items, const string& label, AutomationType auto_type, uint8_t cmd)
448 {
449         using namespace Menu_Helpers;
450
451         /* count the number of selected channels because we will build a different menu structure if there is more than 1 selected.
452          */
453
454         uint16_t selected_channels = _channel_selector.get_selected_channels();
455         int chn_cnt = 0;
456
457         for (uint8_t chn = 0; chn < 16; chn++) {
458                 if (selected_channels & (0x0001 << chn)) {
459                         if (++chn_cnt > 1) {
460                                 break;
461                         }
462                 }
463         }
464
465         if (chn_cnt > 1) {
466
467                 /* multiple channels - create a submenu, with 1 item per channel */
468
469                 Menu* chn_menu = manage (new Menu);
470                 MenuList& chn_items (chn_menu->items());
471                 Evoral::Parameter param_without_channel (auto_type, 0, cmd);
472
473                 /* add a couple of items to hide/show all of them */
474
475                 chn_items.push_back (MenuElem (_("Hide all channels"),
476                                                     sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
477                                                                 false, param_without_channel)));
478                 chn_items.push_back (MenuElem (_("Show all channels"),
479                                                     sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
480                                                                 true, param_without_channel)));
481
482                 for (uint8_t chn = 0; chn < 16; chn++) {
483                         if (selected_channels & (0x0001 << chn)) {
484
485                                 /* for each selected channel, add a menu item for this controller */
486
487                                 Evoral::Parameter fully_qualified_param (auto_type, chn, cmd);
488                                 chn_items.push_back (CheckMenuElem (string_compose (_("Channel %1"), chn+1),
489                                                                     sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
490                                                                                 fully_qualified_param)));
491
492                                 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
493                                 bool visible = false;
494
495                                 if (track) {
496                                         if (track->marked_for_display()) {
497                                                 visible = true;
498                                         }
499                                 }
500
501                                 CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&chn_items.back());
502                                 _channel_command_menu_map[fully_qualified_param] = cmi;
503                                 cmi->set_active (visible);
504                         }
505                 }
506
507                 /* now create an item in the parent menu that has the per-channel list as a submenu */
508
509                 items.push_back (MenuElem (label, *chn_menu));
510
511         } else {
512
513                 /* just one channel - create a single menu item for this command+channel combination*/
514
515                 for (uint8_t chn = 0; chn < 16; chn++) {
516                         if (selected_channels & (0x0001 << chn)) {
517
518                                 Evoral::Parameter fully_qualified_param (auto_type, chn, cmd);
519                                 items.push_back (CheckMenuElem (label,
520                                                                 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
521                                                                             fully_qualified_param)));
522
523                                 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
524                                 bool visible = false;
525
526                                 if (track) {
527                                         if (track->marked_for_display()) {
528                                                 visible = true;
529                                         }
530                                 }
531
532                                 CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&items.back());
533                                 _channel_command_menu_map[fully_qualified_param] = cmi;
534                                 cmi->set_active (visible);
535
536                                 /* one channel only */
537                                 break;
538                         }
539                 }
540         }
541 }
542
543 void
544 MidiTimeAxisView::build_controller_menu ()
545 {
546         using namespace Menu_Helpers;
547
548         if (controller_menu) {
549                 /* it exists and has not been invalidated by a channel mode change, so just return it */
550                 return;
551         }
552
553         controller_menu = new Menu; // explicitly managed by us
554         MenuList& items (controller_menu->items());
555
556         /* create several "top level" menu items for sets of controllers (16 at a time), and populate each one with a submenu
557            for each controller+channel combination covering the currently selected channels for this track
558         */
559
560         uint16_t selected_channels = _channel_selector.get_selected_channels();
561
562         /* count the number of selected channels because we will build a different menu structure if there is more than 1 selected.
563          */
564
565         int chn_cnt = 0;
566
567         for (uint8_t chn = 0; chn < 16; chn++) {
568                 if (selected_channels & (0x0001 << chn)) {
569                         if (++chn_cnt > 1) {
570                                 break;
571                         }
572                 }
573         }
574
575         /* loop over all 127 MIDI controllers, in groups of 16; except don't offer
576            bank select controllers, as they are handled by the `patch' code */
577
578         for (int i = 0; i < 127; i += 16) {
579
580                 Menu* ctl_menu = manage (new Menu);
581                 MenuList& ctl_items (ctl_menu->items());
582
583
584                 /* for each controller, consider whether to create a submenu or a single item */
585
586                 for (int ctl = i; ctl < i+16; ++ctl) {
587
588                         if (ctl == MIDI_CTL_MSB_BANK || ctl == MIDI_CTL_LSB_BANK) {
589                                 continue;
590                         }
591
592                         if (chn_cnt > 1) {
593
594                                 /* multiple channels - create a submenu, with 1 item per channel */
595
596                                 Menu* chn_menu = manage (new Menu);
597                                 MenuList& chn_items (chn_menu->items());
598
599                                 /* add a couple of items to hide/show this controller on all channels */
600
601                                 Evoral::Parameter param_without_channel (MidiCCAutomation, 0, ctl);
602                                 chn_items.push_back (MenuElem (_("Hide all channels"),
603                                                                     sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
604                                                                                 false, param_without_channel)));
605                                 chn_items.push_back (MenuElem (_("Show all channels"),
606                                                                     sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
607                                                                                 true, param_without_channel)));
608
609                                 for (uint8_t chn = 0; chn < 16; chn++) {
610                                         if (selected_channels & (0x0001 << chn)) {
611
612                                                 /* for each selected channel, add a menu item for this controller */
613
614                                                 Evoral::Parameter fully_qualified_param (MidiCCAutomation, chn, ctl);
615                                                 chn_items.push_back (CheckMenuElem (string_compose (_("Channel %1"), chn+1),
616                                                                                     sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
617                                                                                                 fully_qualified_param)));
618
619                                                 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
620                                                 bool visible = false;
621
622                                                 if (track) {
623                                                         if (track->marked_for_display()) {
624                                                                 visible = true;
625                                                         }
626                                                 }
627
628                                                 CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&chn_items.back());
629                                                 _controller_menu_map[fully_qualified_param] = cmi;
630                                                 cmi->set_active (visible);
631                                         }
632                                 }
633
634                                 /* add the per-channel menu to the list of controllers, with the name of the controller */
635                                 ctl_items.push_back (MenuElem (string_compose ("<b>%1</b>: %2", ctl, midi_name (ctl)), *chn_menu));
636                                 dynamic_cast<Label*> (ctl_items.back().get_child())->set_use_markup (true);
637
638                         } else {
639
640                                 /* just one channel - create a single menu item for this ctl+channel combination*/
641
642                                 for (uint8_t chn = 0; chn < 16; chn++) {
643                                         if (selected_channels & (0x0001 << chn)) {
644
645                                                 Evoral::Parameter fully_qualified_param (MidiCCAutomation, chn, ctl);
646                                                 ctl_items.push_back (
647                                                         CheckMenuElem (
648                                                                 string_compose ("<b>%1</b>: %2 [%3]", ctl, midi_name (ctl), int (chn)),
649                                                                 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
650                                                                             fully_qualified_param)
651                                                                 )
652                                                         );
653                                                 dynamic_cast<Label*> (ctl_items.back().get_child())->set_use_markup (true);
654
655                                                 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
656                                                 bool visible = false;
657
658                                                 if (track) {
659                                                         if (track->marked_for_display()) {
660                                                                 visible = true;
661                                                         }
662                                                 }
663
664                                                 CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&ctl_items.back());
665                                                 _controller_menu_map[fully_qualified_param] = cmi;
666                                                 cmi->set_active (visible);
667
668                                                 /* one channel only */
669                                                 break;
670                                         }
671                                 }
672                         }
673                 }
674
675                 /* add the menu for this block of controllers to the overall controller menu */
676
677                 items.push_back (MenuElem (string_compose (_("Controllers %1-%2"), i, i+15), *ctl_menu));
678         }
679 }
680
681 Gtk::Menu*
682 MidiTimeAxisView::build_note_mode_menu()
683 {
684         using namespace Menu_Helpers;
685
686         Menu* mode_menu = manage (new Menu);
687         MenuList& items = mode_menu->items();
688         mode_menu->set_name ("ArdourContextMenu");
689
690         RadioMenuItem::Group mode_group;
691         items.push_back (RadioMenuElem (mode_group, _("Sustained"),
692                                 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_mode), Sustained)));
693         _note_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
694         _note_mode_item->set_active(_note_mode == Sustained);
695
696         items.push_back (RadioMenuElem (mode_group, _("Percussive"),
697                                 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_mode), Percussive)));
698         _percussion_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
699         _percussion_mode_item->set_active(_note_mode == Percussive);
700
701         return mode_menu;
702 }
703
704 Gtk::Menu*
705 MidiTimeAxisView::build_color_mode_menu()
706 {
707         using namespace Menu_Helpers;
708
709         Menu* mode_menu = manage (new Menu);
710         MenuList& items = mode_menu->items();
711         mode_menu->set_name ("ArdourContextMenu");
712
713         RadioMenuItem::Group mode_group;
714         items.push_back (RadioMenuElem (mode_group, _("Meter Colors"),
715                                         sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode),
716                                                     MeterColors, false, true)));
717         _meter_color_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
718         _meter_color_mode_item->set_active(_color_mode == MeterColors);
719
720         items.push_back (RadioMenuElem (mode_group, _("Channel Colors"),
721                                         sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode),
722                                                     ChannelColors, false, true)));
723         _channel_color_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
724         _channel_color_mode_item->set_active(_color_mode == ChannelColors);
725
726         items.push_back (RadioMenuElem (mode_group, _("Track Color"),
727                                         sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode),
728                                                     TrackColor, false, true)));
729         _channel_color_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
730         _channel_color_mode_item->set_active(_color_mode == TrackColor);
731
732         return mode_menu;
733 }
734
735 void
736 MidiTimeAxisView::set_note_mode(NoteMode mode)
737 {
738         if (_note_mode != mode || midi_track()->note_mode() != mode) {
739                 _note_mode = mode;
740                 midi_track()->set_note_mode(mode);
741                 set_gui_property ("note-mode", enum_2_string(_note_mode));
742                 _view->redisplay_track();
743         }
744 }
745
746 void
747 MidiTimeAxisView::set_color_mode (ColorMode mode, bool force, bool redisplay)
748 {
749         if (_color_mode == mode && !force) {
750                 return;
751         }
752
753         if (mode == ChannelColors) {
754                 _channel_selector.set_channel_colors(CanvasNoteEvent::midi_channel_colors);
755         } else {
756                 _channel_selector.set_default_channel_color();
757         }
758
759         _color_mode = mode;
760         set_gui_property ("color-mode", enum_2_string(_color_mode));
761         if (redisplay) {
762                 _view->redisplay_track();
763         }
764 }
765
766 void
767 MidiTimeAxisView::set_note_range(MidiStreamView::VisibleNoteRange range)
768 {
769         if (!_ignore_signals)
770                 midi_view()->set_note_range(range);
771 }
772
773
774 void
775 MidiTimeAxisView::update_range()
776 {
777         MidiGhostRegion* mgr;
778
779         for(list<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
780                 if ((mgr = dynamic_cast<MidiGhostRegion*>(*i)) != 0) {
781                         mgr->update_range();
782                 }
783         }
784 }
785
786 void
787 MidiTimeAxisView::show_all_automation (bool apply_to_selection)
788 {
789         if (apply_to_selection) {
790                 _editor.get_selection().tracks.foreach_midi_time_axis (boost::bind (&MidiTimeAxisView::show_all_automation, _1, false));
791         } else {
792                 if (midi_track()) {
793                         const set<Evoral::Parameter> params = midi_track()->midi_playlist()->contained_automation();
794
795                         for (set<Evoral::Parameter>::const_iterator i = params.begin(); i != params.end(); ++i) {
796                                 create_automation_child(*i, true);
797                         }
798                 }
799
800                 RouteTimeAxisView::show_all_automation ();
801         }
802 }
803
804 void
805 MidiTimeAxisView::show_existing_automation (bool apply_to_selection)
806 {
807         if (apply_to_selection) {
808                 _editor.get_selection().tracks.foreach_midi_time_axis (boost::bind (&MidiTimeAxisView::show_existing_automation, _1, false));
809         } else {
810                 if (midi_track()) {
811                         const set<Evoral::Parameter> params = midi_track()->midi_playlist()->contained_automation();
812
813                         for (set<Evoral::Parameter>::const_iterator i = params.begin(); i != params.end(); ++i) {
814                                 create_automation_child (*i, true);
815                         }
816                 }
817
818                 RouteTimeAxisView::show_existing_automation ();
819         }
820 }
821
822 /** Create an automation track for the given parameter (pitch bend, channel pressure).
823  */
824 void
825 MidiTimeAxisView::create_automation_child (const Evoral::Parameter& param, bool show)
826 {
827         if (param.type() == NullAutomation) {
828                 cerr << "WARNING: Attempt to create NullAutomation child, ignoring" << endl;
829                 return;
830         }
831
832         AutomationTracks::iterator existing = _automation_tracks.find (param);
833
834         if (existing != _automation_tracks.end()) {
835
836                 /* automation track created because we had existing data for
837                  * the processor, but visibility may need to be controlled
838                  * since it will have been set visible by default.
839                  */
840
841                 cerr << "show existing auto track: " << show << " noredraw " << no_redraw << endl;
842
843                 if (existing->second->set_marked_for_display (show) && !no_redraw) {
844                         request_redraw ();
845                 }
846
847                 return;
848         }
849
850         boost::shared_ptr<AutomationTimeAxisView> track;
851
852         switch (param.type()) {
853
854         case GainAutomation:
855                 create_gain_automation_child (param, show);
856                 break;
857
858         case PluginAutomation:
859                 /* handled elsewhere */
860                 break;
861
862         case MidiCCAutomation:
863         case MidiPgmChangeAutomation:
864         case MidiPitchBenderAutomation:
865         case MidiChannelPressureAutomation:
866         case MidiSystemExclusiveAutomation:
867                 /* These controllers are region "automation" - they are owned
868                  * by regions (and their MidiModels), not by the track. As a
869                  * result we do not create an AutomationList/Line for the track
870                  * ... except here we are doing something!! XXX 
871                  */
872
873                 track.reset (new AutomationTimeAxisView (
874                                      _session,
875                                      _route,
876                                      boost::shared_ptr<Automatable> (),
877                                      boost::shared_ptr<AutomationControl> (),
878                                      param,
879                                      _editor,
880                                      *this,
881                                      true,
882                                      parent_canvas,
883                                      _route->describe_parameter(param)
884                                      ));
885
886                 if (_view) {
887                         _view->foreach_regionview (sigc::mem_fun (*track.get(), &TimeAxisView::add_ghost));
888                 }
889
890                 add_automation_child (param, track, show);
891                 break;
892
893         default:
894                 error << "MidiTimeAxisView: unknown automation child " << EventTypeMap::instance().to_symbol(param) << endmsg;
895         }
896 }
897
898 void
899 MidiTimeAxisView::route_active_changed ()
900 {
901         RouteUI::route_active_changed ();
902
903         if (is_track()) {
904                 if (_route->active()) {
905                         controls_ebox.set_name ("MidiTrackControlsBaseUnselected");
906                         controls_base_selected_name = "MidiTrackControlsBaseSelected";
907                         controls_base_unselected_name = "MidiTrackControlsBaseUnselected";
908                 } else {
909                         controls_ebox.set_name ("MidiTrackControlsBaseInactiveUnselected");
910                         controls_base_selected_name = "MidiTrackControlsBaseInactiveSelected";
911                         controls_base_unselected_name = "MidiTrackControlsBaseInactiveUnselected";
912                 }
913         } else {
914
915                 throw; // wha?
916
917                 if (_route->active()) {
918                         controls_ebox.set_name ("BusControlsBaseUnselected");
919                         controls_base_selected_name = "BusControlsBaseSelected";
920                         controls_base_unselected_name = "BusControlsBaseUnselected";
921                 } else {
922                         controls_ebox.set_name ("BusControlsBaseInactiveUnselected");
923                         controls_base_selected_name = "BusControlsBaseInactiveSelected";
924                         controls_base_unselected_name = "BusControlsBaseInactiveUnselected";
925                 }
926         }
927 }
928
929
930
931 void
932 MidiTimeAxisView::add_note_selection (uint8_t note)
933 {
934         if (!_editor.internal_editing()) {
935                 return;
936         }
937
938         uint16_t chn_mask = _channel_selector.get_selected_channels();
939
940         if (_view->num_selected_regionviews() == 0) {
941                 _view->foreach_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection_region_view), note, chn_mask));
942         } else {
943                 _view->foreach_selected_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection_region_view), note, chn_mask));
944         }
945 }
946
947 void
948 MidiTimeAxisView::extend_note_selection (uint8_t note)
949 {
950         if (!_editor.internal_editing()) {
951                 return;
952         }
953
954         uint16_t chn_mask = _channel_selector.get_selected_channels();
955
956         if (_view->num_selected_regionviews() == 0) {
957                 _view->foreach_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::extend_note_selection_region_view), note, chn_mask));
958         } else {
959                 _view->foreach_selected_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::extend_note_selection_region_view), note, chn_mask));
960         }
961 }
962
963 void
964 MidiTimeAxisView::toggle_note_selection (uint8_t note)
965 {
966         if (!_editor.internal_editing()) {
967                 return;
968         }
969
970         uint16_t chn_mask = _channel_selector.get_selected_channels();
971
972         if (_view->num_selected_regionviews() == 0) {
973                 _view->foreach_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection_region_view), note, chn_mask));
974         } else {
975                 _view->foreach_selected_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection_region_view), note, chn_mask));
976         }
977 }
978
979 void
980 MidiTimeAxisView::add_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
981 {
982         dynamic_cast<MidiRegionView*>(rv)->select_matching_notes (note, chn_mask, false, false);
983 }
984
985 void
986 MidiTimeAxisView::extend_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
987 {
988         dynamic_cast<MidiRegionView*>(rv)->select_matching_notes (note, chn_mask, true, true);
989 }
990
991 void
992 MidiTimeAxisView::toggle_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
993 {
994         dynamic_cast<MidiRegionView*>(rv)->toggle_matching_notes (note, chn_mask);
995 }
996
997 void
998 MidiTimeAxisView::set_channel_mode (ChannelMode, uint16_t)
999 {
1000         /* hide all automation tracks that use the wrong channel(s) and show all those that use
1001            the right ones.
1002         */
1003
1004         uint16_t selected_channels = _channel_selector.get_selected_channels();
1005         bool changed = false;
1006
1007         no_redraw = true;
1008
1009         for (uint32_t ctl = 0; ctl < 127; ++ctl) {
1010
1011                 for (uint32_t chn = 0; chn < 16; ++chn) {
1012                         Evoral::Parameter fully_qualified_param (MidiCCAutomation, chn, ctl);
1013                         boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
1014
1015                         if (!track) {
1016                                 continue;
1017                         }
1018
1019                         if ((selected_channels & (0x0001 << chn)) == 0) {
1020                                 /* channel not in use. hiding it will trigger RouteTimeAxisView::automation_track_hidden()
1021                                    which will cause a redraw. We don't want one per channel, so block that with no_redraw.
1022                                  */
1023                                 changed = track->set_marked_for_display (false) || changed;
1024                         } else {
1025                                 changed = track->set_marked_for_display (true) || changed;
1026                         }
1027                 }
1028         }
1029
1030         no_redraw = false;
1031
1032         /* TODO: Bender, Pressure */
1033
1034         /* invalidate the controller menu, so that we rebuild it next time */
1035         _controller_menu_map.clear ();
1036         delete controller_menu;
1037         controller_menu = 0;
1038
1039         if (changed) {
1040                 request_redraw ();
1041         }
1042 }
1043
1044 Gtk::CheckMenuItem*
1045 MidiTimeAxisView::automation_child_menu_item (Evoral::Parameter param)
1046 {
1047         Gtk::CheckMenuItem* m = RouteTimeAxisView::automation_child_menu_item (param);
1048         if (m) {
1049                 return m;
1050         }
1051
1052         ParameterMenuMap::iterator i = _controller_menu_map.find (param);
1053         if (i != _controller_menu_map.end()) {
1054                 return i->second;
1055         }
1056
1057         i = _channel_command_menu_map.find (param);
1058         if (i != _channel_command_menu_map.end()) {
1059                 return i->second;
1060         }
1061
1062         return 0;
1063 }
1064
1065 boost::shared_ptr<MidiRegion>
1066 MidiTimeAxisView::add_region (framepos_t pos, framecnt_t length, bool commit)
1067 {
1068         Editor* real_editor = dynamic_cast<Editor*> (&_editor);
1069
1070         real_editor->begin_reversible_command (Operations::create_region);
1071         playlist()->clear_changes ();
1072
1073         real_editor->snap_to (pos, 0);
1074
1075         boost::shared_ptr<Source> src = _session->create_midi_source_for_session (view()->trackview().track().get(),
1076                                                                                   view()->trackview().track()->name());
1077         PropertyList plist;
1078
1079         plist.add (ARDOUR::Properties::start, 0);
1080         plist.add (ARDOUR::Properties::length, length);
1081         plist.add (ARDOUR::Properties::name, PBD::basename_nosuffix(src->name()));
1082
1083         boost::shared_ptr<Region> region = (RegionFactory::create (src, plist));
1084
1085         playlist()->add_region (region, pos);
1086         _session->add_command (new StatefulDiffCommand (playlist()));
1087
1088         if (commit) {
1089                 real_editor->commit_reversible_command ();
1090         }
1091
1092         return boost::dynamic_pointer_cast<MidiRegion>(region);
1093 }
1094
1095 void
1096 MidiTimeAxisView::ensure_step_editor ()
1097 {
1098         if (!_step_editor) {
1099                 _step_editor = new StepEditor (_editor, midi_track(), *this);
1100         }
1101 }
1102
1103 void
1104 MidiTimeAxisView::start_step_editing ()
1105 {
1106         ensure_step_editor ();
1107         _step_editor->start_step_editing ();
1108
1109 }
1110 void
1111 MidiTimeAxisView::stop_step_editing ()
1112 {
1113         if (_step_editor) {
1114                 _step_editor->stop_step_editing ();
1115         }
1116 }
1117
1118
1119 /** @return channel (counted from 0) to add an event to, based on the current setting
1120  *  of the channel selector.
1121  */
1122 uint8_t
1123 MidiTimeAxisView::get_channel_for_add () const
1124 {
1125         uint16_t const chn_mask = _channel_selector.get_selected_channels ();
1126         int chn_cnt = 0;
1127         uint8_t channel = 0;
1128
1129         /* pick the highest selected channel, unless all channels are selected,
1130            which is interpreted to mean channel 1 (zero)
1131         */
1132
1133         for (uint16_t i = 0; i < 16; ++i) {
1134                 if (chn_mask & (1<<i)) {
1135                         channel = i;
1136                         chn_cnt++;
1137                 }
1138         }
1139
1140         if (chn_cnt == 16) {
1141                 channel = 0;
1142         }
1143
1144         return channel;
1145 }