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