~ubuntu-branches/ubuntu/utopic/ardour3/utopic

« back to all changes in this revision

Viewing changes to gtk2_ardour/midi_time_axis.cc

  • Committer: Package Import Robot
  • Author(s): Felipe Sateler
  • Date: 2013-09-21 19:05:02 UTC
  • Revision ID: package-import@ubuntu.com-20130921190502-8gsftrku6jnzhd7v
Tags: upstream-3.4~dfsg
ImportĀ upstreamĀ versionĀ 3.4~dfsg

Show diffs side-by-side

added added

removed removed

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