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