update drobilla's fascistic dir-locals.el to force emacs users into whitespace submis...
[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::enter_internal_edit_mode ()
249 {
250         if (midi_view()) {
251                 midi_view()->enter_internal_edit_mode ();
252         } 
253 }
254
255 void
256 MidiTimeAxisView::leave_internal_edit_mode ()
257 {
258         if (midi_view()) {
259                 midi_view()->leave_internal_edit_mode ();
260         }
261 }
262
263 void
264 MidiTimeAxisView::check_step_edit ()
265 {
266         ensure_step_editor ();
267         _step_editor->check_step_edit ();
268 }
269
270 void 
271 MidiTimeAxisView::model_changed()
272 {
273         std::list<std::string> device_modes = MIDI::Name::MidiPatchManager::instance()
274                 .custom_device_mode_names_by_model(_model_selector.get_active_text());
275
276         _custom_device_mode_selector.clear_items();
277
278         for (std::list<std::string>::const_iterator i = device_modes.begin();
279                         i != device_modes.end(); ++i) {
280                 cerr << "found custom device mode " << *i << " thread_id: " << pthread_self() << endl;
281                 _custom_device_mode_selector.append_text(*i);
282         }
283
284         _custom_device_mode_selector.set_active(0);
285 }
286
287 void MidiTimeAxisView::custom_device_mode_changed()
288 {
289         _midi_patch_settings_changed.emit(_model_selector.get_active_text(),
290                         _custom_device_mode_selector.get_active_text());
291 }
292
293 MidiStreamView*
294 MidiTimeAxisView::midi_view()
295 {
296         return dynamic_cast<MidiStreamView*>(_view);
297 }
298
299 guint32
300 MidiTimeAxisView::show_at (double y, int& nth, Gtk::VBox *parent)
301 {
302         ensure_xml_node ();
303         xml_node->add_property ("shown-editor", "yes");
304
305         guint32 ret = TimeAxisView::show_at (y, nth, parent);
306         return ret;
307 }
308
309 void
310 MidiTimeAxisView::hide ()
311 {
312         ensure_xml_node ();
313         xml_node->add_property ("shown-editor", "no");
314
315         TimeAxisView::hide ();
316 }
317
318 void
319 MidiTimeAxisView::set_height (uint32_t h)
320 {
321         RouteTimeAxisView::set_height (h);
322
323         if (height >= MIDI_CONTROLS_BOX_MIN_HEIGHT) {
324                 _midi_controls_box.show_all ();
325         } else {
326                 _midi_controls_box.hide();
327         }
328
329         if (height >= KEYBOARD_MIN_HEIGHT) {
330                 if (is_track() && _range_scroomer)
331                         _range_scroomer->show();
332                 if (is_track() && _piano_roll_header)
333                         _piano_roll_header->show();
334         } else {
335                 if (is_track() && _range_scroomer)
336                         _range_scroomer->hide();
337                 if (is_track() && _piano_roll_header)
338                         _piano_roll_header->hide();
339         }
340 }
341
342 void
343 MidiTimeAxisView::append_extra_display_menu_items ()
344 {
345         using namespace Menu_Helpers;
346
347         MenuList& items = display_menu->items();
348
349         // Note range
350         Menu *range_menu = manage(new Menu);
351         MenuList& range_items = range_menu->items();
352         range_menu->set_name ("ArdourContextMenu");
353
354         range_items.push_back (MenuElem (_("Show Full Range"), sigc::bind (
355                         sigc::mem_fun(*this, &MidiTimeAxisView::set_note_range),
356                         MidiStreamView::FullRange)));
357
358         range_items.push_back (MenuElem (_("Fit Contents"), sigc::bind (
359                         sigc::mem_fun(*this, &MidiTimeAxisView::set_note_range),
360                         MidiStreamView::ContentsRange)));
361
362         items.push_back (MenuElem (_("Note range"), *range_menu));
363         items.push_back (MenuElem (_("Note mode"), *build_note_mode_menu()));
364         items.push_back (MenuElem (_("Default Channel"), *build_def_channel_menu()));
365
366         items.push_back (CheckMenuElem (_("MIDI Thru"), sigc::mem_fun(*this, &MidiTimeAxisView::toggle_midi_thru)));
367         _midi_thru_item = dynamic_cast<CheckMenuItem*>(&items.back());
368
369         items.push_back (SeparatorElem ());
370 }
371
372 Gtk::Menu*
373 MidiTimeAxisView::build_def_channel_menu ()
374 {
375         using namespace Menu_Helpers;
376
377         default_channel_menu = manage (new Menu ());
378
379         uint8_t defchn = midi_track()->default_channel();
380         MenuList& def_channel_items = default_channel_menu->items();
381         RadioMenuItem* item;
382         RadioMenuItem::Group dc_group;
383
384         for (int i = 0; i < 16; ++i) {
385                 char buf[4];
386                 snprintf (buf, sizeof (buf), "%d", i+1);
387
388                 def_channel_items.push_back (RadioMenuElem (dc_group, buf,
389                                                             sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_default_channel), i)));
390                 item = dynamic_cast<RadioMenuItem*>(&def_channel_items.back());
391                 item->set_active ((i == defchn));
392         }
393
394         return default_channel_menu;
395 }
396
397 void
398 MidiTimeAxisView::set_default_channel (int chn)
399 {
400         midi_track()->set_default_channel (chn);
401 }
402
403 void
404 MidiTimeAxisView::toggle_midi_thru ()
405 {
406         if (!_midi_thru_item) {
407                 return;
408         }
409
410         bool view_yn = _midi_thru_item->get_active();
411         if (view_yn != midi_track()->midi_thru()) {
412                 midi_track()->set_midi_thru (view_yn);
413         }
414 }
415
416 void
417 MidiTimeAxisView::build_automation_action_menu (bool for_selection)
418 {
419         using namespace Menu_Helpers;
420
421         /* If we have a controller menu, we need to detach it before
422            RouteTimeAxis::build_automation_action_menu destroys the
423            menu it is attached to.  Otherwise GTK destroys
424            controller_menu's gobj, meaning that it can't be reattached
425            below.  See bug #3134.
426         */
427            
428         if (controller_menu) {
429                 detach_menu (*controller_menu);
430         }
431
432         _channel_command_menu_map.clear ();
433         RouteTimeAxisView::build_automation_action_menu (for_selection);
434
435         MenuList& automation_items = automation_action_menu->items();
436         
437         uint16_t selected_channels = _channel_selector.get_selected_channels();
438
439         if (selected_channels !=  0) {
440
441                 automation_items.push_back (SeparatorElem());
442
443                 /* these 2 MIDI "command" types are semantically more like automation than note data,
444                    but they are not MIDI controllers. We give them special status in this menu, since
445                    they will not show up in the controller list and anyone who actually knows
446                    something about MIDI (!) would not expect to find them there.
447                 */
448
449                 add_channel_command_menu_item (automation_items, _("Bender"), MidiPitchBenderAutomation, 0);
450                 automation_items.back().set_sensitive (!for_selection || _editor.get_selection().tracks.size() == 1);
451                 add_channel_command_menu_item (automation_items, _("Pressure"), MidiChannelPressureAutomation, 0);
452                 automation_items.back().set_sensitive (!for_selection || _editor.get_selection().tracks.size() == 1);
453                 
454                 /* now all MIDI controllers. Always offer the possibility that we will rebuild the controllers menu
455                    since it might need to be updated after a channel mode change or other change. Also detach it
456                    first in case it has been used anywhere else.
457                 */
458                 
459                 build_controller_menu ();
460                 
461                 automation_items.push_back (SeparatorElem());
462                 automation_items.push_back (MenuElem (_("Controllers"), *controller_menu));
463                 automation_items.back().set_sensitive (!for_selection || _editor.get_selection().tracks.size() == 1);
464         } else {
465                 automation_items.push_back (MenuElem (string_compose ("<i>%1</i>", _("No MIDI Channels selected"))));
466                 dynamic_cast<Label*> (automation_items.back().get_child())->set_use_markup (true);
467         }
468                 
469 }
470
471 void
472 MidiTimeAxisView::change_all_channel_tracks_visibility (bool yn, Evoral::Parameter param)
473 {
474         uint16_t selected_channels = _channel_selector.get_selected_channels();
475         
476         for (uint8_t chn = 0; chn < 16; chn++) {
477                 if (selected_channels & (0x0001 << chn)) {
478                         
479                         Evoral::Parameter fully_qualified_param (param.type(), chn, param.id());
480                         Gtk::CheckMenuItem* menu = automation_child_menu_item (fully_qualified_param);
481
482                         if (menu) {
483                                 menu->set_active (yn);
484                         }
485                 }
486         }
487 }
488
489 void
490 MidiTimeAxisView::add_channel_command_menu_item (Menu_Helpers::MenuList& items, const string& label, AutomationType auto_type, uint8_t cmd)
491 {
492         using namespace Menu_Helpers;
493
494         /* count the number of selected channels because we will build a different menu structure if there is more than 1 selected.
495          */
496
497         uint16_t selected_channels = _channel_selector.get_selected_channels();
498         int chn_cnt = 0;
499         
500         for (uint8_t chn = 0; chn < 16; chn++) {
501                 if (selected_channels & (0x0001 << chn)) {
502                         if (++chn_cnt > 1) {
503                                 break;
504                         }
505                 }
506         }
507         
508         if (chn_cnt > 1) {
509                 
510                 /* multiple channels - create a submenu, with 1 item per channel */
511                 
512                 Menu* chn_menu = manage (new Menu);
513                 MenuList& chn_items (chn_menu->items());
514                 Evoral::Parameter param_without_channel (auto_type, 0, cmd);
515
516                 /* add a couple of items to hide/show all of them */
517
518                 chn_items.push_back (MenuElem (_("Hide all channels"),
519                                                     sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility), 
520                                                                 false, param_without_channel)));
521                 chn_items.push_back (MenuElem (_("Show all channels"),
522                                                     sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility), 
523                                                                 true, param_without_channel)));
524                 
525                 for (uint8_t chn = 0; chn < 16; chn++) {
526                         if (selected_channels & (0x0001 << chn)) {
527                                 
528                                 /* for each selected channel, add a menu item for this controller */
529                                 
530                                 Evoral::Parameter fully_qualified_param (auto_type, chn, cmd);
531                                 chn_items.push_back (CheckMenuElem (string_compose (_("Channel %1"), chn+1),
532                                                                     sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
533                                                                                 fully_qualified_param)));
534                                 
535                                 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
536                                 bool visible = false;
537                                 
538                                 if (track) {
539                                         if (track->marked_for_display()) {
540                                                 visible = true;
541                                         }
542                                 }
543
544                                 CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&chn_items.back());
545                                 _channel_command_menu_map[fully_qualified_param] = cmi;
546                                 cmi->set_active (visible);
547                         }
548                 }
549                 
550                 /* now create an item in the parent menu that has the per-channel list as a submenu */
551                         
552                 items.push_back (MenuElem (label, *chn_menu));
553                 
554         } else {
555                 
556                 /* just one channel - create a single menu item for this command+channel combination*/
557                 
558                 for (uint8_t chn = 0; chn < 16; chn++) {
559                         if (selected_channels & (0x0001 << chn)) {
560                                 
561                                 Evoral::Parameter fully_qualified_param (auto_type, chn, cmd);
562                                 items.push_back (CheckMenuElem (label,
563                                                                 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
564                                                                             fully_qualified_param)));
565                                 
566                                 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
567                                 bool visible = false;
568                                 
569                                 if (track) {
570                                         if (track->marked_for_display()) {
571                                                 visible = true;
572                                         }
573                                 }
574                                 
575                                 CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&items.back());
576                                 _channel_command_menu_map[fully_qualified_param] = cmi;
577                                 cmi->set_active (visible);
578                                 
579                                 /* one channel only */
580                                 break;
581                         }
582                 }
583         }
584 }
585
586 void
587 MidiTimeAxisView::build_controller_menu ()
588 {
589         using namespace Menu_Helpers;
590
591         if (controller_menu) {
592                 /* it exists and has not been invalidated by a channel mode change, so just return it */
593                 return;
594         }
595
596         controller_menu = new Menu; // explicitly managed by us
597         MenuList& items (controller_menu->items());
598
599         /* create several "top level" menu items for sets of controllers (16 at a time), and populate each one with a submenu 
600            for each controller+channel combination covering the currently selected channels for this track
601         */
602
603         uint16_t selected_channels = _channel_selector.get_selected_channels();
604
605         /* count the number of selected channels because we will build a different menu structure if there is more than 1 selected.
606          */
607
608         int chn_cnt = 0;
609         
610         for (uint8_t chn = 0; chn < 16; chn++) {
611                 if (selected_channels & (0x0001 << chn)) {
612                         if (++chn_cnt > 1) {
613                                 break;
614                         }
615                 }
616         }
617         
618         /* loop over all 127 MIDI controllers, in groups of 16; except don't offer
619            bank select controllers, as they are handled by the `patch' code */
620
621         for (int i = 0; i < 127; i += 16) {
622
623                 Menu* ctl_menu = manage (new Menu);
624                 MenuList& ctl_items (ctl_menu->items());
625
626
627                 /* for each controller, consider whether to create a submenu or a single item */
628
629                 for (int ctl = i; ctl < i+16; ++ctl) {
630
631                         if (ctl == MIDI_CTL_MSB_BANK || ctl == MIDI_CTL_LSB_BANK) {
632                                 continue;
633                         }
634
635                         if (chn_cnt > 1) {
636
637                                 /* multiple channels - create a submenu, with 1 item per channel */
638
639                                 Menu* chn_menu = manage (new Menu);
640                                 MenuList& chn_items (chn_menu->items());
641
642                                 /* add a couple of items to hide/show this controller on all channels */
643                                 
644                                 Evoral::Parameter param_without_channel (MidiCCAutomation, 0, ctl);
645                                 chn_items.push_back (MenuElem (_("Hide all channels"),
646                                                                     sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility), 
647                                                                                 false, param_without_channel)));
648                                 chn_items.push_back (MenuElem (_("Show all channels"),
649                                                                     sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility), 
650                                                                                 true, param_without_channel)));
651                 
652                                 for (uint8_t chn = 0; chn < 16; chn++) {
653                                         if (selected_channels & (0x0001 << chn)) {
654                                                 
655                                                 /* for each selected channel, add a menu item for this controller */
656                                                 
657                                                 Evoral::Parameter fully_qualified_param (MidiCCAutomation, chn, ctl);
658                                                 chn_items.push_back (CheckMenuElem (string_compose (_("Channel %1"), chn+1),
659                                                                                     sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
660                                                                                                 fully_qualified_param)));
661
662                                                 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
663                                                 bool visible = false;
664                                                 
665                                                 if (track) {
666                                                         if (track->marked_for_display()) {
667                                                                 visible = true;
668                                                         }
669                                                 }
670                                                 
671                                                 CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&chn_items.back());
672                                                 _controller_menu_map[fully_qualified_param] = cmi;
673                                                 cmi->set_active (visible);
674                                         }
675                                 }
676                                 
677                                 /* add the per-channel menu to the list of controllers, with the name of the controller */
678                                 ctl_items.push_back (MenuElem (string_compose ("<b>%1</b>: %2", ctl, midi_name (ctl)), *chn_menu));
679                                 dynamic_cast<Label*> (ctl_items.back().get_child())->set_use_markup (true);
680                                       
681                         } else {
682
683                                 /* just one channel - create a single menu item for this ctl+channel combination*/
684
685                                 for (uint8_t chn = 0; chn < 16; chn++) {
686                                         if (selected_channels & (0x0001 << chn)) {
687                                                 
688                                                 Evoral::Parameter fully_qualified_param (MidiCCAutomation, chn, ctl);
689                                                 ctl_items.push_back (
690                                                         CheckMenuElem (
691                                                                 string_compose ("<b>%1</b>: %2 [%3]", ctl, midi_name (ctl), int (chn)),
692                                                                 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
693                                                                             fully_qualified_param)
694                                                                 )
695                                                         );
696                                                 dynamic_cast<Label*> (ctl_items.back().get_child())->set_use_markup (true);
697                                                 
698                                                 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
699                                                 bool visible = false;
700                                                 
701                                                 if (track) {
702                                                         if (track->marked_for_display()) {
703                                                                 visible = true;
704                                                         }
705                                                 }
706                                                 
707                                                 CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&ctl_items.back());
708                                                 _controller_menu_map[fully_qualified_param] = cmi;
709                                                 cmi->set_active (visible);
710                                                 
711                                                 /* one channel only */
712                                                 break;
713                                         }
714                                 }
715                         }
716                 }
717                         
718                 /* add the menu for this block of controllers to the overall controller menu */
719
720                 items.push_back (MenuElem (string_compose (_("Controllers %1-%2"), i, i+15), *ctl_menu));
721         }
722 }
723
724 Gtk::Menu*
725 MidiTimeAxisView::build_note_mode_menu()
726 {
727         using namespace Menu_Helpers;
728
729         Menu* mode_menu = manage (new Menu);
730         MenuList& items = mode_menu->items();
731         mode_menu->set_name ("ArdourContextMenu");
732
733         RadioMenuItem::Group mode_group;
734         items.push_back (RadioMenuElem (mode_group, _("Sustained"),
735                                 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_mode), Sustained)));
736         _note_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
737         _note_mode_item->set_active(_note_mode == Sustained);
738
739         items.push_back (RadioMenuElem (mode_group, _("Percussive"),
740                                 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_mode), Percussive)));
741         _percussion_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
742         _percussion_mode_item->set_active(_note_mode == Percussive);
743
744         return mode_menu;
745 }
746
747 Gtk::Menu*
748 MidiTimeAxisView::build_color_mode_menu()
749 {
750         using namespace Menu_Helpers;
751
752         Menu* mode_menu = manage (new Menu);
753         MenuList& items = mode_menu->items();
754         mode_menu->set_name ("ArdourContextMenu");
755
756         RadioMenuItem::Group mode_group;
757         items.push_back (RadioMenuElem (mode_group, _("Meter Colors"),
758                                 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode), MeterColors)));
759         _meter_color_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
760         _meter_color_mode_item->set_active(_color_mode == MeterColors);
761
762         items.push_back (RadioMenuElem (mode_group, _("Channel Colors"),
763                                 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode), ChannelColors)));
764         _channel_color_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
765         _channel_color_mode_item->set_active(_color_mode == ChannelColors);
766
767         items.push_back (RadioMenuElem (mode_group, _("Track Color"),
768                                 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode), TrackColor)));
769         _channel_color_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
770         _channel_color_mode_item->set_active(_color_mode == TrackColor);
771
772         return mode_menu;
773 }
774
775 void
776 MidiTimeAxisView::set_note_mode(NoteMode mode)
777 {
778         if (_note_mode != mode || midi_track()->note_mode() != mode) {
779                 _note_mode = mode;
780                 midi_track()->set_note_mode(mode);
781                 xml_node->add_property ("note-mode", enum_2_string(_note_mode));
782                 _view->redisplay_track();
783         }
784 }
785
786 void
787 MidiTimeAxisView::set_color_mode(ColorMode mode)
788 {
789         if (_color_mode != mode) {
790                 if (mode == ChannelColors) {
791                         _channel_selector.set_channel_colors(CanvasNoteEvent::midi_channel_colors);
792                 } else {
793                         _channel_selector.set_default_channel_color();
794                 }
795
796                 _color_mode = mode;
797                 xml_node->add_property ("color-mode", enum_2_string(_color_mode));
798                 _view->redisplay_track();
799         }
800 }
801
802 void
803 MidiTimeAxisView::set_note_range(MidiStreamView::VisibleNoteRange range)
804 {
805         if (!_ignore_signals)
806                 midi_view()->set_note_range(range);
807 }
808
809
810 void
811 MidiTimeAxisView::update_range()
812 {
813         MidiGhostRegion* mgr;
814
815         for(list<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
816                 if ((mgr = dynamic_cast<MidiGhostRegion*>(*i)) != 0) {
817                         mgr->update_range();
818                 }
819         }
820 }
821
822 void
823 MidiTimeAxisView::show_all_automation (bool apply_to_selection)
824 {
825         if (apply_to_selection) {
826                 _editor.get_selection().tracks.foreach_midi_time_axis (boost::bind (&MidiTimeAxisView::show_all_automation, _1, false));
827         } else {
828                 if (midi_track()) {
829                         const set<Evoral::Parameter> params = midi_track()->midi_playlist()->contained_automation();
830                         
831                         for (set<Evoral::Parameter>::const_iterator i = params.begin(); i != params.end(); ++i) {
832                                 create_automation_child(*i, true);
833                         }
834                 }
835                 
836                 RouteTimeAxisView::show_all_automation ();
837         }
838 }
839
840 void
841 MidiTimeAxisView::show_existing_automation (bool apply_to_selection)
842 {
843         if (apply_to_selection) {
844                 _editor.get_selection().tracks.foreach_midi_time_axis (boost::bind (&MidiTimeAxisView::show_existing_automation, _1, false));
845         } else {
846                 if (midi_track()) {
847                         const set<Evoral::Parameter> params = midi_track()->midi_playlist()->contained_automation();
848                         
849                         for (set<Evoral::Parameter>::const_iterator i = params.begin(); i != params.end(); ++i) {
850                                 create_automation_child(*i, true);
851                         }
852                 }
853                 
854                 RouteTimeAxisView::show_existing_automation ();
855         }
856 }
857
858 /** Create an automation track for the given parameter (pitch bend, channel pressure).
859  */
860 void
861 MidiTimeAxisView::create_automation_child (const Evoral::Parameter& param, bool show)
862 {
863         if (param.type() == NullAutomation) {
864                 cerr << "WARNING: Attempt to create NullAutomation child, ignoring" << endl;
865                 return;
866         }
867         
868         AutomationTracks::iterator existing = _automation_tracks.find (param);
869         if (existing != _automation_tracks.end()) {
870                 return;
871         }
872                 
873         if (param.type() == GainAutomation) {
874                 create_gain_automation_child (param, show);
875         } else {
876                 
877                 /* These controllers are region "automation", so we do not create
878                  * an AutomationList/Line for the track */
879                 
880                 boost::shared_ptr<AutomationTimeAxisView> track (
881                         new AutomationTimeAxisView (
882                                 _session,
883                                 _route, 
884                                 boost::shared_ptr<Automatable> (),
885                                 boost::shared_ptr<AutomationControl> (),
886                                 param,
887                                 _editor,
888                                 *this,
889                                 true,
890                                 parent_canvas,
891                                 _route->describe_parameter(param)
892                                 )
893                         );
894
895                 if (_view) {
896                         _view->foreach_regionview (sigc::mem_fun (*track.get(), &TimeAxisView::add_ghost));
897                 }
898                 
899                 add_automation_child (param, track, show);
900         }
901 }
902
903
904 void
905 MidiTimeAxisView::route_active_changed ()
906 {
907         RouteUI::route_active_changed ();
908
909         if (is_track()) {
910                 if (_route->active()) {
911                         controls_ebox.set_name ("MidiTrackControlsBaseUnselected");
912                         controls_base_selected_name = "MidiTrackControlsBaseSelected";
913                         controls_base_unselected_name = "MidiTrackControlsBaseUnselected";
914                 } else {
915                         controls_ebox.set_name ("MidiTrackControlsBaseInactiveUnselected");
916                         controls_base_selected_name = "MidiTrackControlsBaseInactiveSelected";
917                         controls_base_unselected_name = "MidiTrackControlsBaseInactiveUnselected";
918                 }
919         } else {
920
921                 throw; // wha?
922
923                 if (_route->active()) {
924                         controls_ebox.set_name ("BusControlsBaseUnselected");
925                         controls_base_selected_name = "BusControlsBaseSelected";
926                         controls_base_unselected_name = "BusControlsBaseUnselected";
927                 } else {
928                         controls_ebox.set_name ("BusControlsBaseInactiveUnselected");
929                         controls_base_selected_name = "BusControlsBaseInactiveSelected";
930                         controls_base_unselected_name = "BusControlsBaseInactiveUnselected";
931                 }
932         }
933 }
934
935
936
937 void
938 MidiTimeAxisView::add_note_selection (uint8_t note)
939 {
940         if (!_editor.internal_editing()) {
941                 return;
942         }
943
944         uint16_t chn_mask = _channel_selector.get_selected_channels();
945
946         if (_view->num_selected_regionviews() == 0) {
947                 _view->foreach_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection_region_view), note, chn_mask));
948         } else {
949                 _view->foreach_selected_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection_region_view), note, chn_mask));
950         }
951 }
952
953 void
954 MidiTimeAxisView::extend_note_selection (uint8_t note)
955 {
956         if (!_editor.internal_editing()) {
957                 return;
958         }
959
960         uint16_t chn_mask = _channel_selector.get_selected_channels();
961
962         if (_view->num_selected_regionviews() == 0) {
963                 _view->foreach_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::extend_note_selection_region_view), note, chn_mask));
964         } else {
965                 _view->foreach_selected_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::extend_note_selection_region_view), note, chn_mask));
966         }
967 }
968
969 void
970 MidiTimeAxisView::toggle_note_selection (uint8_t note)
971 {
972         if (!_editor.internal_editing()) {
973                 return;
974         }
975
976         uint16_t chn_mask = _channel_selector.get_selected_channels();
977
978         if (_view->num_selected_regionviews() == 0) {
979                 _view->foreach_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection_region_view), note, chn_mask));
980         } else {
981                 _view->foreach_selected_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection_region_view), note, chn_mask));
982         }
983 }
984
985 void
986 MidiTimeAxisView::add_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
987 {
988         dynamic_cast<MidiRegionView*>(rv)->select_matching_notes (note, chn_mask, false, false);
989 }
990
991 void
992 MidiTimeAxisView::extend_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
993 {
994         dynamic_cast<MidiRegionView*>(rv)->select_matching_notes (note, chn_mask, true, true);
995 }
996
997 void
998 MidiTimeAxisView::toggle_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
999 {
1000         dynamic_cast<MidiRegionView*>(rv)->toggle_matching_notes (note, chn_mask);
1001 }
1002
1003 void
1004 MidiTimeAxisView::set_channel_mode (ChannelMode, uint16_t)
1005 {
1006         /* hide all automation tracks that use the wrong channel(s) and show all those that use
1007            the right ones.
1008         */
1009
1010         uint16_t selected_channels = _channel_selector.get_selected_channels();
1011         bool changed = false;
1012
1013         no_redraw = true;
1014
1015         for (uint32_t ctl = 0; ctl < 127; ++ctl) {
1016
1017                 for (uint32_t chn = 0; chn < 16; ++chn) {
1018                         Evoral::Parameter fully_qualified_param (MidiCCAutomation, chn, ctl);
1019                         boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
1020
1021                         if (!track) {
1022                                 continue;
1023                         }
1024                         
1025                         if ((selected_channels & (0x0001 << chn)) == 0) {
1026                                 /* channel not in use. hiding it will trigger RouteTimeAxisView::automation_track_hidden() 
1027                                    which will cause a redraw. We don't want one per channel, so block that with no_redraw.
1028                                  */
1029                                 changed = track->set_visibility (false) || changed;
1030                         } else {
1031                                 changed = track->set_visibility (true) || changed;
1032                         }
1033                 }
1034         }
1035
1036         no_redraw = false;
1037
1038         /* TODO: Bender, Pressure */
1039
1040         /* invalidate the controller menu, so that we rebuild it next time */
1041         _controller_menu_map.clear ();
1042         delete controller_menu;
1043         controller_menu = 0;
1044
1045         if (changed) {
1046                 _route->gui_changed ("track_height", this);
1047         }
1048 }
1049
1050 Gtk::CheckMenuItem*
1051 MidiTimeAxisView::automation_child_menu_item (Evoral::Parameter param)
1052 {
1053         Gtk::CheckMenuItem* m = RouteTimeAxisView::automation_child_menu_item (param);
1054         if (m) {
1055                 return m;
1056         }
1057
1058         ParameterMenuMap::iterator i = _controller_menu_map.find (param);
1059         if (i != _controller_menu_map.end()) {
1060                 return i->second;
1061         }
1062
1063         i = _channel_command_menu_map.find (param);
1064         if (i != _channel_command_menu_map.end()) {
1065                 return i->second;
1066         }
1067
1068         return 0;
1069 }
1070
1071 boost::shared_ptr<MidiRegion>
1072 MidiTimeAxisView::add_region (framepos_t pos, framecnt_t length, bool commit)
1073 {
1074         Editor* real_editor = dynamic_cast<Editor*> (&_editor);
1075
1076         real_editor->begin_reversible_command (Operations::create_region);
1077         playlist()->clear_changes ();
1078
1079         real_editor->snap_to (pos, 0);
1080         
1081         boost::shared_ptr<Source> src = _session->create_midi_source_for_session (view()->trackview().track().get(),
1082                                                                                   view()->trackview().track()->name());
1083         PropertyList plist; 
1084         
1085         plist.add (ARDOUR::Properties::start, 0);
1086         plist.add (ARDOUR::Properties::length, length);
1087         plist.add (ARDOUR::Properties::name, PBD::basename_nosuffix(src->name()));
1088         
1089         boost::shared_ptr<Region> region = (RegionFactory::create (src, plist));
1090         
1091         playlist()->add_region (region, pos);
1092         _session->add_command (new StatefulDiffCommand (playlist()));
1093
1094         if (commit) {
1095                 real_editor->commit_reversible_command ();
1096         }
1097
1098         return boost::dynamic_pointer_cast<MidiRegion>(region);
1099 }
1100
1101 void
1102 MidiTimeAxisView::ensure_step_editor ()
1103 {
1104         if (!_step_editor) {
1105                 _step_editor = new StepEditor (_editor, midi_track(), *this);
1106         }
1107 }
1108
1109 void 
1110 MidiTimeAxisView::start_step_editing ()
1111 {
1112         ensure_step_editor ();
1113         _step_editor->start_step_editing ();
1114
1115 }
1116 void 
1117 MidiTimeAxisView::stop_step_editing ()
1118 {
1119         if (_step_editor) {
1120                 _step_editor->stop_step_editing ();
1121         }
1122 }