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