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