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