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