Fix insertion of processors at the point at which the processor menu was opened;...
[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         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);
132         }
133         midi_view()->NoteRangeChanged.connect (sigc::mem_fun (*this, &MidiTimeAxisView::note_range_changed));
134
135         ignore_toggle = false;
136
137         mute_button->set_active (false);
138         solo_button->set_active (false);
139
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");
145         }
146
147         /* map current state of the route */
148
149         processors_changed (RouteProcessorChange ());
150
151         _route->processors_changed.connect (*this, invalidator (*this), ui_bind (&MidiTimeAxisView::processors_changed, this, _1), gui_context());
152
153         if (is_track()) {
154                 _piano_roll_header = new PianoRollHeader(*midi_view());
155
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));
159
160                 _range_scroomer = new MidiScroomer(midi_view()->note_range_adjustment);
161
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));
165
166                 controls_hbox.pack_start(*_range_scroomer);
167                 controls_hbox.pack_start(*_piano_roll_header);
168
169                 controls_ebox.set_name ("MidiTrackControlsBaseUnselected");
170                 controls_base_selected_name = "MidiTrackControlsBaseSelected";
171                 controls_base_unselected_name = "MidiTrackControlsBaseUnselected";
172
173                 midi_view()->NoteRangeChanged.connect (sigc::mem_fun(*this, &MidiTimeAxisView::update_range));
174
175                 /* ask for notifications of any new RegionViews */
176                 _view->RegionViewAdded.connect (sigc::mem_fun(*this, &MidiTimeAxisView::region_view_added));
177
178                 if (!_editor.have_idled()) {
179                         /* first idle will do what we need */
180                 } else {
181                         first_idle ();
182                 }
183         }
184
185         HBox* midi_controls_hbox = manage(new HBox());
186
187         MIDI::Name::MidiPatchManager& patch_manager = MIDI::Name::MidiPatchManager::instance();
188
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());
192         }
193
194         _model_selector.signal_changed().connect(sigc::mem_fun(*this, &MidiTimeAxisView::model_changed));
195
196         _custom_device_mode_selector.signal_changed().connect(
197                         sigc::mem_fun(*this, &MidiTimeAxisView::custom_device_mode_changed));
198
199         // TODO: persist the choice
200         // this initializes the comboboxes and sends out the signal
201         _model_selector.set_active(0);
202
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);
207         }
208
209         _midi_controls_box.pack_start(*midi_controls_hbox, true, true);
210
211         controls_vbox.pack_start(_midi_controls_box, false, false);
212
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));
219
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);
225                 }
226         }
227
228         set_color_mode (_color_mode, true, false);
229
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);
235                 }
236         }
237
238         /* Look for any GUI object state nodes that represent automation children that should exist, and create
239          * the children.
240          */
241         
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);
247
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"))));
251                 }
252         }
253 }
254
255 void
256 MidiTimeAxisView::first_idle ()
257 {
258         if (is_track ()) {
259                 _view->attach ();
260         }
261 }
262
263 MidiTimeAxisView::~MidiTimeAxisView ()
264 {
265         delete _piano_roll_header;
266         _piano_roll_header = 0;
267
268         delete _range_scroomer;
269         _range_scroomer = 0;
270
271         delete controller_menu;
272         delete _step_editor;
273 }
274
275 void
276 MidiTimeAxisView::enter_internal_edit_mode ()
277 {
278         if (midi_view()) {
279                 midi_view()->enter_internal_edit_mode ();
280         }
281 }
282
283 void
284 MidiTimeAxisView::leave_internal_edit_mode ()
285 {
286         if (midi_view()) {
287                 midi_view()->leave_internal_edit_mode ();
288         }
289 }
290
291 void
292 MidiTimeAxisView::check_step_edit ()
293 {
294         ensure_step_editor ();
295         _step_editor->check_step_edit ();
296 }
297
298 void
299 MidiTimeAxisView::model_changed()
300 {
301         std::list<std::string> device_modes = MIDI::Name::MidiPatchManager::instance()
302                 .custom_device_mode_names_by_model(_model_selector.get_active_text());
303
304         _custom_device_mode_selector.clear_items();
305
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);
310         }
311
312         _custom_device_mode_selector.set_active(0);
313 }
314
315 void MidiTimeAxisView::custom_device_mode_changed()
316 {
317         _midi_patch_settings_changed.emit(_model_selector.get_active_text(),
318                         _custom_device_mode_selector.get_active_text());
319 }
320
321 MidiStreamView*
322 MidiTimeAxisView::midi_view()
323 {
324         return dynamic_cast<MidiStreamView*>(_view);
325 }
326
327 void
328 MidiTimeAxisView::set_height (uint32_t h)
329 {
330         RouteTimeAxisView::set_height (h);
331
332         if (height >= MIDI_CONTROLS_BOX_MIN_HEIGHT) {
333                 _midi_controls_box.show_all ();
334         } else {
335                 _midi_controls_box.hide();
336         }
337         
338         if (height >= KEYBOARD_MIN_HEIGHT) {
339                 if (is_track() && _range_scroomer) {
340                         _range_scroomer->show();
341                 }
342                 if (is_track() && _piano_roll_header) {
343                         _piano_roll_header->show();
344                 }
345         } else {
346                 if (is_track() && _range_scroomer) {
347                         _range_scroomer->hide();
348                 }
349                 if (is_track() && _piano_roll_header) {
350                         _piano_roll_header->hide();
351                 }
352         }
353 }
354
355 void
356 MidiTimeAxisView::append_extra_display_menu_items ()
357 {
358         using namespace Menu_Helpers;
359
360         MenuList& items = display_menu->items();
361
362         // Note range
363         Menu *range_menu = manage(new Menu);
364         MenuList& range_items = range_menu->items();
365         range_menu->set_name ("ArdourContextMenu");
366
367         range_items.push_back (MenuElem (_("Show Full Range"), sigc::bind (
368                         sigc::mem_fun(*this, &MidiTimeAxisView::set_note_range),
369                         MidiStreamView::FullRange)));
370
371         range_items.push_back (MenuElem (_("Fit Contents"), sigc::bind (
372                         sigc::mem_fun(*this, &MidiTimeAxisView::set_note_range),
373                         MidiStreamView::ContentsRange)));
374
375         items.push_back (MenuElem (_("Note Range"), *range_menu));
376         items.push_back (MenuElem (_("Note Mode"), *build_note_mode_menu()));
377
378         items.push_back (CheckMenuElem (_("MIDI Thru"), sigc::mem_fun(*this, &MidiTimeAxisView::toggle_midi_thru)));
379         _midi_thru_item = dynamic_cast<CheckMenuItem*>(&items.back());
380
381         items.push_back (SeparatorElem ());
382 }
383
384 void
385 MidiTimeAxisView::toggle_midi_thru ()
386 {
387         if (!_midi_thru_item) {
388                 return;
389         }
390
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);
394         }
395 }
396
397 void
398 MidiTimeAxisView::build_automation_action_menu (bool for_selection)
399 {
400         using namespace Menu_Helpers;
401
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.
407         */
408
409         if (controller_menu) {
410                 detach_menu (*controller_menu);
411         }
412
413         _channel_command_menu_map.clear ();
414         RouteTimeAxisView::build_automation_action_menu (for_selection);
415
416         MenuList& automation_items = automation_action_menu->items();
417
418         uint16_t selected_channels = _channel_selector.get_selected_channels();
419
420         if (selected_channels !=  0) {
421
422                 automation_items.push_back (SeparatorElem());
423
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.
428                 */
429
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);
434
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.
438                 */
439
440                 build_controller_menu ();
441
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);
448         }
449
450 }
451
452 void
453 MidiTimeAxisView::change_all_channel_tracks_visibility (bool yn, Evoral::Parameter param)
454 {
455         uint16_t selected_channels = _channel_selector.get_selected_channels();
456
457         for (uint8_t chn = 0; chn < 16; chn++) {
458                 if (selected_channels & (0x0001 << chn)) {
459
460                         Evoral::Parameter fully_qualified_param (param.type(), chn, param.id());
461                         Gtk::CheckMenuItem* menu = automation_child_menu_item (fully_qualified_param);
462
463                         if (menu) {
464                                 menu->set_active (yn);
465                         }
466                 }
467         }
468 }
469
470 void
471 MidiTimeAxisView::add_channel_command_menu_item (Menu_Helpers::MenuList& items, const string& label, AutomationType auto_type, uint8_t cmd)
472 {
473         using namespace Menu_Helpers;
474
475         /* count the number of selected channels because we will build a different menu structure if there is more than 1 selected.
476          */
477
478         uint16_t selected_channels = _channel_selector.get_selected_channels();
479         int chn_cnt = 0;
480
481         for (uint8_t chn = 0; chn < 16; chn++) {
482                 if (selected_channels & (0x0001 << chn)) {
483                         if (++chn_cnt > 1) {
484                                 break;
485                         }
486                 }
487         }
488
489         if (chn_cnt > 1) {
490
491                 /* multiple channels - create a submenu, with 1 item per channel */
492
493                 Menu* chn_menu = manage (new Menu);
494                 MenuList& chn_items (chn_menu->items());
495                 Evoral::Parameter param_without_channel (auto_type, 0, cmd);
496
497                 /* add a couple of items to hide/show all of them */
498
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)));
505
506                 for (uint8_t chn = 0; chn < 16; chn++) {
507                         if (selected_channels & (0x0001 << chn)) {
508
509                                 /* for each selected channel, add a menu item for this controller */
510
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)));
515
516                                 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
517                                 bool visible = false;
518
519                                 if (track) {
520                                         if (track->marked_for_display()) {
521                                                 visible = true;
522                                         }
523                                 }
524
525                                 CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&chn_items.back());
526                                 _channel_command_menu_map[fully_qualified_param] = cmi;
527                                 cmi->set_active (visible);
528                         }
529                 }
530
531                 /* now create an item in the parent menu that has the per-channel list as a submenu */
532
533                 items.push_back (MenuElem (label, *chn_menu));
534
535         } else {
536
537                 /* just one channel - create a single menu item for this command+channel combination*/
538
539                 for (uint8_t chn = 0; chn < 16; chn++) {
540                         if (selected_channels & (0x0001 << chn)) {
541
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)));
546
547                                 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
548                                 bool visible = false;
549
550                                 if (track) {
551                                         if (track->marked_for_display()) {
552                                                 visible = true;
553                                         }
554                                 }
555
556                                 CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&items.back());
557                                 _channel_command_menu_map[fully_qualified_param] = cmi;
558                                 cmi->set_active (visible);
559
560                                 /* one channel only */
561                                 break;
562                         }
563                 }
564         }
565 }
566
567 void
568 MidiTimeAxisView::build_controller_menu ()
569 {
570         using namespace Menu_Helpers;
571
572         if (controller_menu) {
573                 /* it exists and has not been invalidated by a channel mode change, so just return it */
574                 return;
575         }
576
577         controller_menu = new Menu; // explicitly managed by us
578         MenuList& items (controller_menu->items());
579
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
582         */
583
584         uint16_t selected_channels = _channel_selector.get_selected_channels();
585
586         /* count the number of selected channels because we will build a different menu structure if there is more than 1 selected.
587          */
588
589         int chn_cnt = 0;
590
591         for (uint8_t chn = 0; chn < 16; chn++) {
592                 if (selected_channels & (0x0001 << chn)) {
593                         if (++chn_cnt > 1) {
594                                 break;
595                         }
596                 }
597         }
598
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 */
601
602         for (int i = 0; i < 127; i += 16) {
603
604                 Menu* ctl_menu = manage (new Menu);
605                 MenuList& ctl_items (ctl_menu->items());
606
607
608                 /* for each controller, consider whether to create a submenu or a single item */
609
610                 for (int ctl = i; ctl < i+16; ++ctl) {
611
612                         if (ctl == MIDI_CTL_MSB_BANK || ctl == MIDI_CTL_LSB_BANK) {
613                                 continue;
614                         }
615
616                         if (chn_cnt > 1) {
617
618                                 /* multiple channels - create a submenu, with 1 item per channel */
619
620                                 Menu* chn_menu = manage (new Menu);
621                                 MenuList& chn_items (chn_menu->items());
622
623                                 /* add a couple of items to hide/show this controller on all channels */
624
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)));
632
633                                 for (uint8_t chn = 0; chn < 16; chn++) {
634                                         if (selected_channels & (0x0001 << chn)) {
635
636                                                 /* for each selected channel, add a menu item for this controller */
637
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)));
642
643                                                 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
644                                                 bool visible = false;
645
646                                                 if (track) {
647                                                         if (track->marked_for_display()) {
648                                                                 visible = true;
649                                                         }
650                                                 }
651
652                                                 CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&chn_items.back());
653                                                 _controller_menu_map[fully_qualified_param] = cmi;
654                                                 cmi->set_active (visible);
655                                         }
656                                 }
657
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);
661
662                         } else {
663
664                                 /* just one channel - create a single menu item for this ctl+channel combination*/
665
666                                 for (uint8_t chn = 0; chn < 16; chn++) {
667                                         if (selected_channels & (0x0001 << chn)) {
668
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)
675                                                                 )
676                                                         );
677                                                 dynamic_cast<Label*> (ctl_items.back().get_child())->set_use_markup (true);
678
679                                                 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
680                                                 bool visible = false;
681
682                                                 if (track) {
683                                                         if (track->marked_for_display()) {
684                                                                 visible = true;
685                                                         }
686                                                 }
687
688                                                 CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&ctl_items.back());
689                                                 _controller_menu_map[fully_qualified_param] = cmi;
690                                                 cmi->set_active (visible);
691
692                                                 /* one channel only */
693                                                 break;
694                                         }
695                                 }
696                         }
697                 }
698
699                 /* add the menu for this block of controllers to the overall controller menu */
700
701                 items.push_back (MenuElem (string_compose (_("Controllers %1-%2"), i, i+15), *ctl_menu));
702         }
703 }
704
705 Gtk::Menu*
706 MidiTimeAxisView::build_note_mode_menu()
707 {
708         using namespace Menu_Helpers;
709
710         Menu* mode_menu = manage (new Menu);
711         MenuList& items = mode_menu->items();
712         mode_menu->set_name ("ArdourContextMenu");
713
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);
719
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);
724
725         return mode_menu;
726 }
727
728 Gtk::Menu*
729 MidiTimeAxisView::build_color_mode_menu()
730 {
731         using namespace Menu_Helpers;
732
733         Menu* mode_menu = manage (new Menu);
734         MenuList& items = mode_menu->items();
735         mode_menu->set_name ("ArdourContextMenu");
736
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);
743
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);
749
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);
755
756         return mode_menu;
757 }
758
759 void
760 MidiTimeAxisView::set_note_mode(NoteMode mode)
761 {
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();
767         }
768 }
769
770 void
771 MidiTimeAxisView::set_color_mode (ColorMode mode, bool force, bool redisplay)
772 {
773         if (_color_mode == mode && !force) {
774                 return;
775         }
776
777         if (mode == ChannelColors) {
778                 _channel_selector.set_channel_colors(CanvasNoteEvent::midi_channel_colors);
779         } else {
780                 _channel_selector.set_default_channel_color();
781         }
782
783         _color_mode = mode;
784         set_gui_property ("color-mode", enum_2_string(_color_mode));
785         if (redisplay) {
786                 _view->redisplay_track();
787         }
788 }
789
790 void
791 MidiTimeAxisView::set_note_range(MidiStreamView::VisibleNoteRange range)
792 {
793         if (!_ignore_signals)
794                 midi_view()->set_note_range(range);
795 }
796
797
798 void
799 MidiTimeAxisView::update_range()
800 {
801         MidiGhostRegion* mgr;
802
803         for(list<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
804                 if ((mgr = dynamic_cast<MidiGhostRegion*>(*i)) != 0) {
805                         mgr->update_range();
806                 }
807         }
808 }
809
810 void
811 MidiTimeAxisView::show_all_automation (bool apply_to_selection)
812 {
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();
818
819                         for (set<Evoral::Parameter>::const_iterator i = params.begin(); i != params.end(); ++i) {
820                                 create_automation_child(*i, true);
821                         }
822                 }
823
824                 RouteTimeAxisView::show_all_automation ();
825         }
826 }
827
828 void
829 MidiTimeAxisView::show_existing_automation (bool apply_to_selection)
830 {
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();
836
837                         for (set<Evoral::Parameter>::const_iterator i = params.begin(); i != params.end(); ++i) {
838                                 create_automation_child (*i, true);
839                         }
840                 }
841
842                 RouteTimeAxisView::show_existing_automation ();
843         }
844 }
845
846 /** Create an automation track for the given parameter (pitch bend, channel pressure).
847  */
848 void
849 MidiTimeAxisView::create_automation_child (const Evoral::Parameter& param, bool show)
850 {
851         if (param.type() == NullAutomation) {
852                 cerr << "WARNING: Attempt to create NullAutomation child, ignoring" << endl;
853                 return;
854         }
855
856         AutomationTracks::iterator existing = _automation_tracks.find (param);
857
858         if (existing != _automation_tracks.end()) {
859
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.
863                  */
864
865                 cerr << "show existing auto track: " << show << " noredraw " << no_redraw << endl;
866
867                 if (existing->second->set_marked_for_display (show) && !no_redraw) {
868                         request_redraw ();
869                 }
870
871                 return;
872         }
873
874         boost::shared_ptr<AutomationTimeAxisView> track;
875
876         switch (param.type()) {
877
878         case GainAutomation:
879                 create_gain_automation_child (param, show);
880                 break;
881
882         case PluginAutomation:
883                 /* handled elsewhere */
884                 break;
885
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 
895                  */
896
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)
908                                      ));
909
910                 if (_view) {
911                         _view->foreach_regionview (sigc::mem_fun (*track.get(), &TimeAxisView::add_ghost));
912                 }
913
914                 add_automation_child (param, track, show);
915                 break;
916
917         default:
918                 error << "MidiTimeAxisView: unknown automation child " << EventTypeMap::instance().to_symbol(param) << endmsg;
919         }
920 }
921
922 void
923 MidiTimeAxisView::route_active_changed ()
924 {
925         RouteUI::route_active_changed ();
926
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";
936                 }
937         } else {
938
939                 throw; // wha?
940
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";
949                 }
950         }
951 }
952
953
954
955 void
956 MidiTimeAxisView::add_note_selection (uint8_t note)
957 {
958         if (!_editor.internal_editing()) {
959                 return;
960         }
961
962         uint16_t chn_mask = _channel_selector.get_selected_channels();
963
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));
968         }
969 }
970
971 void
972 MidiTimeAxisView::extend_note_selection (uint8_t note)
973 {
974         if (!_editor.internal_editing()) {
975                 return;
976         }
977
978         uint16_t chn_mask = _channel_selector.get_selected_channels();
979
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));
984         }
985 }
986
987 void
988 MidiTimeAxisView::toggle_note_selection (uint8_t note)
989 {
990         if (!_editor.internal_editing()) {
991                 return;
992         }
993
994         uint16_t chn_mask = _channel_selector.get_selected_channels();
995
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));
1000         }
1001 }
1002
1003 void
1004 MidiTimeAxisView::add_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1005 {
1006         dynamic_cast<MidiRegionView*>(rv)->select_matching_notes (note, chn_mask, false, false);
1007 }
1008
1009 void
1010 MidiTimeAxisView::extend_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1011 {
1012         dynamic_cast<MidiRegionView*>(rv)->select_matching_notes (note, chn_mask, true, true);
1013 }
1014
1015 void
1016 MidiTimeAxisView::toggle_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1017 {
1018         dynamic_cast<MidiRegionView*>(rv)->toggle_matching_notes (note, chn_mask);
1019 }
1020
1021 void
1022 MidiTimeAxisView::set_channel_mode (ChannelMode, uint16_t)
1023 {
1024         /* hide all automation tracks that use the wrong channel(s) and show all those that use
1025            the right ones.
1026         */
1027
1028         uint16_t selected_channels = _channel_selector.get_selected_channels();
1029         bool changed = false;
1030
1031         no_redraw = true;
1032
1033         for (uint32_t ctl = 0; ctl < 127; ++ctl) {
1034
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);
1038
1039                         if (!track) {
1040                                 continue;
1041                         }
1042
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.
1046                                  */
1047                                 changed = track->set_marked_for_display (false) || changed;
1048                         } else {
1049                                 changed = track->set_marked_for_display (true) || changed;
1050                         }
1051                 }
1052         }
1053
1054         no_redraw = false;
1055
1056         /* TODO: Bender, Pressure */
1057
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;
1062
1063         if (changed) {
1064                 request_redraw ();
1065         }
1066 }
1067
1068 Gtk::CheckMenuItem*
1069 MidiTimeAxisView::automation_child_menu_item (Evoral::Parameter param)
1070 {
1071         Gtk::CheckMenuItem* m = RouteTimeAxisView::automation_child_menu_item (param);
1072         if (m) {
1073                 return m;
1074         }
1075
1076         ParameterMenuMap::iterator i = _controller_menu_map.find (param);
1077         if (i != _controller_menu_map.end()) {
1078                 return i->second;
1079         }
1080
1081         i = _channel_command_menu_map.find (param);
1082         if (i != _channel_command_menu_map.end()) {
1083                 return i->second;
1084         }
1085
1086         return 0;
1087 }
1088
1089 boost::shared_ptr<MidiRegion>
1090 MidiTimeAxisView::add_region (framepos_t pos, framecnt_t length, bool commit)
1091 {
1092         Editor* real_editor = dynamic_cast<Editor*> (&_editor);
1093
1094         real_editor->begin_reversible_command (Operations::create_region);
1095         playlist()->clear_changes ();
1096
1097         real_editor->snap_to (pos, 0);
1098
1099         boost::shared_ptr<Source> src = _session->create_midi_source_for_session (view()->trackview().track().get(),
1100                                                                                   view()->trackview().track()->name());
1101         PropertyList plist;
1102
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()));
1106
1107         boost::shared_ptr<Region> region = (RegionFactory::create (src, plist));
1108
1109         playlist()->add_region (region, pos);
1110         _session->add_command (new StatefulDiffCommand (playlist()));
1111
1112         if (commit) {
1113                 real_editor->commit_reversible_command ();
1114         }
1115
1116         return boost::dynamic_pointer_cast<MidiRegion>(region);
1117 }
1118
1119 void
1120 MidiTimeAxisView::ensure_step_editor ()
1121 {
1122         if (!_step_editor) {
1123                 _step_editor = new StepEditor (_editor, midi_track(), *this);
1124         }
1125 }
1126
1127 void
1128 MidiTimeAxisView::start_step_editing ()
1129 {
1130         ensure_step_editor ();
1131         _step_editor->start_step_editing ();
1132
1133 }
1134 void
1135 MidiTimeAxisView::stop_step_editing ()
1136 {
1137         if (_step_editor) {
1138                 _step_editor->stop_step_editing ();
1139         }
1140 }
1141
1142
1143 /** @return channel (counted from 0) to add an event to, based on the current setting
1144  *  of the channel selector.
1145  */
1146 uint8_t
1147 MidiTimeAxisView::get_channel_for_add () const
1148 {
1149         uint16_t const chn_mask = _channel_selector.get_selected_channels ();
1150         int chn_cnt = 0;
1151         uint8_t channel = 0;
1152
1153         /* pick the highest selected channel, unless all channels are selected,
1154            which is interpreted to mean channel 1 (zero)
1155         */
1156
1157         for (uint16_t i = 0; i < 16; ++i) {
1158                 if (chn_mask & (1<<i)) {
1159                         channel = i;
1160                         chn_cnt++;
1161                 }
1162         }
1163
1164         if (chn_cnt == 16) {
1165                 channel = 0;
1166         }
1167
1168         return channel;
1169 }
1170
1171 void
1172 MidiTimeAxisView::note_range_changed ()
1173 {
1174         set_gui_property ("note-range-min", (int) midi_view()->lowest_note ());
1175         set_gui_property ("note-range-max", (int) midi_view()->highest_note ());
1176 }