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