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