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