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