extend strict-i/o to include route outputs.
[ardour.git] / gtk2_ardour / midi_region_view.cc
1 /*
2     Copyright (C) 2001-2011 Paul Davis
3     Author: David Robillard
4
5     This program is free software; you can redistribute it and/or modify
6     it under the terms of the GNU General Public License as published by
7     the Free Software Foundation; either version 2 of the License, or
8     (at your option) any later version.
9
10     This program is distributed in the hope that it will be useful,
11     but WITHOUT ANY WARRANTY; without even the implied warranty of
12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13     GNU General Public License for more details.
14
15     You should have received a copy of the GNU General Public License
16     along with this program; if not, write to the Free Software
17     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 */
19
20 #include <cmath>
21 #include <algorithm>
22 #include <ostream>
23
24 #include <gtkmm.h>
25
26 #include "gtkmm2ext/gtk_ui.h"
27
28 #include <sigc++/signal.h>
29
30 #include "midi++/midnam_patch.h"
31
32 #include "pbd/memento_command.h"
33 #include "pbd/stateful_diff_command.h"
34
35 #include "ardour/midi_model.h"
36 #include "ardour/midi_playlist.h"
37 #include "ardour/midi_region.h"
38 #include "ardour/midi_source.h"
39 #include "ardour/midi_track.h"
40 #include "ardour/operations.h"
41 #include "ardour/session.h"
42
43 #include "evoral/Parameter.hpp"
44 #include "evoral/MIDIEvent.hpp"
45 #include "evoral/Control.hpp"
46 #include "evoral/midi_util.h"
47
48 #include "canvas/debug.h"
49 #include "canvas/text.h"
50
51 #include "automation_region_view.h"
52 #include "automation_time_axis.h"
53 #include "control_point.h"
54 #include "debug.h"
55 #include "editor.h"
56 #include "editor_drag.h"
57 #include "ghostregion.h"
58 #include "gui_thread.h"
59 #include "item_counts.h"
60 #include "keyboard.h"
61 #include "midi_channel_dialog.h"
62 #include "midi_cut_buffer.h"
63 #include "midi_list_editor.h"
64 #include "midi_region_view.h"
65 #include "midi_streamview.h"
66 #include "midi_time_axis.h"
67 #include "midi_util.h"
68 #include "midi_velocity_dialog.h"
69 #include "mouse_cursors.h"
70 #include "note_player.h"
71 #include "paste_context.h"
72 #include "public_editor.h"
73 #include "route_time_axis.h"
74 #include "rgb_macros.h"
75 #include "selection.h"
76 #include "streamview.h"
77 #include "patch_change_dialog.h"
78 #include "verbose_cursor.h"
79 #include "note.h"
80 #include "hit.h"
81 #include "patch_change.h"
82 #include "sys_ex.h"
83 #include "ui_config.h"
84
85 #include "i18n.h"
86
87 using namespace ARDOUR;
88 using namespace PBD;
89 using namespace Editing;
90 using namespace std;
91 using Gtkmm2ext::Keyboard;
92
93 #define MIDI_BP_ZERO ((Config->get_first_midi_bank_is_zero())?0:1)
94
95 MidiRegionView::MidiRegionView (ArdourCanvas::Container*      parent,
96                                 RouteTimeAxisView&            tv,
97                                 boost::shared_ptr<MidiRegion> r,
98                                 double                        spu,
99                                 uint32_t                      basic_color)
100         : RegionView (parent, tv, r, spu, basic_color)
101         , _current_range_min(0)
102         , _current_range_max(0)
103         , _region_relative_time_converter(r->session().tempo_map(), r->position())
104         , _source_relative_time_converter(r->session().tempo_map(), r->position() - r->start())
105         , _region_relative_time_converter_double(r->session().tempo_map(), r->position())
106         , _active_notes(0)
107         , _note_group (new ArdourCanvas::Container (group))
108         , _note_diff_command (0)
109         , _ghost_note(0)
110         , _step_edit_cursor (0)
111         , _step_edit_cursor_width (1.0)
112         , _step_edit_cursor_position (0.0)
113         , _channel_selection_scoped_note (0)
114         , _temporary_note_group (0)
115         , _mouse_state(None)
116         , _pressed_button(0)
117         , _sort_needed (true)
118         , _optimization_iterator (_events.end())
119         , _list_editor (0)
120         , _no_sound_notes (false)
121         , _last_display_zoom (0)
122         , _last_event_x (0)
123         , _last_event_y (0)
124         , _grabbed_keyboard (false)
125         , _entered (false)
126         , _note_entered (false)
127         , _mouse_changed_selection (false)
128 {
129         CANVAS_DEBUG_NAME (_note_group, string_compose ("note group for %1", get_item_name()));
130         _note_group->raise_to_top();
131         PublicEditor::DropDownKeys.connect (sigc::mem_fun (*this, &MidiRegionView::drop_down_keys));
132
133         Config->ParameterChanged.connect (*this, invalidator (*this), boost::bind (&MidiRegionView::parameter_changed, this, _1), gui_context());
134         connect_to_diskstream ();
135 }
136
137 MidiRegionView::MidiRegionView (ArdourCanvas::Container*      parent,
138                                 RouteTimeAxisView&            tv,
139                                 boost::shared_ptr<MidiRegion> r,
140                                 double                        spu,
141                                 uint32_t                      basic_color,
142                                 bool                          recording,
143                                 TimeAxisViewItem::Visibility  visibility)
144         : RegionView (parent, tv, r, spu, basic_color, recording, visibility)
145         , _current_range_min(0)
146         , _current_range_max(0)
147         , _region_relative_time_converter(r->session().tempo_map(), r->position())
148         , _source_relative_time_converter(r->session().tempo_map(), r->position() - r->start())
149         , _region_relative_time_converter_double(r->session().tempo_map(), r->position())
150         , _active_notes(0)
151         , _note_group (new ArdourCanvas::Container (group))
152         , _note_diff_command (0)
153         , _ghost_note(0)
154         , _step_edit_cursor (0)
155         , _step_edit_cursor_width (1.0)
156         , _step_edit_cursor_position (0.0)
157         , _channel_selection_scoped_note (0)
158         , _temporary_note_group (0)
159         , _mouse_state(None)
160         , _pressed_button(0)
161         , _sort_needed (true)
162         , _optimization_iterator (_events.end())
163         , _list_editor (0)
164         , _no_sound_notes (false)
165         , _last_display_zoom (0)
166         , _last_event_x (0)
167         , _last_event_y (0)
168         , _grabbed_keyboard (false)
169         , _entered (false)
170         , _note_entered (false)
171         , _mouse_changed_selection (false)
172 {
173         CANVAS_DEBUG_NAME (_note_group, string_compose ("note group for %1", get_item_name()));
174         _note_group->raise_to_top();
175
176         PublicEditor::DropDownKeys.connect (sigc::mem_fun (*this, &MidiRegionView::drop_down_keys));
177
178         connect_to_diskstream ();
179 }
180
181 void
182 MidiRegionView::parameter_changed (std::string const & p)
183 {
184         if (p == "display-first-midi-bank-as-zero") {
185                 if (_enable_display) {
186                         redisplay_model();
187                 }
188         }
189 }
190
191 MidiRegionView::MidiRegionView (const MidiRegionView& other)
192         : sigc::trackable(other)
193         , RegionView (other)
194         , _current_range_min(0)
195         , _current_range_max(0)
196         , _region_relative_time_converter(other.region_relative_time_converter())
197         , _source_relative_time_converter(other.source_relative_time_converter())
198         , _region_relative_time_converter_double(other.region_relative_time_converter_double())
199         , _active_notes(0)
200         , _note_group (new ArdourCanvas::Container (get_canvas_group()))
201         , _note_diff_command (0)
202         , _ghost_note(0)
203         , _step_edit_cursor (0)
204         , _step_edit_cursor_width (1.0)
205         , _step_edit_cursor_position (0.0)
206         , _channel_selection_scoped_note (0)
207         , _temporary_note_group (0)
208         , _mouse_state(None)
209         , _pressed_button(0)
210         , _sort_needed (true)
211         , _optimization_iterator (_events.end())
212         , _list_editor (0)
213         , _no_sound_notes (false)
214         , _last_display_zoom (0)
215         , _last_event_x (0)
216         , _last_event_y (0)
217         , _grabbed_keyboard (false)
218         , _entered (false)
219         , _note_entered (false)
220         , _mouse_changed_selection (false)
221 {
222         init (false);
223 }
224
225 MidiRegionView::MidiRegionView (const MidiRegionView& other, boost::shared_ptr<MidiRegion> region)
226         : RegionView (other, boost::shared_ptr<Region> (region))
227         , _current_range_min(0)
228         , _current_range_max(0)
229         , _region_relative_time_converter(other.region_relative_time_converter())
230         , _source_relative_time_converter(other.source_relative_time_converter())
231         , _region_relative_time_converter_double(other.region_relative_time_converter_double())
232         , _active_notes(0)
233         , _note_group (new ArdourCanvas::Container (get_canvas_group()))
234         , _note_diff_command (0)
235         , _ghost_note(0)
236         , _step_edit_cursor (0)
237         , _step_edit_cursor_width (1.0)
238         , _step_edit_cursor_position (0.0)
239         , _channel_selection_scoped_note (0)
240         , _temporary_note_group (0)
241         , _mouse_state(None)
242         , _pressed_button(0)
243         , _sort_needed (true)
244         , _optimization_iterator (_events.end())
245         , _list_editor (0)
246         , _no_sound_notes (false)
247         , _last_display_zoom (0)
248         , _last_event_x (0)
249         , _last_event_y (0)
250         , _grabbed_keyboard (false)
251         , _entered (false)
252         , _note_entered (false)
253         , _mouse_changed_selection (false)
254 {
255         init (true);
256 }
257
258 void
259 MidiRegionView::init (bool wfd)
260 {
261         PublicEditor::DropDownKeys.connect (sigc::mem_fun (*this, &MidiRegionView::drop_down_keys));
262
263         if (wfd) {
264                 Glib::Threads::Mutex::Lock lm(midi_region()->midi_source(0)->mutex());
265                 midi_region()->midi_source(0)->load_model(lm);
266         }
267
268         _model = midi_region()->midi_source(0)->model();
269         _enable_display = false;
270         fill_color_name = "midi frame base";
271
272         RegionView::init (false);
273
274         set_height (trackview.current_height());
275
276         region_muted ();
277         region_sync_changed ();
278         region_resized (ARDOUR::bounds_change);
279         region_locked ();
280
281         set_colors ();
282
283         _enable_display = true;
284         if (_model) {
285                 if (wfd) {
286                         display_model (_model);
287                 }
288         }
289
290         reset_width_dependent_items (_pixel_width);
291
292         group->raise_to_top();
293
294         midi_view()->midi_track()->playback_filter().ChannelModeChanged.connect (_channel_mode_changed_connection, invalidator (*this),
295                                                                        boost::bind (&MidiRegionView::midi_channel_mode_changed, this),
296                                                                        gui_context ());
297
298         instrument_info().Changed.connect (_instrument_changed_connection, invalidator (*this),
299                                            boost::bind (&MidiRegionView::instrument_settings_changed, this), gui_context());
300
301         trackview.editor().SnapChanged.connect(snap_changed_connection, invalidator(*this),
302                                                boost::bind (&MidiRegionView::snap_changed, this),
303                                                gui_context());
304
305         trackview.editor().MouseModeChanged.connect(_mouse_mode_connection, invalidator (*this),
306                                                     boost::bind (&MidiRegionView::mouse_mode_changed, this),
307                                                     gui_context ());
308
309         Config->ParameterChanged.connect (*this, invalidator (*this), boost::bind (&MidiRegionView::parameter_changed, this, _1), gui_context());
310         connect_to_diskstream ();
311 }
312
313 InstrumentInfo&
314 MidiRegionView::instrument_info () const
315 {
316         RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
317         return route_ui->route()->instrument_info();
318 }
319
320 const boost::shared_ptr<ARDOUR::MidiRegion>
321 MidiRegionView::midi_region() const
322 {
323         return boost::dynamic_pointer_cast<ARDOUR::MidiRegion>(_region);
324 }
325
326 void
327 MidiRegionView::connect_to_diskstream ()
328 {
329         midi_view()->midi_track()->DataRecorded.connect(
330                 *this, invalidator(*this),
331                 boost::bind (&MidiRegionView::data_recorded, this, _1),
332                 gui_context());
333 }
334
335 bool
336 MidiRegionView::canvas_group_event(GdkEvent* ev)
337 {
338         if (in_destructor || _recregion) {
339                 return false;
340         }
341
342         if (!trackview.editor().internal_editing()) {
343                 // not in internal edit mode, so just act like a normal region
344                 return RegionView::canvas_group_event (ev);
345         }
346
347         bool r;
348
349         switch (ev->type) {
350         case GDK_ENTER_NOTIFY:
351                 _last_event_x = ev->crossing.x;
352                 _last_event_y = ev->crossing.y;
353                 enter_notify(&ev->crossing);
354                 // set entered_regionview (among other things)
355                 return RegionView::canvas_group_event (ev);
356
357         case GDK_LEAVE_NOTIFY:
358                 _last_event_x = ev->crossing.x;
359                 _last_event_y = ev->crossing.y;
360                 leave_notify(&ev->crossing);
361                 // reset entered_regionview (among other things)
362                 return RegionView::canvas_group_event (ev);
363
364         case GDK_SCROLL:
365                 if (scroll (&ev->scroll)) {
366                         return true;
367                 }
368                 break;
369
370         case GDK_KEY_PRESS:
371                 return key_press (&ev->key);
372
373         case GDK_KEY_RELEASE:
374                 return key_release (&ev->key);
375
376         case GDK_BUTTON_PRESS:
377                 return button_press (&ev->button);
378
379         case GDK_BUTTON_RELEASE:
380                 r = button_release (&ev->button);
381                 return r;
382
383         case GDK_MOTION_NOTIFY:
384                 _last_event_x = ev->motion.x;
385                 _last_event_y = ev->motion.y;
386                 return motion (&ev->motion);
387
388         default:
389                 break;
390         }
391
392         return RegionView::canvas_group_event (ev);
393 }
394
395 bool
396 MidiRegionView::enter_notify (GdkEventCrossing* ev)
397 {
398         enter_internal();
399
400         _entered = true;
401         return false;
402 }
403
404 bool
405 MidiRegionView::leave_notify (GdkEventCrossing*)
406 {
407         leave_internal();
408
409         _entered = false;
410         return false;
411 }
412
413 void
414 MidiRegionView::mouse_mode_changed ()
415 {
416         // Adjust frame colour (become more transparent for internal tools)
417         set_frame_color();
418
419         if (_entered) {
420                 if (!trackview.editor().internal_editing()) {
421                         /* Switched out of internal editing mode while entered.
422                         Only necessary for leave as a mouse_mode_change over a region
423                         automatically triggers an enter event. */
424                         leave_internal();
425                 }
426                 else if (trackview.editor().current_mouse_mode() == MouseContent) {
427                         // hide cursor and ghost note after changing to internal edit mode
428                         remove_ghost_note ();
429
430                         /* XXX This is problematic as the function is executed for every region
431                         and only for one region _note_entered can be true. Still it's
432                         necessary as to hide the verbose cursor when we're changing from
433                         draw mode to internal edit mode. These lines are the reason why
434                         in some situations no verbose cursor is shown when we enter internal
435                         edit mode over a note. */
436                         if (!_note_entered) {
437                                 hide_verbose_cursor ();
438                         }
439                 }
440         }
441 }
442
443 void
444 MidiRegionView::enter_internal()
445 {
446         if (trackview.editor().current_mouse_mode() == MouseDraw && _mouse_state != AddDragging) {
447                 // Show ghost note under pencil
448                 create_ghost_note(_last_event_x, _last_event_y);
449         }
450
451         if (!_selection.empty()) {
452                 // Grab keyboard for moving selected notes with arrow keys
453                 Keyboard::magic_widget_grab_focus();
454                 _grabbed_keyboard = true;
455         }
456
457         // Lower frame handles below notes so they don't steal events
458         if (frame_handle_start) {
459                 frame_handle_start->lower_to_bottom();
460         }
461         if (frame_handle_end) {
462                 frame_handle_end->lower_to_bottom();
463         }
464 }
465
466 void
467 MidiRegionView::leave_internal()
468 {
469         hide_verbose_cursor ();
470         remove_ghost_note ();
471         _note_entered = false;
472
473         if (_grabbed_keyboard) {
474                 Keyboard::magic_widget_drop_focus();
475                 _grabbed_keyboard = false;
476         }
477
478         // Raise frame handles above notes so they catch events
479         if (frame_handle_start) {
480                 frame_handle_start->raise_to_top();
481         }
482         if (frame_handle_end) {
483                 frame_handle_end->raise_to_top();
484         }
485 }
486
487 bool
488 MidiRegionView::button_press (GdkEventButton* ev)
489 {
490         if (ev->button != 1) {
491                 return false;
492         }
493
494         Editor* editor = dynamic_cast<Editor *> (&trackview.editor());
495         MouseMode m = editor->current_mouse_mode();
496
497         if (m == MouseContent && Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier())) {
498                 _press_cursor_ctx = CursorContext::create(*editor, editor->cursors()->midi_pencil);
499         }
500
501         if (_mouse_state != SelectTouchDragging) {
502
503                 _pressed_button = ev->button;
504                 _mouse_state = Pressed;
505
506                 return true;
507         }
508
509         _pressed_button = ev->button;
510         _mouse_changed_selection = false;
511
512         return true;
513 }
514
515 bool
516 MidiRegionView::button_release (GdkEventButton* ev)
517 {
518         double event_x, event_y;
519
520         if (ev->button != 1) {
521                 return false;
522         }
523
524         event_x = ev->x;
525         event_y = ev->y;
526
527         group->canvas_to_item (event_x, event_y);
528         group->ungrab ();
529
530         PublicEditor& editor = trackview.editor ();
531
532         _press_cursor_ctx.reset();
533
534         switch (_mouse_state) {
535         case Pressed: // Clicked
536
537                 switch (editor.current_mouse_mode()) {
538                 case MouseRange:
539                         /* no motion occurred - simple click */
540                         clear_editor_note_selection ();
541                         _mouse_changed_selection = true;
542                         break;
543
544                 case MouseContent:
545                 case MouseTimeFX:
546                         {
547                                 _mouse_changed_selection = true;
548
549                                 if (Keyboard::is_insert_note_event(ev)) {
550
551                                         double event_x, event_y;
552
553                                         event_x = ev->x;
554                                         event_y = ev->y;
555                                         group->canvas_to_item (event_x, event_y);
556
557                                         Evoral::Beats beats = get_grid_beats(editor.pixel_to_sample(event_x));
558                                         create_note_at (editor.pixel_to_sample (event_x), event_y, beats, true);
559                                 } else {
560                                         clear_editor_note_selection ();
561                                 }
562
563                                 break;
564                         }
565                 case MouseDraw:
566                         {
567                                 Evoral::Beats beats = get_grid_beats(editor.pixel_to_sample(event_x));
568                                 create_note_at (editor.pixel_to_sample (event_x), event_y, beats, true);
569                                 break;
570                         }
571                 default:
572                         break;
573                 }
574
575                 _mouse_state = None;
576                 break;
577
578         case AddDragging:
579                 /* Only create a ghost note when we added a note, not when we were drag-selecting. */
580                 create_ghost_note (ev->x, ev->y);
581         case SelectRectDragging:
582                 editor.drags()->end_grab ((GdkEvent *) ev);
583                 _mouse_state = None;
584                 break;
585
586
587         default:
588                 break;
589         }
590
591         if (_mouse_changed_selection) {
592                 trackview.editor().begin_reversible_selection_op (X_("Mouse Selection Change"));
593                 trackview.editor().commit_reversible_selection_op ();
594         }
595
596         return false;
597 }
598
599 bool
600 MidiRegionView::motion (GdkEventMotion* ev)
601 {
602         PublicEditor& editor = trackview.editor ();
603
604         if (!_note_entered) {
605
606                 if (!_ghost_note && editor.current_mouse_mode() == MouseContent &&
607                     Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier()) &&
608                     _mouse_state != AddDragging) {
609
610                         create_ghost_note (ev->x, ev->y);
611
612                 } else if (_ghost_note && editor.current_mouse_mode() == MouseContent &&
613                            Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier())) {
614
615                         update_ghost_note (ev->x, ev->y);
616
617                 } else if (_ghost_note && editor.current_mouse_mode() == MouseContent) {
618
619                         remove_ghost_note ();
620                         hide_verbose_cursor ();
621
622                 } else if (editor.current_mouse_mode() == MouseDraw) {
623
624                         if (_ghost_note) {
625                                 update_ghost_note (ev->x, ev->y);
626                         }
627                         else {
628                                 create_ghost_note (ev->x, ev->y);
629                         }
630                 }
631         }
632
633         /* any motion immediately hides velocity text that may have been visible */
634
635         for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
636                 (*i)->hide_velocity ();
637         }
638
639         switch (_mouse_state) {
640         case Pressed:
641
642                 if (_pressed_button == 1) {
643
644                         MouseMode m = editor.current_mouse_mode();
645
646                         if (m == MouseDraw || (m == MouseContent && Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier()))) {
647                                 editor.drags()->set (new NoteCreateDrag (dynamic_cast<Editor *> (&editor), group, this), (GdkEvent *) ev);
648                                 _mouse_state = AddDragging;
649                                 remove_ghost_note ();
650                                 hide_verbose_cursor ();
651                                 return true;
652                         } else if (m == MouseContent) {
653                                 editor.drags()->set (new MidiRubberbandSelectDrag (dynamic_cast<Editor *> (&editor), this), (GdkEvent *) ev);
654                                 if (!Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) {
655                                         clear_editor_note_selection ();
656                                         _mouse_changed_selection = true;
657                                 }
658                                 _mouse_state = SelectRectDragging;
659                                 return true;
660                         } else if (m == MouseRange) {
661                                 editor.drags()->set (new MidiVerticalSelectDrag (dynamic_cast<Editor *> (&editor), this), (GdkEvent *) ev);
662                                 _mouse_state = SelectVerticalDragging;
663                                 return true;
664                         }
665                 }
666
667                 return false;
668
669         case SelectRectDragging:
670         case SelectVerticalDragging:
671         case AddDragging:
672                 editor.drags()->motion_handler ((GdkEvent *) ev, false);
673                 break;
674
675         case SelectTouchDragging:
676                 return false;
677
678         default:
679                 break;
680
681         }
682
683         /* we may be dragging some non-note object (eg. patch-change, sysex)
684          */
685
686         return editor.drags()->motion_handler ((GdkEvent *) ev, false);
687 }
688
689
690 bool
691 MidiRegionView::scroll (GdkEventScroll* ev)
692 {
693         if (_selection.empty()) {
694                 return false;
695         }
696
697         if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier) ||
698             Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) {
699                 /* XXX: bit of a hack; allow PrimaryModifier and TertiaryModifier scroll
700                  * through so that it still works for navigation.
701                 */
702                 return false;
703         }
704
705         hide_verbose_cursor ();
706
707         bool fine = !Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
708         Keyboard::ModifierMask mask_together(Keyboard::PrimaryModifier|Keyboard::TertiaryModifier);
709         bool together = Keyboard::modifier_state_contains (ev->state, mask_together);
710
711         if (ev->direction == GDK_SCROLL_UP) {
712                 change_velocities (true, fine, false, together);
713         } else if (ev->direction == GDK_SCROLL_DOWN) {
714                 change_velocities (false, fine, false, together);
715         } else {
716                 /* left, right: we don't use them */
717                 return false;
718         }
719
720         return true;
721 }
722
723 bool
724 MidiRegionView::key_press (GdkEventKey* ev)
725 {
726         /* since GTK bindings are generally activated on press, and since
727            detectable auto-repeat is the name of the game and only sends
728            repeated presses, carry out key actions at key press, not release.
729         */
730
731         bool unmodified = Keyboard::no_modifier_keys_pressed (ev);
732
733         if (unmodified && (ev->keyval == GDK_Alt_L || ev->keyval == GDK_Alt_R)) {
734                 _mouse_state = SelectTouchDragging;
735                 return true;
736
737         } else if (ev->keyval == GDK_Escape && unmodified) {
738                 clear_editor_note_selection ();
739                 _mouse_state = None;
740
741         } else if (ev->keyval == GDK_comma || ev->keyval == GDK_period) {
742
743                 bool start = (ev->keyval == GDK_comma);
744                 bool end = (ev->keyval == GDK_period);
745                 bool shorter = Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier);
746                 bool fine = Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
747
748                 change_note_lengths (fine, shorter, Evoral::Beats(), start, end);
749
750                 return true;
751
752         } else if ((ev->keyval == GDK_BackSpace || ev->keyval == GDK_Delete) && unmodified) {
753
754                 if (_selection.empty()) {
755                         return false;
756                 }
757
758                 delete_selection();
759                 return true;
760
761         } else if (ev->keyval == GDK_Tab || ev->keyval == GDK_ISO_Left_Tab) {
762
763                 trackview.editor().begin_reversible_selection_op (X_("Select Adjacent Note"));
764
765                 if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) {
766                         goto_previous_note (Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier));
767                 } else {
768                         goto_next_note (Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier));
769                 }
770
771                 trackview.editor().commit_reversible_selection_op();
772
773                 return true;
774
775         } else if (ev->keyval == GDK_Up) {
776
777                 bool allow_smush = Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier);
778                 bool fine = !Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
779                 bool together = Keyboard::modifier_state_contains (ev->state, Keyboard::Level4Modifier);
780
781                 if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) {
782                         change_velocities (true, fine, allow_smush, together);
783                 } else {
784                         transpose (true, fine, allow_smush);
785                 }
786                 return true;
787
788         } else if (ev->keyval == GDK_Down) {
789
790                 bool allow_smush = Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier);
791                 bool fine = !Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
792                 bool together = Keyboard::modifier_state_contains (ev->state, Keyboard::Level4Modifier);
793
794                 if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) {
795                         change_velocities (false, fine, allow_smush, together);
796                 } else {
797                         transpose (false, fine, allow_smush);
798                 }
799                 return true;
800
801         } else if (ev->keyval == GDK_Left) {
802
803                 bool fine = !Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
804                 nudge_notes (false, fine);
805                 return true;
806
807         } else if (ev->keyval == GDK_Right) {
808
809                 bool fine = !Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
810                 nudge_notes (true, fine);
811                 return true;
812
813         } else if (ev->keyval == GDK_c && unmodified) {
814                 channel_edit ();
815                 return true;
816
817         } else if (ev->keyval == GDK_v && unmodified) {
818                 velocity_edit ();
819                 return true;
820         }
821
822         return false;
823 }
824
825 bool
826 MidiRegionView::key_release (GdkEventKey* ev)
827 {
828         if ((_mouse_state == SelectTouchDragging) && (ev->keyval == GDK_Alt_L || ev->keyval == GDK_Alt_R)) {
829                 _mouse_state = None;
830                 return true;
831         }
832         return false;
833 }
834
835 void
836 MidiRegionView::channel_edit ()
837 {
838         if (_selection.empty()) {
839                 return;
840         }
841
842         /* pick a note somewhat at random (since Selection is a set<>) to
843          * provide the "current" channel for the dialog.
844          */
845
846         uint8_t current_channel = (*_selection.begin())->note()->channel ();
847         MidiChannelDialog channel_dialog (current_channel);
848         int ret = channel_dialog.run ();
849
850         switch (ret) {
851         case Gtk::RESPONSE_OK:
852                 break;
853         default:
854                 return;
855         }
856
857         uint8_t new_channel = channel_dialog.active_channel ();
858
859         start_note_diff_command (_("channel edit"));
860
861         for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
862                 Selection::iterator next = i;
863                 ++next;
864                 change_note_channel (*i, new_channel);
865                 i = next;
866         }
867
868         apply_diff ();
869 }
870
871 void
872 MidiRegionView::velocity_edit ()
873 {
874         if (_selection.empty()) {
875                 return;
876         }
877
878         /* pick a note somewhat at random (since Selection is a set<>) to
879          * provide the "current" velocity for the dialog.
880          */
881
882         uint8_t current_velocity = (*_selection.begin())->note()->velocity ();
883         MidiVelocityDialog velocity_dialog (current_velocity);
884         int ret = velocity_dialog.run ();
885
886         switch (ret) {
887         case Gtk::RESPONSE_OK:
888                 break;
889         default:
890                 return;
891         }
892
893         uint8_t new_velocity = velocity_dialog.velocity ();
894
895         start_note_diff_command (_("velocity edit"));
896
897         for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
898                 Selection::iterator next = i;
899                 ++next;
900                 change_note_velocity (*i, new_velocity, false);
901                 i = next;
902         }
903
904         apply_diff ();
905 }
906
907 void
908 MidiRegionView::show_list_editor ()
909 {
910         if (!_list_editor) {
911                 _list_editor = new MidiListEditor (trackview.session(), midi_region(), midi_view()->midi_track());
912         }
913         _list_editor->present ();
914 }
915
916 /** Add a note to the model, and the view, at a canvas (click) coordinate.
917  * \param t time in frames relative to the position of the region
918  * \param y vertical position in pixels
919  * \param length duration of the note in beats
920  * \param snap_t true to snap t to the grid, otherwise false.
921  */
922 void
923 MidiRegionView::create_note_at (framepos_t t, double y, Evoral::Beats length, bool snap_t)
924 {
925         if (length < 2 * DBL_EPSILON) {
926                 return;
927         }
928
929         MidiTimeAxisView* const mtv  = dynamic_cast<MidiTimeAxisView*>(&trackview);
930         MidiStreamView* const   view = mtv->midi_view();
931
932         // Start of note in frames relative to region start
933         if (snap_t) {
934                 framecnt_t grid_frames;
935                 t = snap_frame_to_grid_underneath (t, grid_frames);
936         }
937
938         const MidiModel::TimeType beat_time = region_frames_to_region_beats(
939                 t + _region->start());
940
941         const double  note     = view->y_to_note(y);
942         const uint8_t chan     = mtv->get_channel_for_add();
943         const uint8_t velocity = get_velocity_for_add(beat_time);
944
945         const boost::shared_ptr<NoteType> new_note(
946                 new NoteType (chan, beat_time, length, (uint8_t)note, velocity));
947
948         if (_model->contains (new_note)) {
949                 return;
950         }
951
952         view->update_note_range(new_note->note());
953
954         start_note_diff_command(_("add note"));
955
956         clear_editor_note_selection ();
957         note_diff_add_note (new_note, true, false);
958
959         apply_diff();
960
961         play_midi_note (new_note);
962 }
963
964 void
965 MidiRegionView::clear_events ()
966 {
967         // clear selection without signaling
968         clear_selection_internal ();
969
970         MidiGhostRegion* gr;
971         for (std::vector<GhostRegion*>::iterator g = ghosts.begin(); g != ghosts.end(); ++g) {
972                 if ((gr = dynamic_cast<MidiGhostRegion*>(*g)) != 0) {
973                         gr->clear_events();
974                 }
975         }
976
977         for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
978                 delete *i;
979         }
980
981         _events.clear();
982         _patch_changes.clear();
983         _sys_exes.clear();
984         _optimization_iterator = _events.end();
985 }
986
987 void
988 MidiRegionView::display_model(boost::shared_ptr<MidiModel> model)
989 {
990         _model = model;
991
992         content_connection.disconnect ();
993         _model->ContentsChanged.connect (content_connection, invalidator (*this), boost::bind (&MidiRegionView::redisplay_model, this), gui_context());
994         /* Don't signal as nobody else needs to know until selection has been altered. */
995         clear_events ();
996
997         if (_enable_display) {
998                 redisplay_model();
999         }
1000 }
1001
1002 void
1003 MidiRegionView::start_note_diff_command (string name)
1004 {
1005         if (!_note_diff_command) {
1006                 trackview.editor().begin_reversible_command (name);
1007                 _note_diff_command = _model->new_note_diff_command (name);
1008         }
1009 }
1010
1011 void
1012 MidiRegionView::note_diff_add_note (const boost::shared_ptr<NoteType> note, bool selected, bool show_velocity)
1013 {
1014         if (_note_diff_command) {
1015                 _note_diff_command->add (note);
1016         }
1017         if (selected) {
1018                 _marked_for_selection.insert(note);
1019         }
1020         if (show_velocity) {
1021                 _marked_for_velocity.insert(note);
1022         }
1023 }
1024
1025 void
1026 MidiRegionView::note_diff_remove_note (NoteBase* ev)
1027 {
1028         if (_note_diff_command && ev->note()) {
1029                 _note_diff_command->remove(ev->note());
1030         }
1031 }
1032
1033 void
1034 MidiRegionView::note_diff_add_change (NoteBase* ev,
1035                                       MidiModel::NoteDiffCommand::Property property,
1036                                       uint8_t val)
1037 {
1038         if (_note_diff_command) {
1039                 _note_diff_command->change (ev->note(), property, val);
1040         }
1041 }
1042
1043 void
1044 MidiRegionView::note_diff_add_change (NoteBase* ev,
1045                                       MidiModel::NoteDiffCommand::Property property,
1046                                       Evoral::Beats val)
1047 {
1048         if (_note_diff_command) {
1049                 _note_diff_command->change (ev->note(), property, val);
1050         }
1051 }
1052
1053 void
1054 MidiRegionView::apply_diff (bool as_subcommand)
1055 {
1056         bool add_or_remove;
1057         bool commit = false;
1058
1059         if (!_note_diff_command) {
1060                 return;
1061         }
1062
1063         if ((add_or_remove = _note_diff_command->adds_or_removes())) {
1064                 // Mark all selected notes for selection when model reloads
1065                 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
1066                         _marked_for_selection.insert((*i)->note());
1067                 }
1068         }
1069
1070         midi_view()->midi_track()->midi_playlist()->region_edited(
1071                 _region, _note_diff_command);
1072
1073         if (as_subcommand) {
1074                 _model->apply_command_as_subcommand (*trackview.session(), _note_diff_command);
1075         } else {
1076                 _model->apply_command (*trackview.session(), _note_diff_command);
1077                 commit = true;
1078         }
1079
1080         _note_diff_command = 0;
1081
1082         if (add_or_remove) {
1083                 _marked_for_selection.clear();
1084         }
1085
1086         _marked_for_velocity.clear();
1087         if (commit) {
1088                 trackview.editor().commit_reversible_command ();
1089         }
1090 }
1091
1092 void
1093 MidiRegionView::abort_command()
1094 {
1095         delete _note_diff_command;
1096         _note_diff_command = 0;
1097         clear_editor_note_selection();
1098 }
1099
1100 NoteBase*
1101 MidiRegionView::find_canvas_note (boost::shared_ptr<NoteType> note)
1102 {
1103         if (_optimization_iterator != _events.end()) {
1104                 ++_optimization_iterator;
1105         }
1106
1107         if (_optimization_iterator != _events.end() && (*_optimization_iterator)->note() == note) {
1108                 return *_optimization_iterator;
1109         }
1110
1111         for (_optimization_iterator = _events.begin(); _optimization_iterator != _events.end(); ++_optimization_iterator) {
1112                 if ((*_optimization_iterator)->note() == note) {
1113                         return *_optimization_iterator;
1114                 }
1115         }
1116
1117         return 0;
1118 }
1119
1120 /** This version finds any canvas note matching the supplied note. */
1121 NoteBase*
1122 MidiRegionView::find_canvas_note (NoteType note)
1123 {
1124         Events::iterator it;
1125
1126         for (it = _events.begin(); it != _events.end(); ++it) {
1127                 if (*((*it)->note()) == note) {
1128                         return *it;
1129                 }
1130         }
1131
1132         return 0;
1133 }
1134
1135 void
1136 MidiRegionView::get_events (Events& e, Evoral::Sequence<Evoral::Beats>::NoteOperator op, uint8_t val, int chan_mask)
1137 {
1138         MidiModel::Notes notes;
1139         _model->get_notes (notes, op, val, chan_mask);
1140
1141         for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
1142                 NoteBase* cne = find_canvas_note (*n);
1143                 if (cne) {
1144                         e.push_back (cne);
1145                 }
1146         }
1147 }
1148
1149 void
1150 MidiRegionView::redisplay_model()
1151 {
1152         if (_active_notes) {
1153                 // Currently recording
1154                 const framecnt_t zoom = trackview.editor().get_current_zoom();
1155                 if (zoom != _last_display_zoom) {
1156                         /* Update resolved canvas notes to reflect changes in zoom without
1157                            touching model.  Leave active notes (with length 0) alone since
1158                            they are being extended. */
1159                         for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
1160                                 if ((*i)->note()->length() > 0) {
1161                                         update_note(*i);
1162                                 }
1163                         }
1164                         _last_display_zoom = zoom;
1165                 }
1166                 return;
1167         }
1168
1169         if (!_model) {
1170                 return;
1171         }
1172
1173         for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
1174                 (*i)->invalidate ();
1175         }
1176
1177         MidiModel::ReadLock lock(_model->read_lock());
1178
1179         MidiModel::Notes& notes (_model->notes());
1180         _optimization_iterator = _events.begin();
1181
1182         bool empty_when_starting = _events.empty();
1183
1184         for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
1185
1186                 boost::shared_ptr<NoteType> note (*n);
1187                 NoteBase* cne;
1188                 bool visible;
1189
1190                 if (note_in_region_range (note, visible)) {
1191
1192                         if (!empty_when_starting && (cne = find_canvas_note (note)) != 0) {
1193
1194                                 cne->validate ();
1195                                 update_note (cne);
1196
1197                                 if (visible) {
1198                                         cne->show ();
1199                                 } else {
1200                                         cne->hide ();
1201                                 }
1202
1203                         } else {
1204
1205                                 cne = add_note (note, visible);
1206                         }
1207
1208                         set<boost::shared_ptr<NoteType> >::iterator it;
1209                         for (it = _pending_note_selection.begin(); it != _pending_note_selection.end(); ++it) {
1210                                 if (*(*it) == *note) {
1211                                         add_to_selection (cne);
1212                                 }
1213                         }
1214
1215                 } else {
1216
1217                         if (!empty_when_starting && (cne = find_canvas_note (note)) != 0) {
1218                                 cne->validate ();
1219                                 cne->hide ();
1220                         }
1221                 }
1222         }
1223
1224         /* remove note items that are no longer valid */
1225
1226         if (!empty_when_starting) {
1227                 for (Events::iterator i = _events.begin(); i != _events.end(); ) {
1228                         if (!(*i)->valid ()) {
1229
1230                                 for (vector<GhostRegion*>::iterator j = ghosts.begin(); j != ghosts.end(); ++j) {
1231                                         MidiGhostRegion* gr = dynamic_cast<MidiGhostRegion*> (*j);
1232                                         if (gr) {
1233                                                 gr->remove_note (*i);
1234                                         }
1235                                 }
1236
1237                                 delete *i;
1238                                 i = _events.erase (i);
1239
1240                         } else {
1241                                 ++i;
1242                         }
1243                 }
1244         }
1245
1246         _patch_changes.clear();
1247         _sys_exes.clear();
1248
1249         display_sysexes();
1250         display_patch_changes ();
1251
1252         _marked_for_selection.clear ();
1253         _marked_for_velocity.clear ();
1254         _pending_note_selection.clear ();
1255
1256         /* we may have caused _events to contain things out of order (e.g. if a note
1257            moved earlier or later). we don't generally need them in time order, but
1258            make a note that a sort is required for those cases that require it.
1259         */
1260
1261         _sort_needed = true;
1262 }
1263
1264 void
1265 MidiRegionView::display_patch_changes ()
1266 {
1267         MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1268         uint16_t chn_mask = mtv->midi_track()->get_playback_channel_mask();
1269
1270         for (uint8_t i = 0; i < 16; ++i) {
1271                 display_patch_changes_on_channel (i, chn_mask & (1 << i));
1272         }
1273 }
1274
1275 /** @param active_channel true to display patch changes fully, false to display
1276  * them `greyed-out' (as on an inactive channel)
1277  */
1278 void
1279 MidiRegionView::display_patch_changes_on_channel (uint8_t channel, bool active_channel)
1280 {
1281         for (MidiModel::PatchChanges::const_iterator i = _model->patch_changes().begin(); i != _model->patch_changes().end(); ++i) {
1282
1283                 if ((*i)->channel() != channel) {
1284                         continue;
1285                 }
1286
1287                 const string patch_name = instrument_info().get_patch_name ((*i)->bank(), (*i)->program(), channel);
1288                 add_canvas_patch_change (*i, patch_name, active_channel);
1289         }
1290 }
1291
1292 void
1293 MidiRegionView::display_sysexes()
1294 {
1295         bool have_periodic_system_messages = false;
1296         bool display_periodic_messages = true;
1297
1298         if (!UIConfiguration::instance().get_never_display_periodic_midi()) {
1299
1300                 for (MidiModel::SysExes::const_iterator i = _model->sysexes().begin(); i != _model->sysexes().end(); ++i) {
1301                         const boost::shared_ptr<const Evoral::MIDIEvent<Evoral::Beats> > mev =
1302                                 boost::static_pointer_cast<const Evoral::MIDIEvent<Evoral::Beats> > (*i);
1303
1304                         if (mev) {
1305                                 if (mev->is_spp() || mev->is_mtc_quarter() || mev->is_mtc_full()) {
1306                                         have_periodic_system_messages = true;
1307                                         break;
1308                                 }
1309                         }
1310                 }
1311
1312                 if (have_periodic_system_messages) {
1313                         double zoom = trackview.editor().get_current_zoom (); // frames per pixel
1314
1315                         /* get an approximate value for the number of samples per video frame */
1316
1317                         double video_frame = trackview.session()->frame_rate() * (1.0/30);
1318
1319                         /* if we are zoomed out beyond than the cutoff (i.e. more
1320                          * frames per pixel than frames per 4 video frames), don't
1321                          * show periodic sysex messages.
1322                          */
1323
1324                         if (zoom > (video_frame*4)) {
1325                                 display_periodic_messages = false;
1326                         }
1327                 }
1328         } else {
1329                 display_periodic_messages = false;
1330         }
1331
1332         for (MidiModel::SysExes::const_iterator i = _model->sysexes().begin(); i != _model->sysexes().end(); ++i) {
1333
1334                 const boost::shared_ptr<const Evoral::MIDIEvent<Evoral::Beats> > mev =
1335                         boost::static_pointer_cast<const Evoral::MIDIEvent<Evoral::Beats> > (*i);
1336
1337                 Evoral::Beats time = (*i)->time();
1338
1339                 if (mev) {
1340                         if (mev->is_spp() || mev->is_mtc_quarter() || mev->is_mtc_full()) {
1341                                 if (!display_periodic_messages) {
1342                                         continue;
1343                                 }
1344                         }
1345                 }
1346
1347                 ostringstream str;
1348                 str << hex;
1349                 for (uint32_t b = 0; b < (*i)->size(); ++b) {
1350                         str << int((*i)->buffer()[b]);
1351                         if (b != (*i)->size() -1) {
1352                                 str << " ";
1353                         }
1354                 }
1355                 string text = str.str();
1356
1357                 const double x = trackview.editor().sample_to_pixel(source_beats_to_region_frames(time));
1358
1359                 double height = midi_stream_view()->contents_height();
1360
1361                 // CAIROCANVAS: no longer passing *i (the sysex event) to the
1362                 // SysEx canvas object!!!
1363
1364                 boost::shared_ptr<SysEx> sysex = boost::shared_ptr<SysEx>(
1365                         new SysEx (*this, _note_group, text, height, x, 1.0));
1366
1367                 // Show unless message is beyond the region bounds
1368                 if (time - _region->start() >= _region->length() || time < _region->start()) {
1369                         sysex->hide();
1370                 } else {
1371                         sysex->show();
1372                 }
1373
1374                 _sys_exes.push_back(sysex);
1375         }
1376 }
1377
1378 MidiRegionView::~MidiRegionView ()
1379 {
1380         in_destructor = true;
1381
1382         hide_verbose_cursor ();
1383
1384         delete _list_editor;
1385
1386         RegionViewGoingAway (this); /* EMIT_SIGNAL */
1387
1388         if (_active_notes) {
1389                 end_write();
1390         }
1391
1392         _selection.clear();
1393         clear_events ();
1394
1395         delete _note_group;
1396         delete _note_diff_command;
1397         delete _step_edit_cursor;
1398         delete _temporary_note_group;
1399 }
1400
1401 void
1402 MidiRegionView::region_resized (const PropertyChange& what_changed)
1403 {
1404         RegionView::region_resized(what_changed);
1405
1406         if (what_changed.contains (ARDOUR::Properties::position)) {
1407                 _region_relative_time_converter.set_origin_b(_region->position());
1408                 _region_relative_time_converter_double.set_origin_b(_region->position());
1409                 set_duration(_region->length(), 0);
1410                 if (_enable_display) {
1411                         redisplay_model();
1412                 }
1413         }
1414
1415         if (what_changed.contains (ARDOUR::Properties::start) ||
1416             what_changed.contains (ARDOUR::Properties::position)) {
1417                 _source_relative_time_converter.set_origin_b (_region->position() - _region->start());
1418         }
1419 }
1420
1421 void
1422 MidiRegionView::reset_width_dependent_items (double pixel_width)
1423 {
1424         RegionView::reset_width_dependent_items(pixel_width);
1425
1426         if (_enable_display) {
1427                 redisplay_model();
1428         }
1429
1430         for (PatchChanges::iterator x = _patch_changes.begin(); x != _patch_changes.end(); ++x) {
1431                 if ((*x)->canvas_item()->width() >= _pixel_width) {
1432                         (*x)->hide();
1433                 } else {
1434                         (*x)->show();
1435                 }
1436         }
1437
1438         move_step_edit_cursor (_step_edit_cursor_position);
1439         set_step_edit_cursor_width (_step_edit_cursor_width);
1440 }
1441
1442 void
1443 MidiRegionView::set_height (double height)
1444 {
1445         double old_height = _height;
1446         RegionView::set_height(height);
1447
1448         apply_note_range (midi_stream_view()->lowest_note(),
1449                           midi_stream_view()->highest_note(),
1450                           height != old_height);
1451
1452         if (name_text) {
1453                 name_text->raise_to_top();
1454         }
1455
1456         for (PatchChanges::iterator x = _patch_changes.begin(); x != _patch_changes.end(); ++x) {
1457                 (*x)->set_height (midi_stream_view()->contents_height());
1458         }
1459
1460         if (_step_edit_cursor) {
1461                 _step_edit_cursor->set_y1 (midi_stream_view()->contents_height());
1462         }
1463 }
1464
1465
1466 /** Apply the current note range from the stream view
1467  * by repositioning/hiding notes as necessary
1468  */
1469 void
1470 MidiRegionView::apply_note_range (uint8_t min, uint8_t max, bool force)
1471 {
1472         if (!_enable_display) {
1473                 return;
1474         }
1475
1476         if (!force && _current_range_min == min && _current_range_max == max) {
1477                 return;
1478         }
1479
1480         _current_range_min = min;
1481         _current_range_max = max;
1482
1483         for (Events::const_iterator i = _events.begin(); i != _events.end(); ++i) {
1484                 NoteBase* event = *i;
1485                 boost::shared_ptr<NoteType> note (event->note());
1486
1487                 if (note->note() < _current_range_min ||
1488                     note->note() > _current_range_max) {
1489                         event->hide();
1490                 } else {
1491                         event->show();
1492                 }
1493
1494                 if (Note* cnote = dynamic_cast<Note*>(event)) {
1495
1496                         const double y0 = 1. + floor (midi_stream_view()->note_to_y(note->note()));
1497                         const double y1 = y0 + std::max(1., floor(midi_stream_view()->note_height()) - 1.);
1498
1499                         if (y0 < 0 || y1 >= _height) {
1500                                 /* During DnD, the region uses the 'old/current'
1501                                  * midi_stream_view()'s range and its position/height calculation.
1502                                  *
1503                                  * Ideally DnD would decouple the midi_stream_view() for the
1504                                  * region(s) being dragged and set it to the target's range
1505                                  * (or in case of the drop-zone, FullRange).
1506                                  * but I don't see how this can be done without major rework.
1507                                  *
1508                                  * For now, just prevent visual bleeding of events in case
1509                                  * the target-track is smaller.
1510                                  */
1511                                 event->hide();
1512                                 continue;
1513                         }
1514                         cnote->set_y0 (y0);
1515                         cnote->set_y1 (y1);
1516
1517                 } else if (Hit* chit = dynamic_cast<Hit*>(event)) {
1518                         update_hit (chit);
1519                 }
1520         }
1521 }
1522
1523 GhostRegion*
1524 MidiRegionView::add_ghost (TimeAxisView& tv)
1525 {
1526         double unit_position = _region->position () / samples_per_pixel;
1527         MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*>(&tv);
1528         MidiGhostRegion* ghost;
1529
1530         if (mtv && mtv->midi_view()) {
1531                 /* if ghost is inserted into midi track, use a dedicated midi ghost canvas group
1532                    to allow having midi notes on top of note lines and waveforms.
1533                 */
1534                 ghost = new MidiGhostRegion (*this, *mtv->midi_view(), trackview, unit_position);
1535         } else {
1536                 ghost = new MidiGhostRegion (*this, tv, trackview, unit_position);
1537         }
1538
1539         for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
1540                 ghost->add_note(*i);
1541         }
1542
1543         ghost->set_height ();
1544         ghost->set_duration (_region->length() / samples_per_pixel);
1545         ghosts.push_back (ghost);
1546
1547         return ghost;
1548 }
1549
1550
1551 /** Begin tracking note state for successive calls to add_event
1552  */
1553 void
1554 MidiRegionView::begin_write()
1555 {
1556         if (_active_notes) {
1557                 delete[] _active_notes;
1558         }
1559         _active_notes = new Note*[128];
1560         for (unsigned i = 0; i < 128; ++i) {
1561                 _active_notes[i] = 0;
1562         }
1563 }
1564
1565
1566 /** Destroy note state for add_event
1567  */
1568 void
1569 MidiRegionView::end_write()
1570 {
1571         delete[] _active_notes;
1572         _active_notes = 0;
1573         _marked_for_selection.clear();
1574         _marked_for_velocity.clear();
1575 }
1576
1577
1578 /** Resolve an active MIDI note (while recording).
1579  */
1580 void
1581 MidiRegionView::resolve_note(uint8_t note, Evoral::Beats end_time)
1582 {
1583         if (midi_view()->note_mode() != Sustained) {
1584                 return;
1585         }
1586
1587         if (_active_notes && _active_notes[note]) {
1588                 /* Set note length so update_note() works.  Note this is a local note
1589                    for recording, not from a model, so we can safely mess with it. */
1590                 _active_notes[note]->note()->set_length(
1591                         end_time - _active_notes[note]->note()->time());
1592
1593                 /* End time is relative to the region being recorded. */
1594                 const framepos_t end_time_frames = region_beats_to_region_frames(end_time);
1595
1596                 _active_notes[note]->set_x1 (trackview.editor().sample_to_pixel(end_time_frames));
1597                 _active_notes[note]->set_outline_all ();
1598                 _active_notes[note] = 0;
1599         }
1600 }
1601
1602
1603 /** Extend active notes to rightmost edge of region (if length is changed)
1604  */
1605 void
1606 MidiRegionView::extend_active_notes()
1607 {
1608         if (!_active_notes) {
1609                 return;
1610         }
1611
1612         for (unsigned i = 0; i < 128; ++i) {
1613                 if (_active_notes[i]) {
1614                         _active_notes[i]->set_x1(
1615                                 trackview.editor().sample_to_pixel(_region->length()));
1616                 }
1617         }
1618 }
1619
1620 void
1621 MidiRegionView::play_midi_note(boost::shared_ptr<NoteType> note)
1622 {
1623         if (_no_sound_notes || !UIConfiguration::instance().get_sound_midi_notes()) {
1624                 return;
1625         }
1626
1627         RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
1628
1629         if (!route_ui || !route_ui->midi_track()) {
1630                 return;
1631         }
1632
1633         NotePlayer* np = new NotePlayer (route_ui->midi_track ());
1634         np->add (note);
1635         np->play ();
1636
1637         /* NotePlayer deletes itself */
1638 }
1639
1640 void
1641 MidiRegionView::start_playing_midi_note(boost::shared_ptr<NoteType> note)
1642 {
1643         const std::vector< boost::shared_ptr<NoteType> > notes(1, note);
1644         start_playing_midi_chord(notes);
1645 }
1646
1647 void
1648 MidiRegionView::start_playing_midi_chord (vector<boost::shared_ptr<NoteType> > notes)
1649 {
1650         if (_no_sound_notes || !UIConfiguration::instance().get_sound_midi_notes()) {
1651                 return;
1652         }
1653
1654         RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
1655
1656         if (!route_ui || !route_ui->midi_track()) {
1657                 return;
1658         }
1659
1660         NotePlayer* player = new NotePlayer (route_ui->midi_track());
1661
1662         for (vector<boost::shared_ptr<NoteType> >::iterator n = notes.begin(); n != notes.end(); ++n) {
1663                 player->add (*n);
1664         }
1665
1666         player->play ();
1667 }
1668
1669
1670 bool
1671 MidiRegionView::note_in_region_range (const boost::shared_ptr<NoteType> note, bool& visible) const
1672 {
1673         /* This is imprecise due to all the conversion conversion involved, so only
1674            hide notes if they seem to start more than one tick before the start. */
1675         const framecnt_t tick_frames       = Evoral::Beats::tick().to_ticks(trackview.session()->frame_rate());
1676         const framepos_t note_start_frames = source_beats_to_region_frames (note->time());
1677         const bool       outside           = ((note_start_frames <= -tick_frames) ||
1678                                               (note_start_frames >= _region->length()));
1679
1680         visible = (note->note() >= midi_stream_view()->lowest_note()) &&
1681                 (note->note() <= midi_stream_view()->highest_note());
1682
1683         return !outside;
1684 }
1685
1686 void
1687 MidiRegionView::update_note (NoteBase* note, bool update_ghost_regions)
1688 {
1689         Note* sus = NULL;
1690         Hit*  hit = NULL;
1691         if ((sus = dynamic_cast<Note*>(note))) {
1692                 update_sustained(sus, update_ghost_regions);
1693         } else if ((hit = dynamic_cast<Hit*>(note))) {
1694                 update_hit(hit, update_ghost_regions);
1695         }
1696 }
1697
1698 /** Update a canvas note's size from its model note.
1699  *  @param ev Canvas note to update.
1700  *  @param update_ghost_regions true to update the note in any ghost regions that we have, otherwise false.
1701  */
1702 void
1703 MidiRegionView::update_sustained (Note* ev, bool update_ghost_regions)
1704 {
1705         boost::shared_ptr<NoteType> note = ev->note();
1706         const double x = trackview.editor().sample_to_pixel (source_beats_to_region_frames (note->time()));
1707         const double y0 = 1 + floor(midi_stream_view()->note_to_y(note->note()));
1708
1709         ev->set_x0 (x);
1710         ev->set_y0 (y0);
1711
1712         /* trim note display to not overlap the end of its region */
1713
1714         if (note->length() > 0) {
1715                 const framepos_t note_end_frames = min (source_beats_to_region_frames (note->end_time()), _region->length());
1716                 ev->set_x1 (std::max(1., trackview.editor().sample_to_pixel (note_end_frames)) - 1);
1717         } else {
1718                 ev->set_x1 (std::max(1., trackview.editor().sample_to_pixel (_region->length())) - 1);
1719         }
1720
1721         ev->set_y1 (y0 + std::max(1., floor(midi_stream_view()->note_height()) - 1));
1722
1723         if (!note->length()) {
1724                 if (_active_notes && note->note() < 128) {
1725                         Note* const old_rect = _active_notes[note->note()];
1726                         if (old_rect) {
1727                                 /* There is an active note on this key, so we have a stuck
1728                                    note.  Finish the old rectangle here. */
1729                                 old_rect->set_x1 (x);
1730                                 old_rect->set_outline_all ();
1731                         }
1732                         _active_notes[note->note()] = ev;
1733                 }
1734                 /* outline all but right edge */
1735                 ev->set_outline_what (ArdourCanvas::Rectangle::What (
1736                                               ArdourCanvas::Rectangle::TOP|
1737                                               ArdourCanvas::Rectangle::LEFT|
1738                                               ArdourCanvas::Rectangle::BOTTOM));
1739         } else {
1740                 /* outline all edges */
1741                 ev->set_outline_all ();
1742         }
1743
1744         // Update color in case velocity has changed
1745         ev->set_fill_color(ev->base_color());
1746         ev->set_outline_color(ev->calculate_outline(ev->base_color(), ev->selected()));
1747
1748         if (update_ghost_regions) {
1749                 for (std::vector<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
1750                         MidiGhostRegion* gr = dynamic_cast<MidiGhostRegion*> (*i);
1751                         if (gr) {
1752                                 gr->update_note (ev);
1753                         }
1754                 }
1755         }
1756 }
1757
1758 void
1759 MidiRegionView::update_hit (Hit* ev, bool update_ghost_regions)
1760 {
1761         boost::shared_ptr<NoteType> note = ev->note();
1762
1763         const framepos_t note_start_frames = source_beats_to_region_frames(note->time());
1764         const double x = trackview.editor().sample_to_pixel(note_start_frames);
1765         const double diamond_size = std::max(1., floor(midi_stream_view()->note_height()) - 2.);
1766         const double y = 1.5 + floor(midi_stream_view()->note_to_y(note->note())) + diamond_size * .5;
1767
1768         // see DnD note in MidiRegionView::apply_note_range() above
1769         if (y <= 0 || y >= _height) {
1770                 ev->hide();
1771         } else {
1772                 ev->show();
1773         }
1774
1775         ev->set_position (ArdourCanvas::Duple (x, y));
1776         ev->set_height (diamond_size);
1777
1778         // Update color in case velocity has changed
1779         ev->set_fill_color(ev->base_color());
1780         ev->set_outline_color(ev->calculate_outline(ev->base_color(), ev->selected()));
1781
1782         if (update_ghost_regions) {
1783                 for (std::vector<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
1784                         MidiGhostRegion* gr = dynamic_cast<MidiGhostRegion*> (*i);
1785                         if (gr) {
1786                                 gr->update_note (ev);
1787                         }
1788                 }
1789         }
1790 }
1791
1792 /** Add a MIDI note to the view (with length).
1793  *
1794  * If in sustained mode, notes with length 0 will be considered active
1795  * notes, and resolve_note should be called when the corresponding note off
1796  * event arrives, to properly display the note.
1797  */
1798 NoteBase*
1799 MidiRegionView::add_note(const boost::shared_ptr<NoteType> note, bool visible)
1800 {
1801         NoteBase* event = 0;
1802
1803         if (midi_view()->note_mode() == Sustained) {
1804
1805                 Note* ev_rect = new Note (*this, _note_group, note);
1806
1807                 update_sustained (ev_rect);
1808
1809                 event = ev_rect;
1810
1811         } else if (midi_view()->note_mode() == Percussive) {
1812
1813                 const double diamond_size = std::max(1., floor(midi_stream_view()->note_height()) - 2.);
1814
1815                 Hit* ev_diamond = new Hit (*this, _note_group, diamond_size, note);
1816
1817                 update_hit (ev_diamond);
1818
1819                 event = ev_diamond;
1820
1821         } else {
1822                 event = 0;
1823         }
1824
1825         if (event) {
1826                 MidiGhostRegion* gr;
1827
1828                 for (std::vector<GhostRegion*>::iterator g = ghosts.begin(); g != ghosts.end(); ++g) {
1829                         if ((gr = dynamic_cast<MidiGhostRegion*>(*g)) != 0) {
1830                                 gr->add_note(event);
1831                         }
1832                 }
1833
1834                 if (_marked_for_selection.find(note) != _marked_for_selection.end()) {
1835                         note_selected(event, true);
1836                 }
1837
1838                 if (_marked_for_velocity.find(note) != _marked_for_velocity.end()) {
1839                         event->show_velocity();
1840                 }
1841
1842                 event->on_channel_selection_change (get_selected_channels());
1843                 _events.push_back(event);
1844
1845                 if (visible) {
1846                         event->show();
1847                 } else {
1848                         event->hide ();
1849                 }
1850         }
1851
1852         MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1853         MidiStreamView* const view = mtv->midi_view();
1854
1855         view->update_note_range (note->note());
1856         return event;
1857 }
1858
1859 void
1860 MidiRegionView::step_add_note (uint8_t channel, uint8_t number, uint8_t velocity,
1861                                Evoral::Beats pos, Evoral::Beats len)
1862 {
1863         boost::shared_ptr<NoteType> new_note (new NoteType (channel, pos, len, number, velocity));
1864
1865         /* potentially extend region to hold new note */
1866
1867         framepos_t end_frame = source_beats_to_absolute_frames (new_note->end_time());
1868         framepos_t region_end = _region->last_frame();
1869
1870         if (end_frame > region_end) {
1871                 _region->set_length (end_frame - _region->position());
1872         }
1873
1874         MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1875         MidiStreamView* const view = mtv->midi_view();
1876
1877         view->update_note_range(new_note->note());
1878
1879         _marked_for_selection.clear ();
1880
1881         start_note_diff_command (_("step add"));
1882
1883         clear_editor_note_selection ();
1884         note_diff_add_note (new_note, true, false);
1885
1886         apply_diff();
1887
1888         // last_step_edit_note = new_note;
1889 }
1890
1891 void
1892 MidiRegionView::step_sustain (Evoral::Beats beats)
1893 {
1894         change_note_lengths (false, false, beats, false, true);
1895 }
1896
1897 /** Add a new patch change flag to the canvas.
1898  * @param patch the patch change to add
1899  * @param the text to display in the flag
1900  * @param active_channel true to display the flag as on an active channel, false to grey it out for an inactive channel.
1901  */
1902 void
1903 MidiRegionView::add_canvas_patch_change (MidiModel::PatchChangePtr patch, const string& displaytext, bool /*active_channel*/)
1904 {
1905         framecnt_t region_frames = source_beats_to_region_frames (patch->time());
1906         const double x = trackview.editor().sample_to_pixel (region_frames);
1907
1908         double const height = midi_stream_view()->contents_height();
1909
1910         // CAIROCANVAS: active_channel info removed from PatcChange constructor
1911         // so we need to do something more sophisticated to keep its color
1912         // appearance (MidiPatchChangeFill/MidiPatchChangeInactiveChannelFill)
1913         // up to date.
1914
1915         boost::shared_ptr<PatchChange> patch_change = boost::shared_ptr<PatchChange>(
1916                 new PatchChange(*this, group,
1917                                 displaytext,
1918                                 height,
1919                                 x, 1.0,
1920                                 instrument_info(),
1921                                 patch));
1922
1923         if (patch_change->item().width() < _pixel_width) {
1924                 // Show unless patch change is beyond the region bounds
1925                 if (region_frames < 0 || region_frames >= _region->length()) {
1926                         patch_change->hide();
1927                 } else {
1928                         patch_change->show();
1929                 }
1930         } else {
1931                 patch_change->hide ();
1932         }
1933
1934         _patch_changes.push_back (patch_change);
1935 }
1936
1937 MIDI::Name::PatchPrimaryKey
1938 MidiRegionView::patch_change_to_patch_key (MidiModel::PatchChangePtr p)
1939 {
1940         return MIDI::Name::PatchPrimaryKey (p->program(), p->bank());
1941 }
1942
1943 /// Return true iff @p pc applies to the given time on the given channel.
1944 static bool
1945 patch_applies (const ARDOUR::MidiModel::constPatchChangePtr pc, Evoral::Beats time, uint8_t channel)
1946 {
1947         return pc->time() <= time && pc->channel() == channel;
1948 }
1949
1950 void
1951 MidiRegionView::get_patch_key_at (Evoral::Beats time, uint8_t channel, MIDI::Name::PatchPrimaryKey& key) const
1952 {
1953         // The earliest event not before time
1954         MidiModel::PatchChanges::iterator i = _model->patch_change_lower_bound (time);
1955
1956         // Go backwards until we find the latest PC for this channel, or the start
1957         while (i != _model->patch_changes().begin() &&
1958                (i == _model->patch_changes().end() ||
1959                 !patch_applies(*i, time, channel))) {
1960                 --i;
1961         }
1962
1963         if (i != _model->patch_changes().end() && patch_applies(*i, time, channel)) {
1964                 key.set_bank((*i)->bank());
1965                 key.set_program((*i)->program ());
1966         } else {
1967                 key.set_bank(0);
1968                 key.set_program(0);
1969         }
1970 }
1971
1972 void
1973 MidiRegionView::change_patch_change (PatchChange& pc, const MIDI::Name::PatchPrimaryKey& new_patch)
1974 {
1975         string name = _("alter patch change");
1976         trackview.editor().begin_reversible_command (name);
1977         MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (name);
1978
1979         if (pc.patch()->program() != new_patch.program()) {
1980                 c->change_program (pc.patch (), new_patch.program());
1981         }
1982
1983         int const new_bank = new_patch.bank();
1984         if (pc.patch()->bank() != new_bank) {
1985                 c->change_bank (pc.patch (), new_bank);
1986         }
1987
1988         _model->apply_command (*trackview.session(), c);
1989         trackview.editor().commit_reversible_command ();
1990
1991         _patch_changes.clear ();
1992         display_patch_changes ();
1993 }
1994
1995 void
1996 MidiRegionView::change_patch_change (MidiModel::PatchChangePtr old_change, const Evoral::PatchChange<Evoral::Beats> & new_change)
1997 {
1998         string name = _("alter patch change");
1999         trackview.editor().begin_reversible_command (name);
2000         MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (name);
2001
2002         if (old_change->time() != new_change.time()) {
2003                 c->change_time (old_change, new_change.time());
2004         }
2005
2006         if (old_change->channel() != new_change.channel()) {
2007                 c->change_channel (old_change, new_change.channel());
2008         }
2009
2010         if (old_change->program() != new_change.program()) {
2011                 c->change_program (old_change, new_change.program());
2012         }
2013
2014         if (old_change->bank() != new_change.bank()) {
2015                 c->change_bank (old_change, new_change.bank());
2016         }
2017
2018         _model->apply_command (*trackview.session(), c);
2019         trackview.editor().commit_reversible_command ();
2020
2021         _patch_changes.clear ();
2022         display_patch_changes ();
2023 }
2024
2025 /** Add a patch change to the region.
2026  *  @param t Time in frames relative to region position
2027  *  @param patch Patch to add; time and channel are ignored (time is converted from t, and channel comes from
2028  *  MidiTimeAxisView::get_channel_for_add())
2029  */
2030 void
2031 MidiRegionView::add_patch_change (framecnt_t t, Evoral::PatchChange<Evoral::Beats> const & patch)
2032 {
2033         MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
2034         string name = _("add patch change");
2035
2036         trackview.editor().begin_reversible_command (name);
2037         MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (name);
2038         c->add (MidiModel::PatchChangePtr (
2039                         new Evoral::PatchChange<Evoral::Beats> (
2040                                 absolute_frames_to_source_beats (_region->position() + t),
2041                                 mtv->get_channel_for_add(), patch.program(), patch.bank()
2042                                 )
2043                         )
2044                 );
2045
2046         _model->apply_command (*trackview.session(), c);
2047         trackview.editor().commit_reversible_command ();
2048
2049         _patch_changes.clear ();
2050         display_patch_changes ();
2051 }
2052
2053 void
2054 MidiRegionView::move_patch_change (PatchChange& pc, Evoral::Beats t)
2055 {
2056         trackview.editor().begin_reversible_command (_("move patch change"));
2057         MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("move patch change"));
2058         c->change_time (pc.patch (), t);
2059         _model->apply_command (*trackview.session(), c);
2060         trackview.editor().commit_reversible_command ();
2061
2062         _patch_changes.clear ();
2063         display_patch_changes ();
2064 }
2065
2066 void
2067 MidiRegionView::delete_patch_change (PatchChange* pc)
2068 {
2069         trackview.editor().begin_reversible_command (_("delete patch change"));
2070         MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("delete patch change"));
2071         c->remove (pc->patch ());
2072         _model->apply_command (*trackview.session(), c);
2073         trackview.editor().commit_reversible_command ();
2074
2075         _patch_changes.clear ();
2076         display_patch_changes ();
2077 }
2078
2079 void
2080 MidiRegionView::step_patch (PatchChange& patch, bool bank, int delta)
2081 {
2082         MIDI::Name::PatchPrimaryKey key = patch_change_to_patch_key(patch.patch());
2083         if (bank) {
2084                 key.set_bank(key.bank() + delta);
2085         } else {
2086                 key.set_program(key.program() + delta);
2087         }
2088         change_patch_change(patch, key);
2089 }
2090
2091 void
2092 MidiRegionView::note_deleted (NoteBase* cne)
2093 {
2094         if (_selection.empty()) {
2095                 return;
2096         }
2097
2098         _selection.erase (cne);
2099 }
2100
2101 void
2102 MidiRegionView::delete_selection()
2103 {
2104         if (_selection.empty()) {
2105                 return;
2106         }
2107
2108         start_note_diff_command (_("delete selection"));
2109
2110         for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2111                 if ((*i)->selected()) {
2112                         _note_diff_command->remove((*i)->note());
2113                 }
2114         }
2115
2116         _selection.clear();
2117
2118         apply_diff ();
2119         hide_verbose_cursor ();
2120 }
2121
2122 void
2123 MidiRegionView::delete_note (boost::shared_ptr<NoteType> n)
2124 {
2125         start_note_diff_command (_("delete note"));
2126         _note_diff_command->remove (n);
2127         apply_diff ();
2128
2129         hide_verbose_cursor ();
2130 }
2131
2132 void
2133 MidiRegionView::clear_editor_note_selection ()
2134 {
2135         DEBUG_TRACE(DEBUG::Selection, "MRV::clear_editor_note_selection\n");
2136         PublicEditor& editor(trackview.editor());
2137         editor.get_selection().clear_midi_notes();
2138 }
2139
2140 void
2141 MidiRegionView::clear_selection ()
2142 {
2143         clear_selection_internal();
2144         PublicEditor& editor(trackview.editor());
2145         editor.get_selection().remove(this);
2146 }
2147
2148 void
2149 MidiRegionView::clear_selection_internal ()
2150 {
2151         DEBUG_TRACE(DEBUG::Selection, "MRV::clear_selection_internal\n");
2152
2153         for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2154                 (*i)->set_selected(false);
2155                 (*i)->hide_velocity();
2156         }
2157         _selection.clear();
2158
2159         if (_entered) {
2160                 // Clearing selection entirely, ungrab keyboard
2161                 Keyboard::magic_widget_drop_focus();
2162                 _grabbed_keyboard = false;
2163         }
2164 }
2165
2166 void
2167 MidiRegionView::unique_select(NoteBase* ev)
2168 {
2169         clear_editor_note_selection();
2170         add_to_selection(ev);
2171 }
2172
2173 void
2174 MidiRegionView::select_all_notes ()
2175 {
2176         clear_editor_note_selection ();
2177
2178         for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2179                 add_to_selection (*i);
2180         }
2181 }
2182
2183 void
2184 MidiRegionView::select_range (framepos_t start, framepos_t end)
2185 {
2186         clear_editor_note_selection ();
2187
2188         for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2189                 framepos_t t = source_beats_to_absolute_frames((*i)->note()->time());
2190                 if (t >= start && t <= end) {
2191                         add_to_selection (*i);
2192                 }
2193         }
2194 }
2195
2196 void
2197 MidiRegionView::invert_selection ()
2198 {
2199         for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2200                 if ((*i)->selected()) {
2201                         remove_from_selection(*i);
2202                 } else {
2203                         add_to_selection (*i);
2204                 }
2205         }
2206 }
2207
2208 /** Used for selection undo/redo.
2209     The requested notes most likely won't exist in the view until the next model redisplay.
2210 */
2211 void
2212 MidiRegionView::select_notes (list<boost::shared_ptr<NoteType> > notes)
2213 {
2214         NoteBase* cne;
2215         list<boost::shared_ptr<NoteType> >::iterator n;
2216
2217         for (n = notes.begin(); n != notes.end(); ++n) {
2218                 if ((cne = find_canvas_note(*(*n))) != 0) {
2219                         add_to_selection (cne);
2220                 } else {
2221                         _pending_note_selection.insert(*n);
2222                 }
2223         }
2224 }
2225
2226 void
2227 MidiRegionView::select_matching_notes (uint8_t notenum, uint16_t channel_mask, bool add, bool extend)
2228 {
2229         bool have_selection = !_selection.empty();
2230         uint8_t low_note = 127;
2231         uint8_t high_note = 0;
2232         MidiModel::Notes& notes (_model->notes());
2233         _optimization_iterator = _events.begin();
2234
2235         if (extend && !have_selection) {
2236                 extend = false;
2237         }
2238
2239         /* scan existing selection to get note range */
2240
2241         for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2242                 if ((*i)->note()->note() < low_note) {
2243                         low_note = (*i)->note()->note();
2244                 }
2245                 if ((*i)->note()->note() > high_note) {
2246                         high_note = (*i)->note()->note();
2247                 }
2248         }
2249
2250         if (!add) {
2251                 clear_editor_note_selection ();
2252
2253                 if (!extend && (low_note == high_note) && (high_note == notenum)) {
2254                         /* only note previously selected is the one we are
2255                          * reselecting. treat this as cancelling the selection.
2256                          */
2257                         return;
2258                 }
2259         }
2260
2261         if (extend) {
2262                 low_note = min (low_note, notenum);
2263                 high_note = max (high_note, notenum);
2264         }
2265
2266         _no_sound_notes = true;
2267
2268         for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
2269
2270                 boost::shared_ptr<NoteType> note (*n);
2271                 NoteBase* cne;
2272                 bool select = false;
2273
2274                 if (((1 << note->channel()) & channel_mask) != 0) {
2275                         if (extend) {
2276                                 if ((note->note() >= low_note && note->note() <= high_note)) {
2277                                         select = true;
2278                                 }
2279                         } else if (note->note() == notenum) {
2280                                 select = true;
2281                         }
2282                 }
2283
2284                 if (select) {
2285                         if ((cne = find_canvas_note (note)) != 0) {
2286                                 // extend is false because we've taken care of it,
2287                                 // since it extends by time range, not pitch.
2288                                 note_selected (cne, add, false);
2289                         }
2290                 }
2291
2292                 add = true; // we need to add all remaining matching notes, even if the passed in value was false (for "set")
2293
2294         }
2295
2296         _no_sound_notes = false;
2297 }
2298
2299 void
2300 MidiRegionView::toggle_matching_notes (uint8_t notenum, uint16_t channel_mask)
2301 {
2302         MidiModel::Notes& notes (_model->notes());
2303         _optimization_iterator = _events.begin();
2304
2305         for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
2306
2307                 boost::shared_ptr<NoteType> note (*n);
2308                 NoteBase* cne;
2309
2310                 if (note->note() == notenum && (((0x0001 << note->channel()) & channel_mask) != 0)) {
2311                         if ((cne = find_canvas_note (note)) != 0) {
2312                                 if (cne->selected()) {
2313                                         note_deselected (cne);
2314                                 } else {
2315                                         note_selected (cne, true, false);
2316                                 }
2317                         }
2318                 }
2319         }
2320 }
2321
2322 void
2323 MidiRegionView::note_selected (NoteBase* ev, bool add, bool extend)
2324 {
2325         if (!add) {
2326                 clear_editor_note_selection();
2327                 add_to_selection (ev);
2328         }
2329
2330         if (!extend) {
2331
2332                 if (!ev->selected()) {
2333                         add_to_selection (ev);
2334                 }
2335
2336         } else {
2337                 /* find end of latest note selected, select all between that and the start of "ev" */
2338
2339                 Evoral::Beats earliest = Evoral::MaxBeats;
2340                 Evoral::Beats latest   = Evoral::Beats();
2341
2342                 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2343                         if ((*i)->note()->end_time() > latest) {
2344                                 latest = (*i)->note()->end_time();
2345                         }
2346                         if ((*i)->note()->time() < earliest) {
2347                                 earliest = (*i)->note()->time();
2348                         }
2349                 }
2350
2351                 if (ev->note()->end_time() > latest) {
2352                         latest = ev->note()->end_time();
2353                 }
2354
2355                 if (ev->note()->time() < earliest) {
2356                         earliest = ev->note()->time();
2357                 }
2358
2359                 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2360
2361                         /* find notes entirely within OR spanning the earliest..latest range */
2362
2363                         if (((*i)->note()->time() >= earliest && (*i)->note()->end_time() <= latest) ||
2364                             ((*i)->note()->time() <= earliest && (*i)->note()->end_time() >= latest)) {
2365                                 add_to_selection (*i);
2366                         }
2367
2368                 }
2369         }
2370 }
2371
2372 void
2373 MidiRegionView::note_deselected(NoteBase* ev)
2374 {
2375         remove_from_selection (ev);
2376 }
2377
2378 void
2379 MidiRegionView::update_drag_selection(framepos_t start, framepos_t end, double gy0, double gy1, bool extend)
2380 {
2381         PublicEditor& editor = trackview.editor();
2382
2383         // Convert to local coordinates
2384         const framepos_t p  = _region->position();
2385         const double     y  = midi_view()->y_position();
2386         const double     x0 = editor.sample_to_pixel(max((framepos_t)0, start - p));
2387         const double     x1 = editor.sample_to_pixel(max((framepos_t)0, end - p));
2388         const double     y0 = max(0.0, gy0 - y);
2389         const double     y1 = max(0.0, gy1 - y);
2390
2391         // TODO: Make this faster by storing the last updated selection rect, and only
2392         // adjusting things that are in the area that appears/disappeared.
2393         // We probably need a tree to be able to find events in O(log(n)) time.
2394
2395         for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2396                 if ((*i)->x0() < x1 && (*i)->x1() > x0 && (*i)->y0() < y1 && (*i)->y1() > y0) {
2397                         // Rectangles intersect
2398                         if (!(*i)->selected()) {
2399                                 add_to_selection (*i);
2400                         }
2401                 } else if ((*i)->selected() && !extend) {
2402                         // Rectangles do not intersect
2403                         remove_from_selection (*i);
2404                 }
2405         }
2406
2407         typedef RouteTimeAxisView::AutomationTracks ATracks;
2408         typedef std::list<Selectable*>              Selectables;
2409
2410         /* Add control points to selection. */
2411         const ATracks& atracks = midi_view()->automation_tracks();
2412         Selectables    selectables;
2413         editor.get_selection().clear_points();
2414         for (ATracks::const_iterator a = atracks.begin(); a != atracks.end(); ++a) {
2415                 a->second->get_selectables(start, end, gy0, gy1, selectables);
2416                 for (Selectables::const_iterator s = selectables.begin(); s != selectables.end(); ++s) {
2417                         ControlPoint* cp = dynamic_cast<ControlPoint*>(*s);
2418                         if (cp) {
2419                                 editor.get_selection().add(cp);
2420                         }
2421                 }
2422                 a->second->set_selected_points(editor.get_selection().points);
2423         }
2424 }
2425
2426 void
2427 MidiRegionView::update_vertical_drag_selection (double y1, double y2, bool extend)
2428 {
2429         if (y1 > y2) {
2430                 swap (y1, y2);
2431         }
2432
2433         // TODO: Make this faster by storing the last updated selection rect, and only
2434         // adjusting things that are in the area that appears/disappeared.
2435         // We probably need a tree to be able to find events in O(log(n)) time.
2436
2437         for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2438                 if (((*i)->y1() >= y1 && (*i)->y1() <= y2)) {
2439                         // within y- (note-) range
2440                         if (!(*i)->selected()) {
2441                                 add_to_selection (*i);
2442                         }
2443                 } else if ((*i)->selected() && !extend) {
2444                         remove_from_selection (*i);
2445                 }
2446         }
2447 }
2448
2449 void
2450 MidiRegionView::remove_from_selection (NoteBase* ev)
2451 {
2452         Selection::iterator i = _selection.find (ev);
2453
2454         if (i != _selection.end()) {
2455                 _selection.erase (i);
2456                 if (_selection.empty() && _grabbed_keyboard) {
2457                         // Ungrab keyboard
2458                         Keyboard::magic_widget_drop_focus();
2459                         _grabbed_keyboard = false;
2460                 }
2461         }
2462
2463         ev->set_selected (false);
2464         ev->hide_velocity ();
2465
2466         if (_selection.empty()) {
2467                 PublicEditor& editor (trackview.editor());
2468                 editor.get_selection().remove (this);
2469         }
2470 }
2471
2472 void
2473 MidiRegionView::add_to_selection (NoteBase* ev)
2474 {
2475         const bool selection_was_empty = _selection.empty();
2476
2477         if (_selection.insert (ev).second) {
2478                 ev->set_selected (true);
2479                 start_playing_midi_note ((ev)->note());
2480                 if (selection_was_empty && _entered) {
2481                         // Grab keyboard for moving notes with arrow keys
2482                         Keyboard::magic_widget_grab_focus();
2483                         _grabbed_keyboard = true;
2484                 }
2485         }
2486
2487         if (selection_was_empty) {
2488                 PublicEditor& editor (trackview.editor());
2489                 editor.get_selection().add (this);
2490         }
2491 }
2492
2493 void
2494 MidiRegionView::move_selection(double dx, double dy, double cumulative_dy)
2495 {
2496         typedef vector<boost::shared_ptr<NoteType> > PossibleChord;
2497         PossibleChord to_play;
2498         Evoral::Beats earliest = Evoral::MaxBeats;
2499
2500         for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2501                 if ((*i)->note()->time() < earliest) {
2502                         earliest = (*i)->note()->time();
2503                 }
2504         }
2505
2506         for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2507                 if ((*i)->note()->time() == earliest) {
2508                         to_play.push_back ((*i)->note());
2509                 }
2510                 (*i)->move_event(dx, dy);
2511         }
2512
2513         if (dy && !_selection.empty() && !_no_sound_notes && UIConfiguration::instance().get_sound_midi_notes()) {
2514
2515                 if (to_play.size() > 1) {
2516
2517                         PossibleChord shifted;
2518
2519                         for (PossibleChord::iterator n = to_play.begin(); n != to_play.end(); ++n) {
2520                                 boost::shared_ptr<NoteType> moved_note (new NoteType (**n));
2521                                 moved_note->set_note (moved_note->note() + cumulative_dy);
2522                                 shifted.push_back (moved_note);
2523                         }
2524
2525                         start_playing_midi_chord (shifted);
2526
2527                 } else if (!to_play.empty()) {
2528
2529                         boost::shared_ptr<NoteType> moved_note (new NoteType (*to_play.front()));
2530                         moved_note->set_note (moved_note->note() + cumulative_dy);
2531                         start_playing_midi_note (moved_note);
2532                 }
2533         }
2534 }
2535
2536 void
2537 MidiRegionView::note_dropped(NoteBase *, frameoffset_t dt, int8_t dnote)
2538 {
2539         uint8_t lowest_note_in_selection  = 127;
2540         uint8_t highest_note_in_selection = 0;
2541         uint8_t highest_note_difference   = 0;
2542
2543         // find highest and lowest notes first
2544
2545         for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2546                 uint8_t pitch = (*i)->note()->note();
2547                 lowest_note_in_selection  = std::min(lowest_note_in_selection,  pitch);
2548                 highest_note_in_selection = std::max(highest_note_in_selection, pitch);
2549         }
2550
2551         /*
2552           cerr << "dnote: " << (int) dnote << endl;
2553           cerr << "lowest note (streamview): " << int(midi_stream_view()->lowest_note())
2554           << " highest note (streamview): " << int(midi_stream_view()->highest_note()) << endl;
2555           cerr << "lowest note (selection): " << int(lowest_note_in_selection) << " highest note(selection): "
2556           << int(highest_note_in_selection) << endl;
2557           cerr << "selection size: " << _selection.size() << endl;
2558           cerr << "Highest note in selection: " << (int) highest_note_in_selection << endl;
2559         */
2560
2561         // Make sure the note pitch does not exceed the MIDI standard range
2562         if (highest_note_in_selection + dnote > 127) {
2563                 highest_note_difference = highest_note_in_selection - 127;
2564         }
2565
2566         start_note_diff_command (_("move notes"));
2567
2568         for (Selection::iterator i = _selection.begin(); i != _selection.end() ; ++i) {
2569
2570                 framepos_t new_frames = source_beats_to_absolute_frames ((*i)->note()->time()) + dt;
2571                 Evoral::Beats new_time = absolute_frames_to_source_beats (new_frames);
2572
2573                 if (new_time < 0) {
2574                         continue;
2575                 }
2576
2577                 note_diff_add_change (*i, MidiModel::NoteDiffCommand::StartTime, new_time);
2578
2579                 uint8_t original_pitch = (*i)->note()->note();
2580                 uint8_t new_pitch      = original_pitch + dnote - highest_note_difference;
2581
2582                 // keep notes in standard midi range
2583                 clamp_to_0_127(new_pitch);
2584
2585                 lowest_note_in_selection  = std::min(lowest_note_in_selection,  new_pitch);
2586                 highest_note_in_selection = std::max(highest_note_in_selection, new_pitch);
2587
2588                 note_diff_add_change (*i, MidiModel::NoteDiffCommand::NoteNumber, new_pitch);
2589         }
2590
2591         apply_diff();
2592
2593         // care about notes being moved beyond the upper/lower bounds on the canvas
2594         if (lowest_note_in_selection  < midi_stream_view()->lowest_note() ||
2595             highest_note_in_selection > midi_stream_view()->highest_note()) {
2596                 midi_stream_view()->set_note_range(MidiStreamView::ContentsRange);
2597         }
2598 }
2599
2600 /** @param x Pixel relative to the region position.
2601  *  @param ensure_snap defaults to false. true = snap always, ignoring snap mode and magnetic snap.
2602  *  Used for inverting the snap logic with key modifiers and snap delta calculation.
2603  *  @return Snapped frame relative to the region position.
2604  */
2605 framepos_t
2606 MidiRegionView::snap_pixel_to_sample(double x, bool ensure_snap)
2607 {
2608         PublicEditor& editor (trackview.editor());
2609         return snap_frame_to_frame (editor.pixel_to_sample (x), ensure_snap);
2610 }
2611
2612 /** @param x Pixel relative to the region position.
2613  *  @param ensure_snap defaults to false. true = ignore magnetic snap and snap mode (used for snap delta calculation).
2614  *  @return Snapped pixel relative to the region position.
2615  */
2616 double
2617 MidiRegionView::snap_to_pixel(double x, bool ensure_snap)
2618 {
2619         return (double) trackview.editor().sample_to_pixel(snap_pixel_to_sample(x, ensure_snap));
2620 }
2621
2622 double
2623 MidiRegionView::get_position_pixels()
2624 {
2625         framepos_t region_frame = get_position();
2626         return trackview.editor().sample_to_pixel(region_frame);
2627 }
2628
2629 double
2630 MidiRegionView::get_end_position_pixels()
2631 {
2632         framepos_t frame = get_position() + get_duration ();
2633         return trackview.editor().sample_to_pixel(frame);
2634 }
2635
2636 framepos_t
2637 MidiRegionView::source_beats_to_absolute_frames(Evoral::Beats beats) const
2638 {
2639         /* the time converter will return the frame corresponding to `beats'
2640            relative to the start of the source. The start of the source
2641            is an implied position given by region->position - region->start
2642         */
2643         const framepos_t source_start = _region->position() - _region->start();
2644         return  source_start +  _source_relative_time_converter.to (beats);
2645 }
2646
2647 Evoral::Beats
2648 MidiRegionView::absolute_frames_to_source_beats(framepos_t frames) const
2649 {
2650         /* the `frames' argument needs to be converted into a frame count
2651            relative to the start of the source before being passed in to the
2652            converter.
2653         */
2654         const framepos_t source_start = _region->position() - _region->start();
2655         return  _source_relative_time_converter.from (frames - source_start);
2656 }
2657
2658 framepos_t
2659 MidiRegionView::region_beats_to_region_frames(Evoral::Beats beats) const
2660 {
2661         return _region_relative_time_converter.to(beats);
2662 }
2663
2664 Evoral::Beats
2665 MidiRegionView::region_frames_to_region_beats(framepos_t frames) const
2666 {
2667         return _region_relative_time_converter.from(frames);
2668 }
2669
2670 double
2671 MidiRegionView::region_frames_to_region_beats_double (framepos_t frames) const
2672 {
2673         return _region_relative_time_converter_double.from(frames);
2674 }
2675
2676 void
2677 MidiRegionView::begin_resizing (bool /*at_front*/)
2678 {
2679         _resize_data.clear();
2680
2681         for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2682                 Note *note = dynamic_cast<Note*> (*i);
2683
2684                 // only insert CanvasNotes into the map
2685                 if (note) {
2686                         NoteResizeData *resize_data = new NoteResizeData();
2687                         resize_data->note = note;
2688
2689                         // create a new SimpleRect from the note which will be the resize preview
2690                         ArdourCanvas::Rectangle *resize_rect = new ArdourCanvas::Rectangle (_note_group,
2691                                                                                             ArdourCanvas::Rect (note->x0(), note->y0(), note->x0(), note->y1()));
2692
2693                         // calculate the colors: get the color settings
2694                         uint32_t fill_color = UINT_RGBA_CHANGE_A(
2695                                 UIConfiguration::instance().color ("midi note selected"),
2696                                 128);
2697
2698                         // make the resize preview notes more transparent and bright
2699                         fill_color = UINT_INTERPOLATE(fill_color, 0xFFFFFF40, 0.5);
2700
2701                         // calculate color based on note velocity
2702                         resize_rect->set_fill_color (UINT_INTERPOLATE(
2703                                 NoteBase::meter_style_fill_color(note->note()->velocity(), note->selected()),
2704                                 fill_color,
2705                                 0.85));
2706
2707                         resize_rect->set_outline_color (NoteBase::calculate_outline (
2708                                                                 UIConfiguration::instance().color ("midi note selected")));
2709
2710                         resize_data->resize_rect = resize_rect;
2711                         _resize_data.push_back(resize_data);
2712                 }
2713         }
2714 }
2715
2716 /** Update resizing notes while user drags.
2717  * @param primary `primary' note for the drag; ie the one that is used as the reference in non-relative mode.
2718  * @param at_front which end of the note (true == note on, false == note off)
2719  * @param delta_x change in mouse position since the start of the drag
2720  * @param relative true if relative resizing is taking place, false if absolute resizing.  This only makes
2721  * a difference when multiple notes are being resized; in relative mode, each note's length is changed by the
2722  * amount of the drag.  In non-relative mode, all selected notes are set to have the same start or end point
2723  * as the \a primary note.
2724  * @param snap_delta snap offset of the primary note in pixels. used in SnapRelative SnapDelta mode.
2725  * @param with_snap true if snap is to be used to determine the position, false if no snap is to be used.
2726  */
2727 void
2728 MidiRegionView::update_resizing (NoteBase* primary, bool at_front, double delta_x, bool relative, double snap_delta, bool with_snap)
2729 {
2730         bool cursor_set = false;
2731         bool const ensure_snap = trackview.editor().snap_mode () != SnapMagnetic;
2732
2733         for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
2734                 ArdourCanvas::Rectangle* resize_rect = (*i)->resize_rect;
2735                 Note* canvas_note = (*i)->note;
2736                 double current_x;
2737
2738                 if (at_front) {
2739                         if (relative) {
2740                                 current_x = canvas_note->x0() + delta_x + snap_delta;
2741                         } else {
2742                                 current_x = primary->x0() + delta_x + snap_delta;
2743                         }
2744                 } else {
2745                         if (relative) {
2746                                 current_x = canvas_note->x1() + delta_x + snap_delta;
2747                         } else {
2748                                 current_x = primary->x1() + delta_x + snap_delta;
2749                         }
2750                 }
2751
2752                 if (current_x < 0) {
2753                         // This works even with snapping because RegionView::snap_frame_to_frame()
2754                         // snaps forward if the snapped sample is before the beginning of the region
2755                         current_x = 0;
2756                 }
2757                 if (current_x > trackview.editor().sample_to_pixel(_region->length())) {
2758                         current_x = trackview.editor().sample_to_pixel(_region->length());
2759                 }
2760
2761                 if (at_front) {
2762                         if (with_snap) {
2763                                 resize_rect->set_x0 (snap_to_pixel (current_x, ensure_snap) - snap_delta);
2764                         } else {
2765                                 resize_rect->set_x0 (current_x - snap_delta);
2766                         }
2767                         resize_rect->set_x1 (canvas_note->x1());
2768                 } else {
2769                         if (with_snap) {
2770                                 resize_rect->set_x1 (snap_to_pixel (current_x, ensure_snap) - snap_delta);
2771                         } else {
2772                                 resize_rect->set_x1 (current_x - snap_delta);
2773                         }
2774                         resize_rect->set_x0 (canvas_note->x0());
2775                 }
2776
2777                 if (!cursor_set) {
2778                         /* Convert snap delta from pixels to beats. */
2779                         framepos_t snap_delta_samps = trackview.editor().pixel_to_sample (snap_delta);
2780                         double snap_delta_beats = 0.0;
2781                         int sign = 1;
2782
2783                         /* negative beat offsets aren't allowed */
2784                         if (snap_delta_samps > 0) {
2785                                 snap_delta_beats = region_frames_to_region_beats_double (snap_delta_samps);
2786                         } else if (snap_delta_samps < 0) {
2787                                 snap_delta_beats = region_frames_to_region_beats_double ( - snap_delta_samps);
2788                                 sign = -1;
2789                         }
2790
2791                         const double  snapped_x = (with_snap ? snap_pixel_to_sample (current_x, ensure_snap) : trackview.editor ().pixel_to_sample (current_x));
2792                         Evoral::Beats beats     = region_frames_to_region_beats (snapped_x);
2793                         Evoral::Beats len       = Evoral::Beats();
2794
2795                         if (at_front) {
2796                                 if (beats < canvas_note->note()->end_time()) {
2797                                         len = canvas_note->note()->time() - beats + (sign * snap_delta_beats);
2798                                         len += canvas_note->note()->length();
2799                                 }
2800                         } else {
2801                                 if (beats >= canvas_note->note()->time()) {
2802                                         len = beats - canvas_note->note()->time() - (sign * snap_delta_beats);
2803                                 }
2804                         }
2805
2806                         len = std::max(Evoral::Beats(1 / 512.0), len);
2807
2808                         char buf[16];
2809                         snprintf (buf, sizeof (buf), "%.3g beats", len.to_double());
2810                         show_verbose_cursor (buf, 0, 0);
2811
2812                         cursor_set = true;
2813                 }
2814
2815         }
2816 }
2817
2818
2819 /** Finish resizing notes when the user releases the mouse button.
2820  *  Parameters the same as for \a update_resizing().
2821  */
2822 void
2823 MidiRegionView::commit_resizing (NoteBase* primary, bool at_front, double delta_x, bool relative, double snap_delta, bool with_snap)
2824 {
2825         _note_diff_command = _model->new_note_diff_command (_("resize notes"));
2826
2827         /* XX why doesn't snap_pixel_to_sample() handle this properly? */
2828         bool const ensure_snap = trackview.editor().snap_mode () != SnapMagnetic;
2829
2830         for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
2831                 Note*  canvas_note = (*i)->note;
2832                 ArdourCanvas::Rectangle*  resize_rect = (*i)->resize_rect;
2833
2834                 /* Get the new x position for this resize, which is in pixels relative
2835                  * to the region position.
2836                  */
2837
2838                 double current_x;
2839
2840                 if (at_front) {
2841                         if (relative) {
2842                                 current_x = canvas_note->x0() + delta_x + snap_delta;
2843                         } else {
2844                                 current_x = primary->x0() + delta_x + snap_delta;
2845                         }
2846                 } else {
2847                         if (relative) {
2848                                 current_x = canvas_note->x1() + delta_x + snap_delta;
2849                         } else {
2850                                 current_x = primary->x1() + delta_x + snap_delta;
2851                         }
2852                 }
2853
2854                 if (current_x < 0) {
2855                         current_x = 0;
2856                 }
2857                 if (current_x > trackview.editor().sample_to_pixel(_region->length())) {
2858                         current_x = trackview.editor().sample_to_pixel(_region->length());
2859                 }
2860
2861                 /* Convert snap delta from pixels to beats with sign. */
2862                 framepos_t snap_delta_samps = trackview.editor().pixel_to_sample (snap_delta);
2863                 double snap_delta_beats = 0.0;
2864                 int sign = 1;
2865
2866                 if (snap_delta_samps > 0) {
2867                         snap_delta_beats = region_frames_to_region_beats_double (snap_delta_samps);
2868                 } else if (snap_delta_samps < 0) {
2869                         snap_delta_beats = region_frames_to_region_beats_double ( - snap_delta_samps);
2870                         sign = -1;
2871                 }
2872
2873                 /* Convert the new x position to a frame within the source */
2874                 framepos_t current_fr;
2875                 if (with_snap) {
2876                         current_fr = snap_pixel_to_sample (current_x, ensure_snap) + _region->start ();
2877                 } else {
2878                         current_fr = trackview.editor().pixel_to_sample (current_x) + _region->start ();
2879                 }
2880
2881                 /* and then to beats */
2882                 const Evoral::Beats x_beats = region_frames_to_region_beats (current_fr);
2883
2884                 if (at_front && x_beats < canvas_note->note()->end_time()) {
2885                         note_diff_add_change (canvas_note, MidiModel::NoteDiffCommand::StartTime, x_beats - (sign * snap_delta_beats));
2886                         Evoral::Beats len = canvas_note->note()->time() - x_beats + (sign * snap_delta_beats);
2887                         len += canvas_note->note()->length();
2888
2889                         if (!!len) {
2890                                 note_diff_add_change (canvas_note, MidiModel::NoteDiffCommand::Length, len);
2891                         }
2892                 }
2893
2894                 if (!at_front) {
2895                         Evoral::Beats len = std::max(Evoral::Beats(1 / 512.0),
2896                                                      x_beats - canvas_note->note()->time() - (sign * snap_delta_beats));
2897                         note_diff_add_change (canvas_note, MidiModel::NoteDiffCommand::Length, len);
2898                 }
2899
2900                 delete resize_rect;
2901                 delete (*i);
2902         }
2903
2904         _resize_data.clear();
2905         apply_diff(true);
2906 }
2907
2908 void
2909 MidiRegionView::abort_resizing ()
2910 {
2911         for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
2912                 delete (*i)->resize_rect;
2913                 delete *i;
2914         }
2915
2916         _resize_data.clear ();
2917 }
2918
2919 void
2920 MidiRegionView::change_note_velocity(NoteBase* event, int8_t velocity, bool relative)
2921 {
2922         uint8_t new_velocity;
2923
2924         if (relative) {
2925                 new_velocity = event->note()->velocity() + velocity;
2926                 clamp_to_0_127(new_velocity);
2927         } else {
2928                 new_velocity = velocity;
2929         }
2930
2931         event->set_selected (event->selected()); // change color
2932
2933         note_diff_add_change (event, MidiModel::NoteDiffCommand::Velocity, new_velocity);
2934 }
2935
2936 void
2937 MidiRegionView::change_note_note (NoteBase* event, int8_t note, bool relative)
2938 {
2939         uint8_t new_note;
2940
2941         if (relative) {
2942                 new_note = event->note()->note() + note;
2943         } else {
2944                 new_note = note;
2945         }
2946
2947         clamp_to_0_127 (new_note);
2948         note_diff_add_change (event, MidiModel::NoteDiffCommand::NoteNumber, new_note);
2949 }
2950
2951 void
2952 MidiRegionView::trim_note (NoteBase* event, Evoral::Beats front_delta, Evoral::Beats end_delta)
2953 {
2954         bool change_start = false;
2955         bool change_length = false;
2956         Evoral::Beats new_start;
2957         Evoral::Beats new_length;
2958
2959         /* NOTE: the semantics of the two delta arguments are slightly subtle:
2960
2961            front_delta: if positive - move the start of the note later in time (shortening it)
2962            if negative - move the start of the note earlier in time (lengthening it)
2963
2964            end_delta:   if positive - move the end of the note later in time (lengthening it)
2965            if negative - move the end of the note earlier in time (shortening it)
2966         */
2967
2968         if (!!front_delta) {
2969                 if (front_delta < 0) {
2970
2971                         if (event->note()->time() < -front_delta) {
2972                                 new_start = Evoral::Beats();
2973                         } else {
2974                                 new_start = event->note()->time() + front_delta; // moves earlier
2975                         }
2976
2977                         /* start moved toward zero, so move the end point out to where it used to be.
2978                            Note that front_delta is negative, so this increases the length.
2979                         */
2980
2981                         new_length = event->note()->length() - front_delta;
2982                         change_start = true;
2983                         change_length = true;
2984
2985                 } else {
2986
2987                         Evoral::Beats new_pos = event->note()->time() + front_delta;
2988
2989                         if (new_pos < event->note()->end_time()) {
2990                                 new_start = event->note()->time() + front_delta;
2991                                 /* start moved toward the end, so move the end point back to where it used to be */
2992                                 new_length = event->note()->length() - front_delta;
2993                                 change_start = true;
2994                                 change_length = true;
2995                         }
2996                 }
2997
2998         }
2999
3000         if (!!end_delta) {
3001                 bool can_change = true;
3002                 if (end_delta < 0) {
3003                         if (event->note()->length() < -end_delta) {
3004                                 can_change = false;
3005                         }
3006                 }
3007
3008                 if (can_change) {
3009                         new_length = event->note()->length() + end_delta;
3010                         change_length = true;
3011                 }
3012         }
3013
3014         if (change_start) {
3015                 note_diff_add_change (event, MidiModel::NoteDiffCommand::StartTime, new_start);
3016         }
3017
3018         if (change_length) {
3019                 note_diff_add_change (event, MidiModel::NoteDiffCommand::Length, new_length);
3020         }
3021 }
3022
3023 void
3024 MidiRegionView::change_note_channel (NoteBase* event, int8_t chn, bool relative)
3025 {
3026         uint8_t new_channel;
3027
3028         if (relative) {
3029                 if (chn < 0.0) {
3030                         if (event->note()->channel() < -chn) {
3031                                 new_channel = 0;
3032                         } else {
3033                                 new_channel = event->note()->channel() + chn;
3034                         }
3035                 } else {
3036                         new_channel = event->note()->channel() + chn;
3037                 }
3038         } else {
3039                 new_channel = (uint8_t) chn;
3040         }
3041
3042         note_diff_add_change (event, MidiModel::NoteDiffCommand::Channel, new_channel);
3043 }
3044
3045 void
3046 MidiRegionView::change_note_time (NoteBase* event, Evoral::Beats delta, bool relative)
3047 {
3048         Evoral::Beats new_time;
3049
3050         if (relative) {
3051                 if (delta < 0.0) {
3052                         if (event->note()->time() < -delta) {
3053                                 new_time = Evoral::Beats();
3054                         } else {
3055                                 new_time = event->note()->time() + delta;
3056                         }
3057                 } else {
3058                         new_time = event->note()->time() + delta;
3059                 }
3060         } else {
3061                 new_time = delta;
3062         }
3063
3064         note_diff_add_change (event, MidiModel::NoteDiffCommand::StartTime, new_time);
3065 }
3066
3067 void
3068 MidiRegionView::change_note_length (NoteBase* event, Evoral::Beats t)
3069 {
3070         note_diff_add_change (event, MidiModel::NoteDiffCommand::Length, t);
3071 }
3072
3073 void
3074 MidiRegionView::change_velocities (bool up, bool fine, bool allow_smush, bool all_together)
3075 {
3076         int8_t delta;
3077         int8_t value = 0;
3078
3079         if (_selection.empty()) {
3080                 return;
3081         }
3082
3083         if (fine) {
3084                 delta = 1;
3085         } else {
3086                 delta = 10;
3087         }
3088
3089         if (!up) {
3090                 delta = -delta;
3091         }
3092
3093         if (!allow_smush) {
3094                 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3095                         if ((*i)->note()->velocity() < -delta || (*i)->note()->velocity() + delta > 127) {
3096                                 goto cursor_label;
3097                         }
3098                 }
3099         }
3100
3101         start_note_diff_command (_("change velocities"));
3102
3103         for (Selection::iterator i = _selection.begin(); i != _selection.end();) {
3104                 Selection::iterator next = i;
3105                 ++next;
3106
3107                 if (all_together) {
3108                         if (i == _selection.begin()) {
3109                                 change_note_velocity (*i, delta, true);
3110                                 value = (*i)->note()->velocity() + delta;
3111                         } else {
3112                                 change_note_velocity (*i, value, false);
3113                         }
3114
3115                 } else {
3116                         change_note_velocity (*i, delta, true);
3117                 }
3118
3119                 i = next;
3120         }
3121
3122         apply_diff();
3123
3124   cursor_label:
3125         if (!_selection.empty()) {
3126                 char buf[24];
3127                 snprintf (buf, sizeof (buf), "Vel %d",
3128                           (int) (*_selection.begin())->note()->velocity());
3129                 show_verbose_cursor (buf, 10, 10);
3130         }
3131 }
3132
3133
3134 void
3135 MidiRegionView::transpose (bool up, bool fine, bool allow_smush)
3136 {
3137         if (_selection.empty()) {
3138                 return;
3139         }
3140
3141         int8_t delta;
3142
3143         if (fine) {
3144                 delta = 1;
3145         } else {
3146                 delta = 12;
3147         }
3148
3149         if (!up) {
3150                 delta = -delta;
3151         }
3152
3153         if (!allow_smush) {
3154                 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3155                         if (!up) {
3156                                 if ((int8_t) (*i)->note()->note() + delta <= 0) {
3157                                         return;
3158                                 }
3159                         } else {
3160                                 if ((int8_t) (*i)->note()->note() + delta > 127) {
3161                                         return;
3162                                 }
3163                         }
3164                 }
3165         }
3166
3167         start_note_diff_command (_("transpose"));
3168
3169         for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
3170                 Selection::iterator next = i;
3171                 ++next;
3172                 change_note_note (*i, delta, true);
3173                 i = next;
3174         }
3175
3176         apply_diff ();
3177 }
3178
3179 void
3180 MidiRegionView::change_note_lengths (bool fine, bool shorter, Evoral::Beats delta, bool start, bool end)
3181 {
3182         if (!delta) {
3183                 if (fine) {
3184                         delta = Evoral::Beats(1.0/128.0);
3185                 } else {
3186                         /* grab the current grid distance */
3187                         delta = get_grid_beats(_region->position());
3188                 }
3189         }
3190
3191         if (shorter) {
3192                 delta = -delta;
3193         }
3194
3195         start_note_diff_command (_("change note lengths"));
3196
3197         for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
3198                 Selection::iterator next = i;
3199                 ++next;
3200
3201                 /* note the negation of the delta for start */
3202
3203                 trim_note (*i,
3204                            (start ? -delta : Evoral::Beats()),
3205                            (end   ? delta  : Evoral::Beats()));
3206                 i = next;
3207         }
3208
3209         apply_diff ();
3210
3211 }
3212
3213 void
3214 MidiRegionView::nudge_notes (bool forward, bool fine)
3215 {
3216         if (_selection.empty()) {
3217                 return;
3218         }
3219
3220         /* pick a note as the point along the timeline to get the nudge distance.
3221            its not necessarily the earliest note, so we may want to pull the notes out
3222            into a vector and sort before using the first one.
3223         */
3224
3225         const framepos_t ref_point = source_beats_to_absolute_frames ((*(_selection.begin()))->note()->time());
3226         Evoral::Beats    delta;
3227
3228         if (!fine) {
3229
3230                 /* non-fine, move by 1 bar regardless of snap */
3231                 delta = Evoral::Beats(trackview.session()->tempo_map().meter_at(ref_point).divisions_per_bar());
3232
3233         } else if (trackview.editor().snap_mode() == Editing::SnapOff) {
3234
3235                 /* grid is off - use nudge distance */
3236
3237                 framepos_t       unused;
3238                 const framecnt_t distance = trackview.editor().get_nudge_distance (ref_point, unused);
3239                 delta = region_frames_to_region_beats (fabs ((double)distance));
3240
3241         } else {
3242
3243                 /* use grid */
3244
3245                 framepos_t next_pos = ref_point;
3246
3247                 if (forward) {
3248                         if (max_framepos - 1 < next_pos) {
3249                                 next_pos += 1;
3250                         }
3251                 } else {
3252                         if (next_pos == 0) {
3253                                 return;
3254                         }
3255                         next_pos -= 1;
3256                 }
3257
3258                 trackview.editor().snap_to (next_pos, (forward ? RoundUpAlways : RoundDownAlways), false);
3259                 const framecnt_t distance = ref_point - next_pos;
3260                 delta = region_frames_to_region_beats (fabs ((double)distance));
3261         }
3262
3263         if (!delta) {
3264                 return;
3265         }
3266
3267         if (!forward) {
3268                 delta = -delta;
3269         }
3270
3271         start_note_diff_command (_("nudge"));
3272
3273         for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
3274                 Selection::iterator next = i;
3275                 ++next;
3276                 change_note_time (*i, delta, true);
3277                 i = next;
3278         }
3279
3280         apply_diff ();
3281 }
3282
3283 void
3284 MidiRegionView::change_channel(uint8_t channel)
3285 {
3286         start_note_diff_command(_("change channel"));
3287         for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3288                 note_diff_add_change (*i, MidiModel::NoteDiffCommand::Channel, channel);
3289         }
3290
3291         apply_diff();
3292 }
3293
3294
3295 void
3296 MidiRegionView::note_entered(NoteBase* ev)
3297 {
3298         _note_entered = true;
3299
3300         Editor* editor = dynamic_cast<Editor*>(&trackview.editor());
3301
3302         if (_mouse_state == SelectTouchDragging) {
3303
3304                 note_selected (ev, true);
3305
3306         } else if (editor->current_mouse_mode() == MouseContent) {
3307
3308                 remove_ghost_note ();
3309                 show_verbose_cursor (ev->note ());
3310
3311         } else if (editor->current_mouse_mode() == MouseDraw) {
3312
3313                 remove_ghost_note ();
3314                 show_verbose_cursor (ev->note ());
3315         }
3316 }
3317
3318 void
3319 MidiRegionView::note_left (NoteBase*)
3320 {
3321         _note_entered = false;
3322
3323         for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3324                 (*i)->hide_velocity ();
3325         }
3326
3327         hide_verbose_cursor ();
3328 }
3329
3330 void
3331 MidiRegionView::patch_entered (PatchChange* p)
3332 {
3333         ostringstream s;
3334         /* XXX should get patch name if we can */
3335         s << _("Bank ") << (p->patch()->bank() + MIDI_BP_ZERO) << '\n'
3336           << _("Program ") << ((int) p->patch()->program()) + MIDI_BP_ZERO << '\n'
3337           << _("Channel ") << ((int) p->patch()->channel() + 1);
3338         show_verbose_cursor (s.str(), 10, 20);
3339         p->item().grab_focus();
3340 }
3341
3342 void
3343 MidiRegionView::patch_left (PatchChange *)
3344 {
3345         hide_verbose_cursor ();
3346         /* focus will transfer back via the enter-notify event sent to this
3347          * midi region view.
3348          */
3349 }
3350
3351 void
3352 MidiRegionView::sysex_entered (SysEx* p)
3353 {
3354         ostringstream s;
3355         // CAIROCANVAS
3356         // need a way to extract text from p->_flag->_text
3357         // s << p->text();
3358         // show_verbose_cursor (s.str(), 10, 20);
3359         p->item().grab_focus();
3360 }
3361
3362 void
3363 MidiRegionView::sysex_left (SysEx *)
3364 {
3365         hide_verbose_cursor ();
3366         /* focus will transfer back via the enter-notify event sent to this
3367          * midi region view.
3368          */
3369 }
3370
3371 void
3372 MidiRegionView::note_mouse_position (float x_fraction, float /*y_fraction*/, bool can_set_cursor)
3373 {
3374         Editor* editor = dynamic_cast<Editor*>(&trackview.editor());
3375         Editing::MouseMode mm = editor->current_mouse_mode();
3376         bool trimmable = (mm == MouseContent || mm == MouseTimeFX || mm == MouseDraw);
3377
3378         Editor::EnterContext* ctx = editor->get_enter_context(NoteItem);
3379         if (can_set_cursor && ctx) {
3380                 if (trimmable && x_fraction > 0.0 && x_fraction < 0.2) {
3381                         ctx->cursor_ctx->change(editor->cursors()->left_side_trim);
3382                 } else if (trimmable && x_fraction >= 0.8 && x_fraction < 1.0) {
3383                         ctx->cursor_ctx->change(editor->cursors()->right_side_trim);
3384                 } else {
3385                         ctx->cursor_ctx->change(editor->cursors()->grabber_note);
3386                 }
3387         }
3388 }
3389
3390 uint32_t
3391 MidiRegionView::get_fill_color() const
3392 {
3393         const std::string mod_name = (_dragging ? "dragging region" :
3394                                       trackview.editor().internal_editing() ? "editable region" :
3395                                       "midi frame base");
3396         if (_selected) {
3397                 return UIConfiguration::instance().color_mod ("selected region base", mod_name);
3398         } else if ((!UIConfiguration::instance().get_show_name_highlight() || high_enough_for_name) &&
3399                    !UIConfiguration::instance().get_color_regions_using_track_color()) {
3400                 return UIConfiguration::instance().color_mod ("midi frame base", mod_name);
3401         }
3402         return UIConfiguration::instance().color_mod (fill_color, mod_name);
3403 }
3404
3405 void
3406 MidiRegionView::midi_channel_mode_changed ()
3407 {
3408         MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3409         uint16_t mask = mtv->midi_track()->get_playback_channel_mask();
3410         ChannelMode mode = mtv->midi_track()->get_playback_channel_mode ();
3411
3412         if (mode == ForceChannel) {
3413                 mask = 0xFFFF; // Show all notes as active (below)
3414         }
3415
3416         // Update notes for selection
3417         for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3418                 (*i)->on_channel_selection_change (mask);
3419         }
3420
3421         _patch_changes.clear ();
3422         display_patch_changes ();
3423 }
3424
3425 void
3426 MidiRegionView::instrument_settings_changed ()
3427 {
3428         redisplay_model();
3429 }
3430
3431 void
3432 MidiRegionView::cut_copy_clear (Editing::CutCopyOp op)
3433 {
3434         if (_selection.empty()) {
3435                 return;
3436         }
3437
3438         PublicEditor& editor (trackview.editor());
3439
3440         switch (op) {
3441         case Delete:
3442                 /* XXX what to do ? */
3443                 break;
3444         case Cut:
3445         case Copy:
3446                 editor.get_cut_buffer().add (selection_as_cut_buffer());
3447                 break;
3448         default:
3449                 break;
3450         }
3451
3452         if (op != Copy) {
3453
3454                 start_note_diff_command();
3455
3456                 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3457                         switch (op) {
3458                         case Copy:
3459                                 break;
3460                         case Delete:
3461                         case Cut:
3462                         case Clear:
3463                                 note_diff_remove_note (*i);
3464                                 break;
3465                         }
3466                 }
3467
3468                 apply_diff();
3469         }
3470 }
3471
3472 MidiCutBuffer*
3473 MidiRegionView::selection_as_cut_buffer () const
3474 {
3475         Notes notes;
3476
3477         for (Selection::const_iterator i = _selection.begin(); i != _selection.end(); ++i) {
3478                 NoteType* n = (*i)->note().get();
3479                 notes.insert (boost::shared_ptr<NoteType> (new NoteType (*n)));
3480         }
3481
3482         MidiCutBuffer* cb = new MidiCutBuffer (trackview.session());
3483         cb->set (notes);
3484
3485         return cb;
3486 }
3487
3488 /** This method handles undo */
3489 bool
3490 MidiRegionView::paste (framepos_t pos, const ::Selection& selection, PasteContext& ctx)
3491 {
3492         bool commit = false;
3493         // Paste notes, if available
3494         MidiNoteSelection::const_iterator m = selection.midi_notes.get_nth(ctx.counts.n_notes());
3495         if (m != selection.midi_notes.end()) {
3496                 ctx.counts.increase_n_notes();
3497                 if (!(*m)->empty()) { commit = true; }
3498                 paste_internal(pos, ctx.count, ctx.times, **m);
3499         }
3500
3501         // Paste control points to automation children, if available
3502         typedef RouteTimeAxisView::AutomationTracks ATracks;
3503         const ATracks& atracks = midi_view()->automation_tracks();
3504         for (ATracks::const_iterator a = atracks.begin(); a != atracks.end(); ++a) {
3505                 if (a->second->paste(pos, selection, ctx)) {
3506                         commit = true;
3507                 }
3508         }
3509
3510         if (commit) {
3511                 trackview.editor().commit_reversible_command ();
3512         }
3513         return true;
3514 }
3515
3516 /** This method handles undo */
3517 void
3518 MidiRegionView::paste_internal (framepos_t pos, unsigned paste_count, float times, const MidiCutBuffer& mcb)
3519 {
3520         if (mcb.empty()) {
3521                 return;
3522         }
3523
3524         start_note_diff_command (_("paste"));
3525
3526         const Evoral::Beats snap_beats    = get_grid_beats(pos);
3527         const Evoral::Beats first_time    = (*mcb.notes().begin())->time();
3528         const Evoral::Beats last_time     = (*mcb.notes().rbegin())->end_time();
3529         const Evoral::Beats duration      = last_time - first_time;
3530         const Evoral::Beats snap_duration = duration.snap_to(snap_beats);
3531         const Evoral::Beats paste_offset  = snap_duration * paste_count;
3532         const Evoral::Beats pos_beats     = absolute_frames_to_source_beats(pos) + paste_offset;
3533         Evoral::Beats       end_point     = Evoral::Beats();
3534
3535         DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("Paste data spans from %1 to %2 (%3) ; paste pos beats = %4 (based on %5 - %6)\n",
3536                                                        first_time,
3537                                                        last_time,
3538                                                        duration, pos, _region->position(),
3539                                                        pos_beats));
3540
3541         clear_editor_note_selection ();
3542
3543         for (int n = 0; n < (int) times; ++n) {
3544
3545                 for (Notes::const_iterator i = mcb.notes().begin(); i != mcb.notes().end(); ++i) {
3546
3547                         boost::shared_ptr<NoteType> copied_note (new NoteType (*((*i).get())));
3548                         copied_note->set_time (pos_beats + copied_note->time() - first_time);
3549                         copied_note->set_id (Evoral::next_event_id());
3550
3551                         /* make all newly added notes selected */
3552
3553                         note_diff_add_note (copied_note, true);
3554                         end_point = copied_note->end_time();
3555                 }
3556         }
3557
3558         /* if we pasted past the current end of the region, extend the region */
3559
3560         framepos_t end_frame = source_beats_to_absolute_frames (end_point);
3561         framepos_t region_end = _region->position() + _region->length() - 1;
3562
3563         if (end_frame > region_end) {
3564
3565                 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("Paste extended region from %1 to %2\n", region_end, end_frame));
3566
3567                 _region->clear_changes ();
3568                 _region->set_length (end_frame - _region->position());
3569                 trackview.session()->add_command (new StatefulDiffCommand (_region));
3570         }
3571
3572         apply_diff (true);
3573 }
3574
3575 struct EventNoteTimeEarlyFirstComparator {
3576         bool operator() (NoteBase* a, NoteBase* b) {
3577                 return a->note()->time() < b->note()->time();
3578         }
3579 };
3580
3581 void
3582 MidiRegionView::time_sort_events ()
3583 {
3584         if (!_sort_needed) {
3585                 return;
3586         }
3587
3588         EventNoteTimeEarlyFirstComparator cmp;
3589         _events.sort (cmp);
3590
3591         _sort_needed = false;
3592 }
3593
3594 void
3595 MidiRegionView::goto_next_note (bool add_to_selection)
3596 {
3597         bool use_next = false;
3598
3599         if (_events.back()->selected()) {
3600                 return;
3601         }
3602
3603         time_sort_events ();
3604
3605         MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3606         uint16_t const channel_mask = mtv->midi_track()->get_playback_channel_mask();
3607
3608         for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3609                 if ((*i)->selected()) {
3610                         use_next = true;
3611                         continue;
3612                 } else if (use_next) {
3613                         if (channel_mask & (1 << (*i)->note()->channel())) {
3614                                 if (!add_to_selection) {
3615                                         unique_select (*i);
3616                                 } else {
3617                                         note_selected (*i, true, false);
3618                                 }
3619                                 return;
3620                         }
3621                 }
3622         }
3623
3624         /* use the first one */
3625
3626         if (!_events.empty() && (channel_mask & (1 << _events.front()->note()->channel ()))) {
3627                 unique_select (_events.front());
3628         }
3629 }
3630
3631 void
3632 MidiRegionView::goto_previous_note (bool add_to_selection)
3633 {
3634         bool use_next = false;
3635
3636         if (_events.front()->selected()) {
3637                 return;
3638         }
3639
3640         time_sort_events ();
3641
3642         MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3643         uint16_t const channel_mask = mtv->midi_track()->get_playback_channel_mask ();
3644
3645         for (Events::reverse_iterator i = _events.rbegin(); i != _events.rend(); ++i) {
3646                 if ((*i)->selected()) {
3647                         use_next = true;
3648                         continue;
3649                 } else if (use_next) {
3650                         if (channel_mask & (1 << (*i)->note()->channel())) {
3651                                 if (!add_to_selection) {
3652                                         unique_select (*i);
3653                                 } else {
3654                                         note_selected (*i, true, false);
3655                                 }
3656                                 return;
3657                         }
3658                 }
3659         }
3660
3661         /* use the last one */
3662
3663         if (!_events.empty() && (channel_mask & (1 << (*_events.rbegin())->note()->channel ()))) {
3664                 unique_select (*(_events.rbegin()));
3665         }
3666 }
3667
3668 void
3669 MidiRegionView::selection_as_notelist (Notes& selected, bool allow_all_if_none_selected)
3670 {
3671         bool had_selected = false;
3672
3673         time_sort_events ();
3674
3675         for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3676                 if ((*i)->selected()) {
3677                         selected.insert ((*i)->note());
3678                         had_selected = true;
3679                 }
3680         }
3681
3682         if (allow_all_if_none_selected && !had_selected) {
3683                 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3684                         selected.insert ((*i)->note());
3685                 }
3686         }
3687 }
3688
3689 void
3690 MidiRegionView::update_ghost_note (double x, double y)
3691 {
3692         x = std::max(0.0, x);
3693
3694         MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3695
3696         _last_ghost_x = x;
3697         _last_ghost_y = y;
3698
3699         _note_group->canvas_to_item (x, y);
3700
3701         PublicEditor& editor = trackview.editor ();
3702
3703         framepos_t const unsnapped_frame = editor.pixel_to_sample (x);
3704         framecnt_t grid_frames;
3705         framepos_t const f = snap_frame_to_grid_underneath (unsnapped_frame, grid_frames);
3706
3707         /* calculate time in beats relative to start of source */
3708         const Evoral::Beats length = get_grid_beats(unsnapped_frame);
3709         const Evoral::Beats time   = std::max(
3710                 Evoral::Beats(),
3711                 absolute_frames_to_source_beats (f + _region->position ()));
3712
3713         _ghost_note->note()->set_time (time);
3714         _ghost_note->note()->set_length (length);
3715         _ghost_note->note()->set_note (midi_stream_view()->y_to_note (y));
3716         _ghost_note->note()->set_channel (mtv->get_channel_for_add ());
3717         _ghost_note->note()->set_velocity (get_velocity_for_add (time));
3718
3719         /* the ghost note does not appear in ghost regions, so pass false in here */
3720         update_note (_ghost_note, false);
3721
3722         show_verbose_cursor (_ghost_note->note ());
3723 }
3724
3725 void
3726 MidiRegionView::create_ghost_note (double x, double y)
3727 {
3728         remove_ghost_note ();
3729
3730         boost::shared_ptr<NoteType> g (new NoteType);
3731         if (midi_view()->note_mode() == Sustained) {
3732                 _ghost_note = new Note (*this, _note_group, g);
3733         } else {
3734                 _ghost_note = new Hit (*this, _note_group, 10, g);
3735         }
3736         _ghost_note->set_ignore_events (true);
3737         _ghost_note->set_outline_color (0x000000aa);
3738         update_ghost_note (x, y);
3739         _ghost_note->show ();
3740
3741         show_verbose_cursor (_ghost_note->note ());
3742 }
3743
3744 void
3745 MidiRegionView::remove_ghost_note ()
3746 {
3747         delete _ghost_note;
3748         _ghost_note = 0;
3749 }
3750
3751 void
3752 MidiRegionView::hide_verbose_cursor ()
3753 {
3754         trackview.editor().verbose_cursor()->hide ();
3755         MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3756         if (mtv) {
3757                 mtv->set_note_highlight (NO_MIDI_NOTE);
3758         }
3759 }
3760
3761 void
3762 MidiRegionView::snap_changed ()
3763 {
3764         if (!_ghost_note) {
3765                 return;
3766         }
3767
3768         create_ghost_note (_last_ghost_x, _last_ghost_y);
3769 }
3770
3771 void
3772 MidiRegionView::drop_down_keys ()
3773 {
3774         _mouse_state = None;
3775 }
3776
3777 void
3778 MidiRegionView::maybe_select_by_position (GdkEventButton* ev, double /*x*/, double y)
3779 {
3780         /* XXX: This is dead code.  What was it for? */
3781
3782         double note = midi_stream_view()->y_to_note(y);
3783         Events e;
3784         MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3785
3786         uint16_t chn_mask = mtv->midi_track()->get_playback_channel_mask();
3787
3788         if (Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) {
3789                 get_events (e, Evoral::Sequence<Evoral::Beats>::PitchGreaterThanOrEqual, (uint8_t) floor (note), chn_mask);
3790         } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
3791                 get_events (e, Evoral::Sequence<Evoral::Beats>::PitchLessThanOrEqual, (uint8_t) floor (note), chn_mask);
3792         } else {
3793                 return;
3794         }
3795
3796         bool add_mrv_selection = false;
3797
3798         if (_selection.empty()) {
3799                 add_mrv_selection = true;
3800         }
3801
3802         for (Events::iterator i = e.begin(); i != e.end(); ++i) {
3803                 if (_selection.insert (*i).second) {
3804                         (*i)->set_selected (true);
3805                 }
3806         }
3807
3808         if (add_mrv_selection) {
3809                 PublicEditor& editor (trackview.editor());
3810                 editor.get_selection().add (this);
3811         }
3812 }
3813
3814 void
3815 MidiRegionView::color_handler ()
3816 {
3817         RegionView::color_handler ();
3818
3819         for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3820                 (*i)->set_selected ((*i)->selected()); // will change color
3821         }
3822
3823         /* XXX probably more to do here */
3824 }
3825
3826 void
3827 MidiRegionView::enable_display (bool yn)
3828 {
3829         RegionView::enable_display (yn);
3830         if (yn) {
3831                 redisplay_model ();
3832         }
3833 }
3834
3835 void
3836 MidiRegionView::show_step_edit_cursor (Evoral::Beats pos)
3837 {
3838         if (_step_edit_cursor == 0) {
3839                 ArdourCanvas::Item* const group = get_canvas_group();
3840
3841                 _step_edit_cursor = new ArdourCanvas::Rectangle (group);
3842                 _step_edit_cursor->set_y0 (0);
3843                 _step_edit_cursor->set_y1 (midi_stream_view()->contents_height());
3844                 _step_edit_cursor->set_fill_color (RGBA_TO_UINT (45,0,0,90));
3845                 _step_edit_cursor->set_outline_color (RGBA_TO_UINT (85,0,0,90));
3846         }
3847
3848         move_step_edit_cursor (pos);
3849         _step_edit_cursor->show ();
3850 }
3851
3852 void
3853 MidiRegionView::move_step_edit_cursor (Evoral::Beats pos)
3854 {
3855         _step_edit_cursor_position = pos;
3856
3857         if (_step_edit_cursor) {
3858                 double pixel = trackview.editor().sample_to_pixel (region_beats_to_region_frames (pos));
3859                 _step_edit_cursor->set_x0 (pixel);
3860                 set_step_edit_cursor_width (_step_edit_cursor_width);
3861         }
3862 }
3863
3864 void
3865 MidiRegionView::hide_step_edit_cursor ()
3866 {
3867         if (_step_edit_cursor) {
3868                 _step_edit_cursor->hide ();
3869         }
3870 }
3871
3872 void
3873 MidiRegionView::set_step_edit_cursor_width (Evoral::Beats beats)
3874 {
3875         _step_edit_cursor_width = beats;
3876
3877         if (_step_edit_cursor) {
3878                 _step_edit_cursor->set_x1 (_step_edit_cursor->x0() + trackview.editor().sample_to_pixel (region_beats_to_region_frames (beats)));
3879         }
3880 }
3881
3882 /** Called when a diskstream on our track has received some data.  Update the view, if applicable.
3883  *  @param w Source that the data will end up in.
3884  */
3885 void
3886 MidiRegionView::data_recorded (boost::weak_ptr<MidiSource> w)
3887 {
3888         if (!_active_notes) {
3889                 /* we aren't actively being recorded to */
3890                 return;
3891         }
3892
3893         boost::shared_ptr<MidiSource> src = w.lock ();
3894         if (!src || src != midi_region()->midi_source()) {
3895                 /* recorded data was not destined for our source */
3896                 return;
3897         }
3898
3899         MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*> (&trackview);
3900
3901         boost::shared_ptr<MidiBuffer> buf = mtv->midi_track()->get_gui_feed_buffer ();
3902
3903         framepos_t back = max_framepos;
3904
3905         for (MidiBuffer::iterator i = buf->begin(); i != buf->end(); ++i) {
3906                 Evoral::MIDIEvent<MidiBuffer::TimeType> const ev (*i, false);
3907
3908                 if (ev.is_channel_event()) {
3909                         if (get_channel_mode() == FilterChannels) {
3910                                 if (((uint16_t(1) << ev.channel()) & get_selected_channels()) == 0) {
3911                                         continue;
3912                                 }
3913                         }
3914                 }
3915
3916                 /* convert from session frames to source beats */
3917                 Evoral::Beats const time_beats = _source_relative_time_converter.from(
3918                         ev.time() - src->timeline_position() + _region->start());
3919
3920                 if (ev.type() == MIDI_CMD_NOTE_ON) {
3921                         boost::shared_ptr<NoteType> note (
3922                                 new NoteType (ev.channel(), time_beats, Evoral::Beats(), ev.note(), ev.velocity()));
3923
3924                         add_note (note, true);
3925
3926                         /* fix up our note range */
3927                         if (ev.note() < _current_range_min) {
3928                                 midi_stream_view()->apply_note_range (ev.note(), _current_range_max, true);
3929                         } else if (ev.note() > _current_range_max) {
3930                                 midi_stream_view()->apply_note_range (_current_range_min, ev.note(), true);
3931                         }
3932
3933                 } else if (ev.type() == MIDI_CMD_NOTE_OFF) {
3934                         resolve_note (ev.note (), time_beats);
3935                 }
3936
3937                 back = ev.time ();
3938         }
3939
3940         midi_stream_view()->check_record_layers (region(), back);
3941 }
3942
3943 void
3944 MidiRegionView::trim_front_starting ()
3945 {
3946         /* Reparent the note group to the region view's parent, so that it doesn't change
3947            when the region view is trimmed.
3948         */
3949         _temporary_note_group = new ArdourCanvas::Container (group->parent ());
3950         _temporary_note_group->move (group->position ());
3951         _note_group->reparent (_temporary_note_group);
3952 }
3953
3954 void
3955 MidiRegionView::trim_front_ending ()
3956 {
3957         _note_group->reparent (group);
3958         delete _temporary_note_group;
3959         _temporary_note_group = 0;
3960
3961         if (_region->start() < 0) {
3962                 /* Trim drag made start time -ve; fix this */
3963                 midi_region()->fix_negative_start ();
3964         }
3965 }
3966
3967 void
3968 MidiRegionView::edit_patch_change (PatchChange* pc)
3969 {
3970         PatchChangeDialog d (&_source_relative_time_converter, trackview.session(), *pc->patch (), instrument_info(), Gtk::Stock::APPLY, true);
3971
3972         int response = d.run();
3973
3974         switch (response) {
3975         case Gtk::RESPONSE_ACCEPT:
3976                 break;
3977         case Gtk::RESPONSE_REJECT:
3978                 delete_patch_change (pc);
3979                 return;
3980         default:
3981                 return;
3982         }
3983
3984         change_patch_change (pc->patch(), d.patch ());
3985 }
3986
3987 void
3988 MidiRegionView::delete_sysex (SysEx* /*sysex*/)
3989 {
3990         // CAIROCANVAS
3991         // sysyex object doesn't have a pointer to a sysex event
3992         // MidiModel::SysExDiffCommand* c = _model->new_sysex_diff_command (_("delete sysex"));
3993         // c->remove (sysex->sysex());
3994         // _model->apply_command (*trackview.session(), c);
3995
3996         //_sys_exes.clear ();
3997         // display_sysexes();
3998 }
3999
4000 std::string
4001 MidiRegionView::get_note_name (boost::shared_ptr<NoteType> n, uint8_t note_value) const
4002 {
4003         using namespace MIDI::Name;
4004         std::string name;
4005
4006         MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
4007         if (mtv) {
4008                 boost::shared_ptr<MasterDeviceNames> device_names(mtv->get_device_names());
4009                 if (device_names) {
4010                         MIDI::Name::PatchPrimaryKey patch_key;
4011                         get_patch_key_at(n->time(), n->channel(), patch_key);
4012                         name = device_names->note_name(mtv->gui_property(X_("midnam-custom-device-mode")),
4013                                                        n->channel(),
4014                                                        patch_key.bank(),
4015                                                        patch_key.program(),
4016                                                        note_value);
4017                 }
4018         }
4019
4020         char buf[128];
4021         snprintf (buf, sizeof (buf), "%d %s\nCh %d Vel %d",
4022                   (int) note_value,
4023                   name.empty() ? Evoral::midi_note_name (note_value).c_str() : name.c_str(),
4024                   (int) n->channel() + 1,
4025                   (int) n->velocity());
4026
4027         return buf;
4028 }
4029
4030 void
4031 MidiRegionView::show_verbose_cursor_for_new_note_value(boost::shared_ptr<NoteType> current_note,
4032                                                        uint8_t new_value) const
4033 {
4034         MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
4035         if (mtv) {
4036                 mtv->set_note_highlight (new_value);
4037         }
4038
4039         show_verbose_cursor(get_note_name(current_note, new_value), 10, 20);
4040 }
4041
4042 void
4043 MidiRegionView::show_verbose_cursor (boost::shared_ptr<NoteType> n) const
4044 {
4045         show_verbose_cursor_for_new_note_value(n, n->note());
4046 }
4047
4048 void
4049 MidiRegionView::show_verbose_cursor (string const & text, double xoffset, double yoffset) const
4050 {
4051         trackview.editor().verbose_cursor()->set (text);
4052         trackview.editor().verbose_cursor()->show ();
4053         trackview.editor().verbose_cursor()->set_offset (ArdourCanvas::Duple (xoffset, yoffset));
4054 }
4055
4056 uint8_t
4057 MidiRegionView::get_velocity_for_add (MidiModel::TimeType time) const
4058 {
4059         if (_model->notes().empty()) {
4060                 return 0x40;  // No notes, use default
4061         }
4062
4063         MidiModel::Notes::const_iterator m = _model->note_lower_bound(time);
4064         if (m == _model->notes().begin()) {
4065                 // Before the start, use the velocity of the first note
4066                 return (*m)->velocity();
4067         } else if (m == _model->notes().end()) {
4068                 // Past the end, use the velocity of the last note
4069                 --m;
4070                 return (*m)->velocity();
4071         }
4072
4073         // Interpolate velocity of surrounding notes
4074         MidiModel::Notes::const_iterator n = m;
4075         --n;
4076
4077         const double frac = ((time - (*n)->time()).to_double() /
4078                              ((*m)->time() - (*n)->time()).to_double());
4079
4080         return (*n)->velocity() + (frac * ((*m)->velocity() - (*n)->velocity()));
4081 }
4082
4083 /** @param p A session framepos.
4084  *  @param grid_frames Filled in with the number of frames that a grid interval is at p.
4085  *  @return p snapped to the grid subdivision underneath it.
4086  */
4087 framepos_t
4088 MidiRegionView::snap_frame_to_grid_underneath (framepos_t p, framecnt_t& grid_frames) const
4089 {
4090         PublicEditor& editor = trackview.editor ();
4091
4092         const Evoral::Beats grid_beats = get_grid_beats(p);
4093
4094         grid_frames = region_beats_to_region_frames (grid_beats);
4095
4096         /* Hack so that we always snap to the note that we are over, instead of snapping
4097            to the next one if we're more than halfway through the one we're over.
4098         */
4099         if (editor.snap_mode() == SnapNormal && p >= grid_frames / 2) {
4100                 p -= grid_frames / 2;
4101         }
4102
4103         return snap_frame_to_frame (p);
4104 }
4105
4106 ChannelMode
4107 MidiRegionView::get_channel_mode () const
4108 {
4109         RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (&trackview);
4110         return rtav->midi_track()->get_playback_channel_mode();
4111 }
4112
4113 uint16_t
4114 MidiRegionView::get_selected_channels () const
4115 {
4116         RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (&trackview);
4117         return rtav->midi_track()->get_playback_channel_mask();
4118 }
4119
4120
4121 Evoral::Beats
4122 MidiRegionView::get_grid_beats(framepos_t pos) const
4123 {
4124         PublicEditor& editor  = trackview.editor();
4125         bool          success = false;
4126         Evoral::Beats beats   = editor.get_grid_type_as_beats(success, pos);
4127         if (!success) {
4128                 beats = Evoral::Beats(1);
4129         }
4130         return beats;
4131 }