do not use "width" in VBAP panner when working with a single input channel
[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,
103                 boost::shared_ptr<Route> rt, Canvas& canvas)
104         : AxisView(sess) // virtually inherited
105         , RouteTimeAxisView(ed, sess, rt, 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         , default_channel_menu (0)
119         , controller_menu (0)
120         , _step_editor (0)
121 {
122         subplugin_menu.set_name ("ArdourContextMenu");
123
124         _view = new MidiStreamView (*this);
125
126         ignore_toggle = false;
127         
128         mute_button->set_active (false);
129         solo_button->set_active (false);
130
131         if (is_midi_track()) {
132                 controls_ebox.set_name ("MidiTimeAxisViewControlsBaseUnselected");
133                 _note_mode = midi_track()->note_mode();
134         } else { // MIDI bus (which doesn't exist yet..)
135                 controls_ebox.set_name ("MidiBusControlsBaseUnselected");
136         }
137
138         /* map current state of the route */
139
140         processors_changed (RouteProcessorChange ());
141
142         ensure_xml_node ();
143
144         set_state (*xml_node, Stateful::loading_state_version);
145
146         _route->processors_changed.connect (*this, invalidator (*this), ui_bind (&MidiTimeAxisView::processors_changed, this, _1), gui_context());
147
148         if (is_track()) {
149                 _piano_roll_header = new PianoRollHeader(*midi_view());
150
151                 _piano_roll_header->AddNoteSelection.connect (sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection));
152                 _piano_roll_header->ExtendNoteSelection.connect (sigc::mem_fun (*this, &MidiTimeAxisView::extend_note_selection));
153                 _piano_roll_header->ToggleNoteSelection.connect (sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection));
154
155                 _range_scroomer = new MidiScroomer(midi_view()->note_range_adjustment);
156
157                 controls_hbox.pack_start(*_range_scroomer);
158                 controls_hbox.pack_start(*_piano_roll_header);
159
160                 controls_ebox.set_name ("MidiTrackControlsBaseUnselected");
161                 controls_base_selected_name = "MidiTrackControlsBaseSelected";
162                 controls_base_unselected_name = "MidiTrackControlsBaseUnselected";
163
164                 midi_view()->NoteRangeChanged.connect (sigc::mem_fun(*this, &MidiTimeAxisView::update_range));
165
166                 /* ask for notifications of any new RegionViews */
167                 _view->RegionViewAdded.connect (sigc::mem_fun(*this, &MidiTimeAxisView::region_view_added));
168                 
169                 if (!_editor.have_idled()) {
170                         /* first idle will do what we need */
171                 } else {
172                         first_idle ();
173                 }
174         }
175
176         HBox* midi_controls_hbox = manage(new HBox());
177
178         MIDI::Name::MidiPatchManager& patch_manager = MIDI::Name::MidiPatchManager::instance();
179
180         MIDI::Name::MasterDeviceNames::Models::const_iterator m = patch_manager.all_models().begin();
181         for (; m != patch_manager.all_models().end(); ++m) {
182                 _model_selector.append_text(m->c_str());
183         }
184
185         _model_selector.signal_changed().connect(sigc::mem_fun(*this, &MidiTimeAxisView::model_changed));
186
187         _custom_device_mode_selector.signal_changed().connect(
188                         sigc::mem_fun(*this, &MidiTimeAxisView::custom_device_mode_changed));
189
190         // TODO: persist the choice
191         // this initializes the comboboxes and sends out the signal
192         _model_selector.set_active(0);
193
194         midi_controls_hbox->pack_start(_channel_selector, true, false);
195         if (!patch_manager.all_models().empty()) {
196                 _midi_controls_box.pack_start(_model_selector, true, false);
197                 _midi_controls_box.pack_start(_custom_device_mode_selector, true, false);
198         }
199
200         _midi_controls_box.pack_start(*midi_controls_hbox, true, true);
201
202         controls_vbox.pack_start(_midi_controls_box, false, false);
203
204         // restore channel selector settings
205         _channel_selector.set_channel_mode(midi_track()->get_channel_mode(), midi_track()->get_channel_mask());
206         _channel_selector.mode_changed.connect(
207                 sigc::mem_fun(*midi_track(), &MidiTrack::set_channel_mode));
208         _channel_selector.mode_changed.connect(
209                 sigc::mem_fun(*this, &MidiTimeAxisView::set_channel_mode));
210
211         XMLProperty *prop;
212         if ((prop = xml_node->property ("color-mode")) != 0) {
213                 _color_mode = ColorMode (string_2_enum(prop->value(), _color_mode));
214                 if (_color_mode == ChannelColors) {
215                         _channel_selector.set_channel_colors(CanvasNoteEvent::midi_channel_colors);
216                 }
217         }
218
219         if ((prop = xml_node->property ("note-mode")) != 0) {
220                 _note_mode = NoteMode (string_2_enum(prop->value(), _note_mode));
221                 if (_percussion_mode_item) {
222                         _percussion_mode_item->set_active (_note_mode == Percussive);
223                 }
224         }
225 }
226
227 void
228 MidiTimeAxisView::first_idle ()
229 {
230         if (is_track ()) {
231                 _view->attach ();
232         }
233 }
234
235 MidiTimeAxisView::~MidiTimeAxisView ()
236 {
237         delete _piano_roll_header;
238         _piano_roll_header = 0;
239
240         delete _range_scroomer;
241         _range_scroomer = 0;
242
243         delete controller_menu;
244         delete _step_editor;
245 }
246
247 void
248 MidiTimeAxisView::check_step_edit ()
249 {
250         ensure_step_editor ();
251         _step_editor->check_step_edit ();
252 }
253
254 void 
255 MidiTimeAxisView::model_changed()
256 {
257         std::list<std::string> device_modes = MIDI::Name::MidiPatchManager::instance()
258                 .custom_device_mode_names_by_model(_model_selector.get_active_text());
259
260         _custom_device_mode_selector.clear_items();
261
262         for (std::list<std::string>::const_iterator i = device_modes.begin();
263                         i != device_modes.end(); ++i) {
264                 cerr << "found custom device mode " << *i << " thread_id: " << pthread_self() << endl;
265                 _custom_device_mode_selector.append_text(*i);
266         }
267
268         _custom_device_mode_selector.set_active(0);
269 }
270
271 void MidiTimeAxisView::custom_device_mode_changed()
272 {
273         _midi_patch_settings_changed.emit(_model_selector.get_active_text(),
274                         _custom_device_mode_selector.get_active_text());
275 }
276
277 MidiStreamView*
278 MidiTimeAxisView::midi_view()
279 {
280         return dynamic_cast<MidiStreamView*>(_view);
281 }
282
283 guint32
284 MidiTimeAxisView::show_at (double y, int& nth, Gtk::VBox *parent)
285 {
286         ensure_xml_node ();
287         xml_node->add_property ("shown-editor", "yes");
288
289         guint32 ret = TimeAxisView::show_at (y, nth, parent);
290         return ret;
291 }
292
293 void
294 MidiTimeAxisView::hide ()
295 {
296         ensure_xml_node ();
297         xml_node->add_property ("shown-editor", "no");
298
299         TimeAxisView::hide ();
300 }
301
302 void
303 MidiTimeAxisView::set_height (uint32_t h)
304 {
305         RouteTimeAxisView::set_height (h);
306
307         if (height >= MIDI_CONTROLS_BOX_MIN_HEIGHT) {
308                 _midi_controls_box.show_all ();
309         } else {
310                 _midi_controls_box.hide();
311         }
312
313         if (height >= KEYBOARD_MIN_HEIGHT) {
314                 if (is_track() && _range_scroomer)
315                         _range_scroomer->show();
316                 if (is_track() && _piano_roll_header)
317                         _piano_roll_header->show();
318         } else {
319                 if (is_track() && _range_scroomer)
320                         _range_scroomer->hide();
321                 if (is_track() && _piano_roll_header)
322                         _piano_roll_header->hide();
323         }
324 }
325
326 void
327 MidiTimeAxisView::append_extra_display_menu_items ()
328 {
329         using namespace Menu_Helpers;
330
331         MenuList& items = display_menu->items();
332
333         // Note range
334         Menu *range_menu = manage(new Menu);
335         MenuList& range_items = range_menu->items();
336         range_menu->set_name ("ArdourContextMenu");
337
338         range_items.push_back (MenuElem (_("Show Full Range"), sigc::bind (
339                         sigc::mem_fun(*this, &MidiTimeAxisView::set_note_range),
340                         MidiStreamView::FullRange)));
341
342         range_items.push_back (MenuElem (_("Fit Contents"), sigc::bind (
343                         sigc::mem_fun(*this, &MidiTimeAxisView::set_note_range),
344                         MidiStreamView::ContentsRange)));
345
346         items.push_back (MenuElem (_("Note range"), *range_menu));
347         items.push_back (MenuElem (_("Note mode"), *build_note_mode_menu()));
348         items.push_back (MenuElem (_("Default Channel"), *build_def_channel_menu()));
349
350         items.push_back (CheckMenuElem (_("MIDI Thru"), sigc::mem_fun(*this, &MidiTimeAxisView::toggle_midi_thru)));
351         _midi_thru_item = dynamic_cast<CheckMenuItem*>(&items.back());
352
353         items.push_back (SeparatorElem ());
354 }
355
356 Gtk::Menu*
357 MidiTimeAxisView::build_def_channel_menu ()
358 {
359         using namespace Menu_Helpers;
360
361         default_channel_menu = manage (new Menu ());
362
363         uint8_t defchn = midi_track()->default_channel();
364         MenuList& def_channel_items = default_channel_menu->items();
365         RadioMenuItem* item;
366         RadioMenuItem::Group dc_group;
367
368         for (int i = 0; i < 16; ++i) {
369                 char buf[4];
370                 snprintf (buf, sizeof (buf), "%d", i+1);
371
372                 def_channel_items.push_back (RadioMenuElem (dc_group, buf,
373                                                             sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_default_channel), i)));
374                 item = dynamic_cast<RadioMenuItem*>(&def_channel_items.back());
375                 item->set_active ((i == defchn));
376         }
377
378         return default_channel_menu;
379 }
380
381 void
382 MidiTimeAxisView::set_default_channel (int chn)
383 {
384         midi_track()->set_default_channel (chn);
385 }
386
387 void
388 MidiTimeAxisView::toggle_midi_thru ()
389 {
390         if (!_midi_thru_item) {
391                 return;
392         }
393
394         bool view_yn = _midi_thru_item->get_active();
395         if (view_yn != midi_track()->midi_thru()) {
396                 midi_track()->set_midi_thru (view_yn);
397         }
398 }
399
400 void
401 MidiTimeAxisView::build_automation_action_menu ()
402 {
403         using namespace Menu_Helpers;
404
405         /* If we have a controller menu, we need to detach it before
406            RouteTimeAxis::build_automation_action_menu destroys the
407            menu it is attached to.  Otherwise GTK destroys
408            controller_menu's gobj, meaning that it can't be reattached
409            below.  See bug #3134.
410         */
411            
412         if (controller_menu) {
413                 detach_menu (*controller_menu);
414         }
415
416         _channel_command_menu_map.clear ();
417         RouteTimeAxisView::build_automation_action_menu ();
418
419         MenuList& automation_items = automation_action_menu->items();
420         
421         uint16_t selected_channels = _channel_selector.get_selected_channels();
422
423         if (selected_channels !=  0) {
424
425                 automation_items.push_back (SeparatorElem());
426
427                 /* these 2 MIDI "command" types are semantically more like automation than note data,
428                    but they are not MIDI controllers. We give them special status in this menu, since
429                    they will not show up in the controller list and anyone who actually knows
430                    something about MIDI (!) would not expect to find them there.
431                 */
432
433                 add_channel_command_menu_item (automation_items, _("Bender"), MidiPitchBenderAutomation, 0);
434                 add_channel_command_menu_item (automation_items, _("Pressure"), MidiChannelPressureAutomation, 0);
435                 
436                 /* now all MIDI controllers. Always offer the possibility that we will rebuild the controllers menu
437                    since it might need to be updated after a channel mode change or other change. Also detach it
438                    first in case it has been used anywhere else.
439                 */
440                 
441                 build_controller_menu ();
442                 
443                 automation_items.push_back (SeparatorElem());
444                 automation_items.push_back (MenuElem (_("Controllers"), *controller_menu));
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), MeterColors)));
740         _meter_color_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
741         _meter_color_mode_item->set_active(_color_mode == MeterColors);
742
743         items.push_back (RadioMenuElem (mode_group, _("Channel Colors"),
744                                 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode), ChannelColors)));
745         _channel_color_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
746         _channel_color_mode_item->set_active(_color_mode == ChannelColors);
747
748         items.push_back (RadioMenuElem (mode_group, _("Track Color"),
749                                 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode), TrackColor)));
750         _channel_color_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
751         _channel_color_mode_item->set_active(_color_mode == TrackColor);
752
753         return mode_menu;
754 }
755
756 void
757 MidiTimeAxisView::set_note_mode(NoteMode mode)
758 {
759         if (_note_mode != mode || midi_track()->note_mode() != mode) {
760                 _note_mode = mode;
761                 midi_track()->set_note_mode(mode);
762                 xml_node->add_property ("note-mode", enum_2_string(_note_mode));
763                 _view->redisplay_track();
764         }
765 }
766
767 void
768 MidiTimeAxisView::set_color_mode(ColorMode mode)
769 {
770         if (_color_mode != mode) {
771                 if (mode == ChannelColors) {
772                         _channel_selector.set_channel_colors(CanvasNoteEvent::midi_channel_colors);
773                 } else {
774                         _channel_selector.set_default_channel_color();
775                 }
776
777                 _color_mode = mode;
778                 xml_node->add_property ("color-mode", enum_2_string(_color_mode));
779                 _view->redisplay_track();
780         }
781 }
782
783 void
784 MidiTimeAxisView::set_note_range(MidiStreamView::VisibleNoteRange range)
785 {
786         if (!_ignore_signals)
787                 midi_view()->set_note_range(range);
788 }
789
790
791 void
792 MidiTimeAxisView::update_range()
793 {
794         MidiGhostRegion* mgr;
795
796         for(list<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
797                 if ((mgr = dynamic_cast<MidiGhostRegion*>(*i)) != 0) {
798                         mgr->update_range();
799                 }
800         }
801 }
802
803 void
804 MidiTimeAxisView::show_all_automation ()
805 {
806         if (midi_track()) {
807                 const set<Evoral::Parameter> params = midi_track()->midi_playlist()->contained_automation();
808
809                 for (set<Evoral::Parameter>::const_iterator i = params.begin(); i != params.end(); ++i) {
810                         create_automation_child(*i, true);
811                 }
812         }
813
814         RouteTimeAxisView::show_all_automation ();
815 }
816
817 void
818 MidiTimeAxisView::show_existing_automation ()
819 {
820         if (midi_track()) {
821                 const set<Evoral::Parameter> params = midi_track()->midi_playlist()->contained_automation();
822
823                 for (set<Evoral::Parameter>::const_iterator i = params.begin(); i != params.end(); ++i) {
824                         create_automation_child(*i, true);
825                 }
826         }
827
828         RouteTimeAxisView::show_existing_automation ();
829 }
830
831 /** Create an automation track for the given parameter (pitch bend, channel pressure).
832  */
833 void
834 MidiTimeAxisView::create_automation_child (const Evoral::Parameter& param, bool show)
835 {
836         if (param.type() == NullAutomation) {
837                 cerr << "WARNING: Attempt to create NullAutomation child, ignoring" << endl;
838                 return;
839         }
840         
841         AutomationTracks::iterator existing = _automation_tracks.find (param);
842         if (existing != _automation_tracks.end()) {
843                 return;
844         }
845                 
846         if (param.type() == GainAutomation) {
847                 create_gain_automation_child (param, show);
848         } else {
849                 
850                 /* These controllers are region "automation", so we do not create
851                  * an AutomationList/Line for the track */
852                 
853                 boost::shared_ptr<AutomationTimeAxisView> track (
854                         new AutomationTimeAxisView (
855                                 _session,
856                                 _route, 
857                                 boost::shared_ptr<Automatable> (),
858                                 boost::shared_ptr<AutomationControl> (),
859                                 param,
860                                 _editor,
861                                 *this,
862                                 true,
863                                 parent_canvas,
864                                 _route->describe_parameter(param)
865                                 )
866                         );
867                 
868                 add_automation_child (param, track, show);
869         }
870 }
871
872
873 void
874 MidiTimeAxisView::route_active_changed ()
875 {
876         RouteUI::route_active_changed ();
877
878         if (is_track()) {
879                 if (_route->active()) {
880                         controls_ebox.set_name ("MidiTrackControlsBaseUnselected");
881                         controls_base_selected_name = "MidiTrackControlsBaseSelected";
882                         controls_base_unselected_name = "MidiTrackControlsBaseUnselected";
883                 } else {
884                         controls_ebox.set_name ("MidiTrackControlsBaseInactiveUnselected");
885                         controls_base_selected_name = "MidiTrackControlsBaseInactiveSelected";
886                         controls_base_unselected_name = "MidiTrackControlsBaseInactiveUnselected";
887                 }
888         } else {
889
890                 throw; // wha?
891
892                 if (_route->active()) {
893                         controls_ebox.set_name ("BusControlsBaseUnselected");
894                         controls_base_selected_name = "BusControlsBaseSelected";
895                         controls_base_unselected_name = "BusControlsBaseUnselected";
896                 } else {
897                         controls_ebox.set_name ("BusControlsBaseInactiveUnselected");
898                         controls_base_selected_name = "BusControlsBaseInactiveSelected";
899                         controls_base_unselected_name = "BusControlsBaseInactiveUnselected";
900                 }
901         }
902 }
903
904
905
906 void
907 MidiTimeAxisView::add_note_selection (uint8_t note)
908 {
909         if (!_editor.internal_editing()) {
910                 return;
911         }
912
913         uint16_t chn_mask = _channel_selector.get_selected_channels();
914
915         if (_view->num_selected_regionviews() == 0) {
916                 _view->foreach_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection_region_view), note, chn_mask));
917         } else {
918                 _view->foreach_selected_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection_region_view), note, chn_mask));
919         }
920 }
921
922 void
923 MidiTimeAxisView::extend_note_selection (uint8_t note)
924 {
925         if (!_editor.internal_editing()) {
926                 return;
927         }
928
929         uint16_t chn_mask = _channel_selector.get_selected_channels();
930
931         if (_view->num_selected_regionviews() == 0) {
932                 _view->foreach_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::extend_note_selection_region_view), note, chn_mask));
933         } else {
934                 _view->foreach_selected_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::extend_note_selection_region_view), note, chn_mask));
935         }
936 }
937
938 void
939 MidiTimeAxisView::toggle_note_selection (uint8_t note)
940 {
941         if (!_editor.internal_editing()) {
942                 return;
943         }
944
945         uint16_t chn_mask = _channel_selector.get_selected_channels();
946
947         if (_view->num_selected_regionviews() == 0) {
948                 _view->foreach_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection_region_view), note, chn_mask));
949         } else {
950                 _view->foreach_selected_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection_region_view), note, chn_mask));
951         }
952 }
953
954 void
955 MidiTimeAxisView::add_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
956 {
957         dynamic_cast<MidiRegionView*>(rv)->select_matching_notes (note, chn_mask, false, false);
958 }
959
960 void
961 MidiTimeAxisView::extend_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
962 {
963         dynamic_cast<MidiRegionView*>(rv)->select_matching_notes (note, chn_mask, true, true);
964 }
965
966 void
967 MidiTimeAxisView::toggle_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
968 {
969         dynamic_cast<MidiRegionView*>(rv)->toggle_matching_notes (note, chn_mask);
970 }
971
972 void
973 MidiTimeAxisView::set_channel_mode (ChannelMode, uint16_t)
974 {
975         /* hide all automation tracks that use the wrong channel(s) and show all those that use
976            the right ones.
977         */
978
979         uint16_t selected_channels = _channel_selector.get_selected_channels();
980         bool changed = false;
981
982         no_redraw = true;
983
984         for (uint32_t ctl = 0; ctl < 127; ++ctl) {
985
986                 for (uint32_t chn = 0; chn < 16; ++chn) {
987                         Evoral::Parameter fully_qualified_param (MidiCCAutomation, chn, ctl);
988                         boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
989
990                         if (!track) {
991                                 continue;
992                         }
993                         
994                         if ((selected_channels & (0x0001 << chn)) == 0) {
995                                 /* channel not in use. hiding it will trigger RouteTimeAxisView::automation_track_hidden() 
996                                    which will cause a redraw. We don't want one per channel, so block that with no_redraw.
997                                  */
998                                 changed = track->set_visibility (false) || changed;
999                         } else {
1000                                 changed = track->set_visibility (true) || changed;
1001                         }
1002                 }
1003         }
1004
1005         no_redraw = false;
1006
1007         /* TODO: Bender, Pressure */
1008
1009         /* invalidate the controller menu, so that we rebuild it next time */
1010         _controller_menu_map.clear ();
1011         delete controller_menu;
1012         controller_menu = 0;
1013
1014         if (changed) {
1015                 _route->gui_changed ("track_height", this);
1016         }
1017 }
1018
1019 Gtk::CheckMenuItem*
1020 MidiTimeAxisView::automation_child_menu_item (Evoral::Parameter param)
1021 {
1022         Gtk::CheckMenuItem* m = RouteTimeAxisView::automation_child_menu_item (param);
1023         if (m) {
1024                 return m;
1025         }
1026
1027         ParameterMenuMap::iterator i = _controller_menu_map.find (param);
1028         if (i != _controller_menu_map.end()) {
1029                 return i->second;
1030         }
1031
1032         i = _channel_command_menu_map.find (param);
1033         if (i != _channel_command_menu_map.end()) {
1034                 return i->second;
1035         }
1036
1037         return 0;
1038 }
1039
1040 boost::shared_ptr<MidiRegion>
1041 MidiTimeAxisView::add_region (framepos_t pos, framecnt_t length, bool commit)
1042 {
1043         Editor* real_editor = dynamic_cast<Editor*> (&_editor);
1044
1045         real_editor->begin_reversible_command (Operations::create_region);
1046         playlist()->clear_changes ();
1047
1048         real_editor->snap_to (pos, 0);
1049         
1050         boost::shared_ptr<Source> src = _session->create_midi_source_for_session (view()->trackview().track().get(),
1051                                                                                   view()->trackview().track()->name());
1052         PropertyList plist; 
1053         
1054         plist.add (ARDOUR::Properties::start, 0);
1055         plist.add (ARDOUR::Properties::length, length);
1056         plist.add (ARDOUR::Properties::name, PBD::basename_nosuffix(src->name()));
1057         
1058         boost::shared_ptr<Region> region = (RegionFactory::create (src, plist));
1059         
1060         playlist()->add_region (region, pos);
1061         _session->add_command (new StatefulDiffCommand (playlist()));
1062
1063         if (commit) {
1064                 real_editor->commit_reversible_command ();
1065         }
1066
1067         return boost::dynamic_pointer_cast<MidiRegion>(region);
1068 }
1069
1070 void
1071 MidiTimeAxisView::ensure_step_editor ()
1072 {
1073         if (!_step_editor) {
1074                 _step_editor = new StepEditor (_editor, midi_track(), *this);
1075         }
1076 }
1077
1078 void 
1079 MidiTimeAxisView::start_step_editing ()
1080 {
1081         ensure_step_editor ();
1082         _step_editor->start_step_editing ();
1083
1084 }
1085 void 
1086 MidiTimeAxisView::stop_step_editing ()
1087 {
1088         if (_step_editor) {
1089                 _step_editor->stop_step_editing ();
1090         }
1091 }