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