Preliminary support for named MIDI controllers via midname files.
[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         using namespace MIDI::Name;
630         const string& model = _midnam_model_selector.get_active_text();
631         boost::shared_ptr<MIDINameDocument> midnam = MidiPatchManager::instance()
632                 .document_by_model(model);
633         boost::shared_ptr<MasterDeviceNames> device_names;
634         if (midnam && !midnam->master_device_names_by_model().empty()) {
635                 device_names = boost::shared_ptr<MasterDeviceNames>(
636                         midnam->master_device_names_by_model().begin()->second);
637         }
638
639         if (device_names && !device_names->controls().empty()) {
640                 /* Controllers names available from the midnam file, generate a custom controller menu */
641                 for (MasterDeviceNames::ControlNameLists::const_iterator l = device_names->controls().begin();
642                      l != device_names->controls().end(); ++l) {
643                         boost::shared_ptr<ControlNameList> name_list = *l;
644
645                         int       chn        = 0;  // TODO
646                         Menu*     group_menu = manage(new Menu());
647                         MenuList& group_items(group_menu->items());
648
649                         for (ControlNameList::Controls::const_iterator c = (*l)->controls().begin();
650                              c != (*l)->controls().end(); ++c) {
651                                 Evoral::Parameter fully_qualified_param(MidiCCAutomation, chn, atoi((*c)->number().c_str()));
652                                 group_items.push_back(
653                                         CheckMenuElem(string_compose("<b>%1</b>: %2 [%3]",
654                                                                      (*c)->number(), (*c)->name(), int(chn)),
655                                                       sigc::bind(sigc::mem_fun(*this, &RouteTimeAxisView::toggle_automation_track),
656                                                                  fully_qualified_param)));
657                                 dynamic_cast<Label*> (group_items.back().get_child())->set_use_markup (true);
658                         }
659                         items.push_back(MenuElem(name_list->name(), *group_menu));
660                 }
661                 return;
662         }
663
664         /* loop over all 127 MIDI controllers, in groups of 16; except don't offer
665            bank select controllers, as they are handled by the `patch' code */
666
667         for (int i = 0; i < 127; i += 16) {
668
669                 Menu* ctl_menu = manage (new Menu);
670                 MenuList& ctl_items (ctl_menu->items());
671
672
673                 /* for each controller, consider whether to create a submenu or a single item */
674
675                 for (int ctl = i; ctl < i+16; ++ctl) {
676
677                         if (ctl == MIDI_CTL_MSB_BANK || ctl == MIDI_CTL_LSB_BANK) {
678                                 continue;
679                         }
680
681                         if (chn_cnt > 1) {
682
683                                 /* multiple channels - create a submenu, with 1 item per channel */
684
685                                 Menu* chn_menu = manage (new Menu);
686                                 MenuList& chn_items (chn_menu->items());
687
688                                 /* add a couple of items to hide/show this controller on all channels */
689
690                                 Evoral::Parameter param_without_channel (MidiCCAutomation, 0, ctl);
691                                 chn_items.push_back (MenuElem (_("Hide all channels"),
692                                                                     sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
693                                                                                 false, param_without_channel)));
694                                 chn_items.push_back (MenuElem (_("Show all channels"),
695                                                                     sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::change_all_channel_tracks_visibility),
696                                                                                 true, param_without_channel)));
697
698                                 for (uint8_t chn = 0; chn < 16; chn++) {
699                                         if (selected_channels & (0x0001 << chn)) {
700
701                                                 /* for each selected channel, add a menu item for this controller */
702
703                                                 Evoral::Parameter fully_qualified_param (MidiCCAutomation, chn, ctl);
704                                                 chn_items.push_back (CheckMenuElem (string_compose (_("Channel %1"), chn+1),
705                                                                                     sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
706                                                                                                 fully_qualified_param)));
707
708                                                 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
709                                                 bool visible = false;
710
711                                                 if (track) {
712                                                         if (track->marked_for_display()) {
713                                                                 visible = true;
714                                                         }
715                                                 }
716
717                                                 CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&chn_items.back());
718                                                 _controller_menu_map[fully_qualified_param] = cmi;
719                                                 cmi->set_active (visible);
720                                         }
721                                 }
722
723                                 /* add the per-channel menu to the list of controllers, with the name of the controller */
724                                 ctl_items.push_back (MenuElem (string_compose ("<b>%1</b>: %2", ctl, midi_name (ctl)), *chn_menu));
725                                 dynamic_cast<Label*> (ctl_items.back().get_child())->set_use_markup (true);
726
727                         } else {
728
729                                 /* just one channel - create a single menu item for this ctl+channel combination*/
730
731                                 for (uint8_t chn = 0; chn < 16; chn++) {
732                                         if (selected_channels & (0x0001 << chn)) {
733
734                                                 Evoral::Parameter fully_qualified_param (MidiCCAutomation, chn, ctl);
735                                                 ctl_items.push_back (
736                                                         CheckMenuElem (
737                                                                 string_compose ("<b>%1</b>: %2 [%3]", ctl, midi_name (ctl), int (chn)),
738                                                                 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
739                                                                             fully_qualified_param)
740                                                                 )
741                                                         );
742                                                 dynamic_cast<Label*> (ctl_items.back().get_child())->set_use_markup (true);
743
744                                                 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
745                                                 bool visible = false;
746
747                                                 if (track) {
748                                                         if (track->marked_for_display()) {
749                                                                 visible = true;
750                                                         }
751                                                 }
752
753                                                 CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&ctl_items.back());
754                                                 _controller_menu_map[fully_qualified_param] = cmi;
755                                                 cmi->set_active (visible);
756
757                                                 /* one channel only */
758                                                 break;
759                                         }
760                                 }
761                         }
762                 }
763
764                 /* add the menu for this block of controllers to the overall controller menu */
765
766                 items.push_back (MenuElem (string_compose (_("Controllers %1-%2"), i, i+15), *ctl_menu));
767         }
768 }
769
770 Gtk::Menu*
771 MidiTimeAxisView::build_note_mode_menu()
772 {
773         using namespace Menu_Helpers;
774
775         Menu* mode_menu = manage (new Menu);
776         MenuList& items = mode_menu->items();
777         mode_menu->set_name ("ArdourContextMenu");
778
779         RadioMenuItem::Group mode_group;
780         items.push_back (RadioMenuElem (mode_group,_("Sustained"),
781                                 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_mode), Sustained, true)));
782         _note_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
783         _note_mode_item->set_active(_note_mode == Sustained);
784
785         items.push_back (RadioMenuElem (mode_group, _("Percussive"),
786                                 sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_mode), Percussive, true)));
787         _percussion_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
788         _percussion_mode_item->set_active(_note_mode == Percussive);
789
790         return mode_menu;
791 }
792
793 Gtk::Menu*
794 MidiTimeAxisView::build_color_mode_menu()
795 {
796         using namespace Menu_Helpers;
797
798         Menu* mode_menu = manage (new Menu);
799         MenuList& items = mode_menu->items();
800         mode_menu->set_name ("ArdourContextMenu");
801
802         RadioMenuItem::Group mode_group;
803         items.push_back (RadioMenuElem (mode_group, _("Meter Colors"),
804                                         sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode),
805                                                     MeterColors, false, true, true)));
806         _meter_color_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
807         _meter_color_mode_item->set_active(_color_mode == MeterColors);
808
809         items.push_back (RadioMenuElem (mode_group, _("Channel Colors"),
810                                         sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode),
811                                                     ChannelColors, false, true, true)));
812         _channel_color_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
813         _channel_color_mode_item->set_active(_color_mode == ChannelColors);
814
815         items.push_back (RadioMenuElem (mode_group, _("Track Color"),
816                                         sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_color_mode),
817                                                     TrackColor, false, true, true)));
818         _channel_color_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
819         _channel_color_mode_item->set_active(_color_mode == TrackColor);
820
821         return mode_menu;
822 }
823
824 void
825 MidiTimeAxisView::set_note_mode(NoteMode mode, bool apply_to_selection)
826 {
827         if (apply_to_selection) {
828                 _editor.get_selection().tracks.foreach_midi_time_axis (boost::bind (&MidiTimeAxisView::set_note_mode, _1, mode, false));
829         } else {
830                 if (_note_mode != mode || midi_track()->note_mode() != mode) {
831                         _note_mode = mode;
832                         midi_track()->set_note_mode(mode);
833                         set_gui_property ("note-mode", enum_2_string(_note_mode));
834                         _view->redisplay_track();
835                 }
836         }
837 }
838
839 void
840 MidiTimeAxisView::set_color_mode (ColorMode mode, bool force, bool redisplay, bool apply_to_selection)
841 {
842         if (apply_to_selection) {
843                 _editor.get_selection().tracks.foreach_midi_time_axis (
844                         boost::bind (&MidiTimeAxisView::set_color_mode, _1, mode, force, redisplay, false)
845                         );
846         } else {
847                 
848                 if (_color_mode == mode && !force) {
849                         return;
850                 }
851                 
852                 if (mode == ChannelColors) {
853                         _channel_selector.set_channel_colors(CanvasNoteEvent::midi_channel_colors);
854                 } else {
855                         _channel_selector.set_default_channel_color();
856                 }
857                 
858                 _color_mode = mode;
859                 set_gui_property ("color-mode", enum_2_string(_color_mode));
860                 if (redisplay) {
861                         _view->redisplay_track();
862                 }
863         }
864 }
865
866 void
867 MidiTimeAxisView::set_note_range (MidiStreamView::VisibleNoteRange range, bool apply_to_selection)
868 {
869         if (apply_to_selection) {
870                 _editor.get_selection().tracks.foreach_midi_time_axis (
871                         boost::bind (&MidiTimeAxisView::set_note_range, _1, range, false)
872                         );
873         } else {
874                 if (!_ignore_signals) {
875                         midi_view()->set_note_range(range);
876                 }
877         }
878 }
879
880 void
881 MidiTimeAxisView::update_range()
882 {
883         MidiGhostRegion* mgr;
884
885         for(list<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
886                 if ((mgr = dynamic_cast<MidiGhostRegion*>(*i)) != 0) {
887                         mgr->update_range();
888                 }
889         }
890 }
891
892 void
893 MidiTimeAxisView::show_all_automation (bool apply_to_selection)
894 {
895         if (apply_to_selection) {
896                 _editor.get_selection().tracks.foreach_midi_time_axis (boost::bind (&MidiTimeAxisView::show_all_automation, _1, false));
897         } else {
898                 if (midi_track()) {
899                         const set<Evoral::Parameter> params = midi_track()->midi_playlist()->contained_automation();
900
901                         for (set<Evoral::Parameter>::const_iterator i = params.begin(); i != params.end(); ++i) {
902                                 create_automation_child(*i, true);
903                         }
904                 }
905
906                 RouteTimeAxisView::show_all_automation ();
907         }
908 }
909
910 void
911 MidiTimeAxisView::show_existing_automation (bool apply_to_selection)
912 {
913         if (apply_to_selection) {
914                 _editor.get_selection().tracks.foreach_midi_time_axis (boost::bind (&MidiTimeAxisView::show_existing_automation, _1, false));
915         } else {
916                 if (midi_track()) {
917                         const set<Evoral::Parameter> params = midi_track()->midi_playlist()->contained_automation();
918
919                         for (set<Evoral::Parameter>::const_iterator i = params.begin(); i != params.end(); ++i) {
920                                 create_automation_child (*i, true);
921                         }
922                 }
923
924                 RouteTimeAxisView::show_existing_automation ();
925         }
926 }
927
928 /** Create an automation track for the given parameter (pitch bend, channel pressure).
929  */
930 void
931 MidiTimeAxisView::create_automation_child (const Evoral::Parameter& param, bool show)
932 {
933         if (param.type() == NullAutomation) {
934                 return;
935         }
936
937         AutomationTracks::iterator existing = _automation_tracks.find (param);
938
939         if (existing != _automation_tracks.end()) {
940
941                 /* automation track created because we had existing data for
942                  * the processor, but visibility may need to be controlled
943                  * since it will have been set visible by default.
944                  */
945
946                 if (existing->second->set_marked_for_display (show) && !no_redraw) {
947                         request_redraw ();
948                 }
949
950                 return;
951         }
952
953         boost::shared_ptr<AutomationTimeAxisView> track;
954
955         switch (param.type()) {
956
957         case GainAutomation:
958                 create_gain_automation_child (param, show);
959                 break;
960
961         case PluginAutomation:
962                 /* handled elsewhere */
963                 break;
964
965         case MidiCCAutomation:
966         case MidiPgmChangeAutomation:
967         case MidiPitchBenderAutomation:
968         case MidiChannelPressureAutomation:
969         case MidiSystemExclusiveAutomation:
970                 /* These controllers are region "automation" - they are owned
971                  * by regions (and their MidiModels), not by the track. As a
972                  * result we do not create an AutomationList/Line for the track
973                  * ... except here we are doing something!! XXX 
974                  */
975
976                 track.reset (new AutomationTimeAxisView (
977                                      _session,
978                                      _route,
979                                      boost::shared_ptr<Automatable> (),
980                                      boost::shared_ptr<AutomationControl> (),
981                                      param,
982                                      _editor,
983                                      *this,
984                                      true,
985                                      parent_canvas,
986                                      _route->describe_parameter(param)
987                                      ));
988
989                 if (_view) {
990                         _view->foreach_regionview (sigc::mem_fun (*track.get(), &TimeAxisView::add_ghost));
991                 }
992
993                 add_automation_child (param, track, show);
994                 break;
995
996         default:
997                 error << "MidiTimeAxisView: unknown automation child " << EventTypeMap::instance().to_symbol(param) << endmsg;
998         }
999 }
1000
1001 void
1002 MidiTimeAxisView::route_active_changed ()
1003 {
1004         RouteUI::route_active_changed ();
1005
1006         if (is_track()) {
1007                 if (_route->active()) {
1008                         controls_ebox.set_name ("MidiTrackControlsBaseUnselected");
1009                         controls_base_selected_name = "MidiTrackControlsBaseSelected";
1010                         controls_base_unselected_name = "MidiTrackControlsBaseUnselected";
1011                 } else {
1012                         controls_ebox.set_name ("MidiTrackControlsBaseInactiveUnselected");
1013                         controls_base_selected_name = "MidiTrackControlsBaseInactiveSelected";
1014                         controls_base_unselected_name = "MidiTrackControlsBaseInactiveUnselected";
1015                 }
1016         } else {
1017
1018                 throw; // wha?
1019
1020                 if (_route->active()) {
1021                         controls_ebox.set_name ("BusControlsBaseUnselected");
1022                         controls_base_selected_name = "BusControlsBaseSelected";
1023                         controls_base_unselected_name = "BusControlsBaseUnselected";
1024                 } else {
1025                         controls_ebox.set_name ("BusControlsBaseInactiveUnselected");
1026                         controls_base_selected_name = "BusControlsBaseInactiveSelected";
1027                         controls_base_unselected_name = "BusControlsBaseInactiveUnselected";
1028                 }
1029         }
1030 }
1031
1032 void
1033 MidiTimeAxisView::set_note_selection (uint8_t note)
1034 {
1035         if (!_editor.internal_editing()) {
1036                 return;
1037         }
1038
1039         uint16_t chn_mask = _channel_selector.get_selected_channels();
1040
1041         if (_view->num_selected_regionviews() == 0) {
1042                 _view->foreach_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_selection_region_view), note, chn_mask));
1043         } else {
1044                 _view->foreach_selected_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::set_note_selection_region_view), note, chn_mask));
1045         }
1046 }
1047
1048 void
1049 MidiTimeAxisView::add_note_selection (uint8_t note)
1050 {
1051         if (!_editor.internal_editing()) {
1052                 return;
1053         }
1054
1055         uint16_t chn_mask = _channel_selector.get_selected_channels();
1056
1057         if (_view->num_selected_regionviews() == 0) {
1058                 _view->foreach_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection_region_view), note, chn_mask));
1059         } else {
1060                 _view->foreach_selected_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::add_note_selection_region_view), note, chn_mask));
1061         }
1062 }
1063
1064 void
1065 MidiTimeAxisView::extend_note_selection (uint8_t note)
1066 {
1067         if (!_editor.internal_editing()) {
1068                 return;
1069         }
1070
1071         uint16_t chn_mask = _channel_selector.get_selected_channels();
1072
1073         if (_view->num_selected_regionviews() == 0) {
1074                 _view->foreach_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::extend_note_selection_region_view), note, chn_mask));
1075         } else {
1076                 _view->foreach_selected_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::extend_note_selection_region_view), note, chn_mask));
1077         }
1078 }
1079
1080 void
1081 MidiTimeAxisView::toggle_note_selection (uint8_t note)
1082 {
1083         if (!_editor.internal_editing()) {
1084                 return;
1085         }
1086
1087         uint16_t chn_mask = _channel_selector.get_selected_channels();
1088
1089         if (_view->num_selected_regionviews() == 0) {
1090                 _view->foreach_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection_region_view), note, chn_mask));
1091         } else {
1092                 _view->foreach_selected_regionview (sigc::bind (sigc::mem_fun (*this, &MidiTimeAxisView::toggle_note_selection_region_view), note, chn_mask));
1093         }
1094 }
1095
1096 void
1097 MidiTimeAxisView::set_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1098 {
1099         dynamic_cast<MidiRegionView*>(rv)->select_matching_notes (note, chn_mask, false, false);
1100 }
1101
1102 void
1103 MidiTimeAxisView::add_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1104 {
1105         dynamic_cast<MidiRegionView*>(rv)->select_matching_notes (note, chn_mask, true, false);
1106 }
1107
1108 void
1109 MidiTimeAxisView::extend_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1110 {
1111         dynamic_cast<MidiRegionView*>(rv)->select_matching_notes (note, chn_mask, true, true);
1112 }
1113
1114 void
1115 MidiTimeAxisView::toggle_note_selection_region_view (RegionView* rv, uint8_t note, uint16_t chn_mask)
1116 {
1117         dynamic_cast<MidiRegionView*>(rv)->toggle_matching_notes (note, chn_mask);
1118 }
1119
1120 void
1121 MidiTimeAxisView::set_channel_mode (ChannelMode, uint16_t)
1122 {
1123         /* hide all automation tracks that use the wrong channel(s) and show all those that use
1124            the right ones.
1125         */
1126
1127         uint16_t selected_channels = _channel_selector.get_selected_channels();
1128         bool changed = false;
1129
1130         no_redraw = true;
1131
1132         for (uint32_t ctl = 0; ctl < 127; ++ctl) {
1133
1134                 for (uint32_t chn = 0; chn < 16; ++chn) {
1135                         Evoral::Parameter fully_qualified_param (MidiCCAutomation, chn, ctl);
1136                         boost::shared_ptr<AutomationTimeAxisView> track = automation_child (fully_qualified_param);
1137
1138                         if (!track) {
1139                                 continue;
1140                         }
1141
1142                         if ((selected_channels & (0x0001 << chn)) == 0) {
1143                                 /* channel not in use. hiding it will trigger RouteTimeAxisView::automation_track_hidden()
1144                                    which will cause a redraw. We don't want one per channel, so block that with no_redraw.
1145                                  */
1146                                 changed = track->set_marked_for_display (false) || changed;
1147                         } else {
1148                                 changed = track->set_marked_for_display (true) || changed;
1149                         }
1150                 }
1151         }
1152
1153         no_redraw = false;
1154
1155         /* TODO: Bender, Pressure */
1156
1157         /* invalidate the controller menu, so that we rebuild it next time */
1158         _controller_menu_map.clear ();
1159         delete controller_menu;
1160         controller_menu = 0;
1161
1162         if (changed) {
1163                 request_redraw ();
1164         }
1165 }
1166
1167 Gtk::CheckMenuItem*
1168 MidiTimeAxisView::automation_child_menu_item (Evoral::Parameter param)
1169 {
1170         Gtk::CheckMenuItem* m = RouteTimeAxisView::automation_child_menu_item (param);
1171         if (m) {
1172                 return m;
1173         }
1174
1175         ParameterMenuMap::iterator i = _controller_menu_map.find (param);
1176         if (i != _controller_menu_map.end()) {
1177                 return i->second;
1178         }
1179
1180         i = _channel_command_menu_map.find (param);
1181         if (i != _channel_command_menu_map.end()) {
1182                 return i->second;
1183         }
1184
1185         return 0;
1186 }
1187
1188 boost::shared_ptr<MidiRegion>
1189 MidiTimeAxisView::add_region (framepos_t pos, framecnt_t length, bool commit)
1190 {
1191         Editor* real_editor = dynamic_cast<Editor*> (&_editor);
1192
1193         real_editor->begin_reversible_command (Operations::create_region);
1194         playlist()->clear_changes ();
1195
1196         real_editor->snap_to (pos, 0);
1197
1198         boost::shared_ptr<Source> src = _session->create_midi_source_for_session (view()->trackview().track().get(),
1199                                                                                   view()->trackview().track()->name());
1200         PropertyList plist;
1201
1202         plist.add (ARDOUR::Properties::start, 0);
1203         plist.add (ARDOUR::Properties::length, length);
1204         plist.add (ARDOUR::Properties::name, PBD::basename_nosuffix(src->name()));
1205
1206         boost::shared_ptr<Region> region = (RegionFactory::create (src, plist));
1207
1208         playlist()->add_region (region, pos);
1209         _session->add_command (new StatefulDiffCommand (playlist()));
1210
1211         if (commit) {
1212                 real_editor->commit_reversible_command ();
1213         }
1214
1215         return boost::dynamic_pointer_cast<MidiRegion>(region);
1216 }
1217
1218 void
1219 MidiTimeAxisView::ensure_step_editor ()
1220 {
1221         if (!_step_editor) {
1222                 _step_editor = new StepEditor (_editor, midi_track(), *this);
1223         }
1224 }
1225
1226 void
1227 MidiTimeAxisView::start_step_editing ()
1228 {
1229         ensure_step_editor ();
1230         _step_editor->start_step_editing ();
1231
1232 }
1233 void
1234 MidiTimeAxisView::stop_step_editing ()
1235 {
1236         if (_step_editor) {
1237                 _step_editor->stop_step_editing ();
1238         }
1239 }
1240
1241
1242 /** @return channel (counted from 0) to add an event to, based on the current setting
1243  *  of the channel selector.
1244  */
1245 uint8_t
1246 MidiTimeAxisView::get_channel_for_add () const
1247 {
1248         uint16_t const chn_mask = _channel_selector.get_selected_channels ();
1249         int chn_cnt = 0;
1250         uint8_t channel = 0;
1251
1252         /* pick the highest selected channel, unless all channels are selected,
1253            which is interpreted to mean channel 1 (zero)
1254         */
1255
1256         for (uint16_t i = 0; i < 16; ++i) {
1257                 if (chn_mask & (1<<i)) {
1258                         channel = i;
1259                         chn_cnt++;
1260                 }
1261         }
1262
1263         if (chn_cnt == 16) {
1264                 channel = 0;
1265         }
1266
1267         return channel;
1268 }
1269
1270 void
1271 MidiTimeAxisView::note_range_changed ()
1272 {
1273         set_gui_property ("note-range-min", (int) midi_view()->lowest_note ());
1274         set_gui_property ("note-range-max", (int) midi_view()->highest_note ());
1275 }
1276
1277 void
1278 MidiTimeAxisView::contents_height_changed ()
1279 {
1280         _range_scroomer->set_size_request (-1, _view->child_height ());
1281 }