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