merge with master
[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         bool have_selection = !_selection.empty();
2139         uint8_t low_note = 127;
2140         uint8_t high_note = 0;
2141         MidiModel::Notes& notes (_model->notes());
2142         _optimization_iterator = _events.begin();
2143         
2144         if (extend && !have_selection) {
2145                 extend = false;
2146         }
2147
2148         /* scan existing selection to get note range */
2149         
2150         for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2151                 if ((*i)->note()->note() < low_note) {
2152                         low_note = (*i)->note()->note();
2153                 }
2154                 if ((*i)->note()->note() > high_note) {
2155                         high_note = (*i)->note()->note();
2156                 }
2157         }
2158         
2159         if (!add) {
2160                 clear_selection ();
2161
2162                 if (!extend && (low_note == high_note) && (high_note == notenum)) {
2163                         /* only note previously selected is the one we are
2164                          * reselecting. treat this as cancelling the selection.
2165                          */
2166                         return;
2167                 }
2168         }
2169
2170         if (extend) {
2171                 low_note = min (low_note, notenum);
2172                 high_note = max (high_note, notenum);
2173         }
2174
2175         _no_sound_notes = true;
2176
2177         for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
2178
2179                 boost::shared_ptr<NoteType> note (*n);
2180                 NoteBase* cne;
2181                 bool select = false;
2182
2183                 if (((1 << note->channel()) & channel_mask) != 0) {
2184                         if (extend) {
2185                                 if ((note->note() >= low_note && note->note() <= high_note)) {
2186                                         select = true;
2187                                 }
2188                         } else if (note->note() == notenum) {
2189                                 select = true;
2190                         }
2191                 }
2192
2193                 if (select) {
2194                         if ((cne = find_canvas_note (note)) != 0) {
2195                                 // extend is false because we've taken care of it,
2196                                 // since it extends by time range, not pitch.
2197                                 note_selected (cne, add, false);
2198                         }
2199                 }
2200
2201                 add = true; // we need to add all remaining matching notes, even if the passed in value was false (for "set")
2202
2203         }
2204
2205         _no_sound_notes = false;
2206 }
2207
2208 void
2209 MidiRegionView::toggle_matching_notes (uint8_t notenum, uint16_t channel_mask)
2210 {
2211         MidiModel::Notes& notes (_model->notes());
2212         _optimization_iterator = _events.begin();
2213
2214         for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
2215
2216                 boost::shared_ptr<NoteType> note (*n);
2217                 NoteBase* cne;
2218
2219                 if (note->note() == notenum && (((0x0001 << note->channel()) & channel_mask) != 0)) {
2220                         if ((cne = find_canvas_note (note)) != 0) {
2221                                 if (cne->selected()) {
2222                                         note_deselected (cne);
2223                                 } else {
2224                                         note_selected (cne, true, false);
2225                                 }
2226                         }
2227                 }
2228         }
2229 }
2230
2231 void
2232 MidiRegionView::note_selected (NoteBase* ev, bool add, bool extend)
2233 {
2234         if (!add) {
2235                 clear_selection_except (ev);
2236                 if (!_selection.empty()) {
2237                         PublicEditor& editor (trackview.editor());
2238                         editor.get_selection().add (this);
2239                 }
2240         }
2241
2242         if (!extend) {
2243
2244                 if (!ev->selected()) {
2245                         add_to_selection (ev);
2246                 }
2247
2248         } else {
2249                 /* find end of latest note selected, select all between that and the start of "ev" */
2250
2251                 Evoral::MusicalTime earliest = Evoral::MaxMusicalTime;
2252                 Evoral::MusicalTime latest = 0;
2253
2254                 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2255                         if ((*i)->note()->end_time() > latest) {
2256                                 latest = (*i)->note()->end_time();
2257                         }
2258                         if ((*i)->note()->time() < earliest) {
2259                                 earliest = (*i)->note()->time();
2260                         }
2261                 }
2262
2263                 if (ev->note()->end_time() > latest) {
2264                         latest = ev->note()->end_time();
2265                 }
2266
2267                 if (ev->note()->time() < earliest) {
2268                         earliest = ev->note()->time();
2269                 }
2270
2271                 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2272
2273                         /* find notes entirely within OR spanning the earliest..latest range */
2274
2275                         if (((*i)->note()->time() >= earliest && (*i)->note()->end_time() <= latest) ||
2276                             ((*i)->note()->time() <= earliest && (*i)->note()->end_time() >= latest)) {
2277                                 add_to_selection (*i);
2278                         }
2279
2280                 }
2281         }
2282 }
2283
2284 void
2285 MidiRegionView::note_deselected(NoteBase* ev)
2286 {
2287         remove_from_selection (ev);
2288 }
2289
2290 void
2291 MidiRegionView::update_drag_selection(double x0, double x1, double y0, double y1, bool extend)
2292 {
2293         // TODO: Make this faster by storing the last updated selection rect, and only
2294         // adjusting things that are in the area that appears/disappeared.
2295         // We probably need a tree to be able to find events in O(log(n)) time.
2296
2297         for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2298                 if ((*i)->x0() < x1 && (*i)->x1() > x0 && (*i)->y0() < y1 && (*i)->y1() > y0) {
2299                         // Rectangles intersect
2300                         if (!(*i)->selected()) {
2301                                 add_to_selection (*i);
2302                         }
2303                 } else if ((*i)->selected() && !extend) {
2304                         // Rectangles do not intersect
2305                         remove_from_selection (*i);
2306                 }
2307         }
2308 }
2309
2310 void
2311 MidiRegionView::update_vertical_drag_selection (double y1, double y2, bool extend)
2312 {
2313         if (y1 > y2) {
2314                 swap (y1, y2);
2315         }
2316
2317         // TODO: Make this faster by storing the last updated selection rect, and only
2318         // adjusting things that are in the area that appears/disappeared.
2319         // We probably need a tree to be able to find events in O(log(n)) time.
2320
2321         for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2322                 if (((*i)->y1() >= y1 && (*i)->y1() <= y2)) {
2323                         // within y- (note-) range
2324                         if (!(*i)->selected()) {
2325                                 add_to_selection (*i);
2326                         }
2327                 } else if ((*i)->selected() && !extend) {
2328                         remove_from_selection (*i);
2329                 }
2330         }
2331 }
2332
2333 void
2334 MidiRegionView::remove_from_selection (NoteBase* ev)
2335 {
2336         Selection::iterator i = _selection.find (ev);
2337
2338         if (i != _selection.end()) {
2339                 _selection.erase (i);
2340         }
2341
2342         ev->set_selected (false);
2343         ev->hide_velocity ();
2344
2345         if (_selection.empty()) {
2346                 PublicEditor& editor (trackview.editor());
2347                 editor.get_selection().remove (this);
2348         }
2349 }
2350
2351 void
2352 MidiRegionView::add_to_selection (NoteBase* ev)
2353 {
2354         bool add_mrv_selection = false;
2355
2356         if (_selection.empty()) {
2357                 add_mrv_selection = true;
2358         }
2359
2360         if (_selection.insert (ev).second) {
2361                 ev->set_selected (true);
2362                 start_playing_midi_note ((ev)->note());
2363         }
2364
2365         if (add_mrv_selection) {
2366                 PublicEditor& editor (trackview.editor());
2367                 editor.get_selection().add (this);
2368         }
2369 }
2370
2371 void
2372 MidiRegionView::move_selection(double dx, double dy, double cumulative_dy)
2373 {
2374         typedef vector<boost::shared_ptr<NoteType> > PossibleChord;
2375         PossibleChord to_play;
2376         Evoral::MusicalTime earliest = Evoral::MaxMusicalTime;
2377
2378         for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2379                 if ((*i)->note()->time() < earliest) {
2380                         earliest = (*i)->note()->time();
2381                 }
2382         }
2383
2384         for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2385                 if (Evoral::musical_time_equal ((*i)->note()->time(), earliest)) {
2386                         to_play.push_back ((*i)->note());
2387                 }
2388                 (*i)->move_event(dx, dy);
2389         }
2390
2391         if (dy && !_selection.empty() && !_no_sound_notes && Config->get_sound_midi_notes()) {
2392
2393                 if (to_play.size() > 1) {
2394
2395                         PossibleChord shifted;
2396
2397                         for (PossibleChord::iterator n = to_play.begin(); n != to_play.end(); ++n) {
2398                                 boost::shared_ptr<NoteType> moved_note (new NoteType (**n));
2399                                 moved_note->set_note (moved_note->note() + cumulative_dy);
2400                                 shifted.push_back (moved_note);
2401                         }
2402
2403                         start_playing_midi_chord (shifted);
2404
2405                 } else if (!to_play.empty()) {
2406
2407                         boost::shared_ptr<NoteType> moved_note (new NoteType (*to_play.front()));
2408                         moved_note->set_note (moved_note->note() + cumulative_dy);
2409                         start_playing_midi_note (moved_note);
2410                 }
2411         }
2412 }
2413
2414 void
2415 MidiRegionView::note_dropped(NoteBase *, frameoffset_t dt, int8_t dnote)
2416 {
2417         uint8_t lowest_note_in_selection  = 127;
2418         uint8_t highest_note_in_selection = 0;
2419         uint8_t highest_note_difference   = 0;
2420
2421         // find highest and lowest notes first
2422
2423         for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2424                 uint8_t pitch = (*i)->note()->note();
2425                 lowest_note_in_selection  = std::min(lowest_note_in_selection,  pitch);
2426                 highest_note_in_selection = std::max(highest_note_in_selection, pitch);
2427         }
2428
2429         /*
2430           cerr << "dnote: " << (int) dnote << endl;
2431           cerr << "lowest note (streamview): " << int(midi_stream_view()->lowest_note())
2432           << " highest note (streamview): " << int(midi_stream_view()->highest_note()) << endl;
2433           cerr << "lowest note (selection): " << int(lowest_note_in_selection) << " highest note(selection): "
2434           << int(highest_note_in_selection) << endl;
2435           cerr << "selection size: " << _selection.size() << endl;
2436           cerr << "Highest note in selection: " << (int) highest_note_in_selection << endl;
2437         */
2438
2439         // Make sure the note pitch does not exceed the MIDI standard range
2440         if (highest_note_in_selection + dnote > 127) {
2441                 highest_note_difference = highest_note_in_selection - 127;
2442         }
2443
2444         start_note_diff_command (_("move notes"));
2445
2446         for (Selection::iterator i = _selection.begin(); i != _selection.end() ; ++i) {
2447                 
2448                 framepos_t new_frames = source_beats_to_absolute_frames ((*i)->note()->time()) + dt;
2449                 Evoral::MusicalTime new_time = absolute_frames_to_source_beats (new_frames);
2450
2451                 if (new_time < 0) {
2452                         continue;
2453                 }
2454
2455                 note_diff_add_change (*i, MidiModel::NoteDiffCommand::StartTime, new_time);
2456
2457                 uint8_t original_pitch = (*i)->note()->note();
2458                 uint8_t new_pitch      = original_pitch + dnote - highest_note_difference;
2459
2460                 // keep notes in standard midi range
2461                 clamp_to_0_127(new_pitch);
2462
2463                 lowest_note_in_selection  = std::min(lowest_note_in_selection,  new_pitch);
2464                 highest_note_in_selection = std::max(highest_note_in_selection, new_pitch);
2465
2466                 note_diff_add_change (*i, MidiModel::NoteDiffCommand::NoteNumber, new_pitch);
2467         }
2468
2469         apply_diff();
2470
2471         // care about notes being moved beyond the upper/lower bounds on the canvas
2472         if (lowest_note_in_selection  < midi_stream_view()->lowest_note() ||
2473             highest_note_in_selection > midi_stream_view()->highest_note()) {
2474                 midi_stream_view()->set_note_range(MidiStreamView::ContentsRange);
2475         }
2476 }
2477
2478 /** @param x Pixel relative to the region position.
2479  *  @return Snapped frame relative to the region position.
2480  */
2481 framepos_t
2482 MidiRegionView::snap_pixel_to_sample(double x)
2483 {
2484         PublicEditor& editor (trackview.editor());
2485         return snap_frame_to_frame (editor.pixel_to_sample (x));
2486 }
2487
2488 /** @param x Pixel relative to the region position.
2489  *  @return Snapped pixel relative to the region position.
2490  */
2491 double
2492 MidiRegionView::snap_to_pixel(double x)
2493 {
2494         return (double) trackview.editor().sample_to_pixel(snap_pixel_to_sample(x));
2495 }
2496
2497 double
2498 MidiRegionView::get_position_pixels()
2499 {
2500         framepos_t region_frame = get_position();
2501         return trackview.editor().sample_to_pixel(region_frame);
2502 }
2503
2504 double
2505 MidiRegionView::get_end_position_pixels()
2506 {
2507         framepos_t frame = get_position() + get_duration ();
2508         return trackview.editor().sample_to_pixel(frame);
2509 }
2510
2511 framepos_t
2512 MidiRegionView::source_beats_to_absolute_frames(double beats) const
2513 {
2514         /* the time converter will return the frame corresponding to `beats'
2515            relative to the start of the source. The start of the source
2516            is an implied position given by region->position - region->start
2517         */
2518         const framepos_t source_start = _region->position() - _region->start();
2519         return  source_start +  _source_relative_time_converter.to (beats);
2520 }
2521
2522 double
2523 MidiRegionView::absolute_frames_to_source_beats(framepos_t frames) const
2524 {
2525         /* the `frames' argument needs to be converted into a frame count
2526            relative to the start of the source before being passed in to the
2527            converter.
2528         */
2529         const framepos_t source_start = _region->position() - _region->start();
2530         return  _source_relative_time_converter.from (frames - source_start);
2531 }
2532
2533 framepos_t
2534 MidiRegionView::region_beats_to_region_frames(double beats) const
2535 {
2536         return _region_relative_time_converter.to(beats);
2537 }
2538
2539 double
2540 MidiRegionView::region_frames_to_region_beats(framepos_t frames) const
2541 {
2542         return _region_relative_time_converter.from(frames);
2543 }
2544
2545 void
2546 MidiRegionView::begin_resizing (bool /*at_front*/)
2547 {
2548         _resize_data.clear();
2549
2550         for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2551                 Note *note = dynamic_cast<Note*> (*i);
2552
2553                 // only insert CanvasNotes into the map
2554                 if (note) {
2555                         NoteResizeData *resize_data = new NoteResizeData();
2556                         resize_data->note = note;
2557
2558                         // create a new SimpleRect from the note which will be the resize preview
2559                         ArdourCanvas::Rectangle *resize_rect = new ArdourCanvas::Rectangle (_note_group, 
2560                                                                                             ArdourCanvas::Rect (note->x0(), note->y0(), note->x0(), note->y1()));
2561
2562                         // calculate the colors: get the color settings
2563                         uint32_t fill_color = UINT_RGBA_CHANGE_A(
2564                                 ARDOUR_UI::config()->get_canvasvar_MidiNoteSelected(),
2565                                 128);
2566
2567                         // make the resize preview notes more transparent and bright
2568                         fill_color = UINT_INTERPOLATE(fill_color, 0xFFFFFF40, 0.5);
2569
2570                         // calculate color based on note velocity
2571                         resize_rect->set_fill_color (UINT_INTERPOLATE(
2572                                 NoteBase::meter_style_fill_color(note->note()->velocity(), note->selected()),
2573                                 fill_color,
2574                                 0.85));
2575
2576                         resize_rect->set_outline_color (NoteBase::calculate_outline (
2577                                                                 ARDOUR_UI::config()->get_canvasvar_MidiNoteSelected()));
2578
2579                         resize_data->resize_rect = resize_rect;
2580                         _resize_data.push_back(resize_data);
2581                 }
2582         }
2583 }
2584
2585 /** Update resizing notes while user drags.
2586  * @param primary `primary' note for the drag; ie the one that is used as the reference in non-relative mode.
2587  * @param at_front which end of the note (true == note on, false == note off)
2588  * @param delta_x change in mouse position since the start of the drag
2589  * @param relative true if relative resizing is taking place, false if absolute resizing.  This only makes
2590  * a difference when multiple notes are being resized; in relative mode, each note's length is changed by the
2591  * amount of the drag.  In non-relative mode, all selected notes are set to have the same start or end point
2592  * as the \a primary note.
2593  */
2594 void
2595 MidiRegionView::update_resizing (NoteBase* primary, bool at_front, double delta_x, bool relative)
2596 {
2597         bool cursor_set = false;
2598
2599         for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
2600                 ArdourCanvas::Rectangle* resize_rect = (*i)->resize_rect;
2601                 Note* canvas_note = (*i)->note;
2602                 double current_x;
2603
2604                 if (at_front) {
2605                         if (relative) {
2606                                 current_x = canvas_note->x0() + delta_x;
2607                         } else {
2608                                 current_x = primary->x0() + delta_x;
2609                         }
2610                 } else {
2611                         if (relative) {
2612                                 current_x = canvas_note->x1() + delta_x;
2613                         } else {
2614                                 current_x = primary->x1() + delta_x;
2615                         }
2616                 }
2617
2618                 if (at_front) {
2619                         resize_rect->set_x0 (snap_to_pixel(current_x));
2620                         resize_rect->set_x1 (canvas_note->x1());
2621                 } else {
2622                         resize_rect->set_x1 (snap_to_pixel(current_x));
2623                         resize_rect->set_x0 (canvas_note->x0());
2624                 }
2625
2626                 if (!cursor_set) {
2627                         double beats;
2628
2629                         beats = snap_pixel_to_sample (current_x);
2630                         beats = region_frames_to_region_beats (beats);
2631
2632                         double len;
2633
2634                         if (at_front) {
2635                                 if (beats < canvas_note->note()->end_time()) {
2636                                         len = canvas_note->note()->time() - beats;
2637                                         len += canvas_note->note()->length();
2638                                 } else {
2639                                         len = 0;
2640                                 }
2641                         } else {
2642                                 if (beats >= canvas_note->note()->time()) {
2643                                         len = beats - canvas_note->note()->time();
2644                                 } else {
2645                                         len = 0;
2646                                 }
2647                         }
2648
2649                         char buf[16];
2650                         snprintf (buf, sizeof (buf), "%.3g beats", len);
2651                         show_verbose_cursor (buf, 0, 0);
2652
2653                         cursor_set = true;
2654                 }
2655
2656         }
2657 }
2658
2659
2660 /** Finish resizing notes when the user releases the mouse button.
2661  *  Parameters the same as for \a update_resizing().
2662  */
2663 void
2664 MidiRegionView::commit_resizing (NoteBase* primary, bool at_front, double delta_x, bool relative)
2665 {
2666         start_note_diff_command (_("resize notes"));
2667
2668         for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
2669                 Note*  canvas_note = (*i)->note;
2670                 ArdourCanvas::Rectangle*  resize_rect = (*i)->resize_rect;
2671
2672                 /* Get the new x position for this resize, which is in pixels relative
2673                  * to the region position.
2674                  */
2675                 
2676                 double current_x;
2677
2678                 if (at_front) {
2679                         if (relative) {
2680                                 current_x = canvas_note->x0() + delta_x;
2681                         } else {
2682                                 current_x = primary->x0() + delta_x;
2683                         }
2684                 } else {
2685                         if (relative) {
2686                                 current_x = canvas_note->x1() + delta_x;
2687                         } else {
2688                                 current_x = primary->x1() + delta_x;
2689                         }
2690                 }
2691
2692                 /* Convert that to a frame within the source */
2693                 current_x = snap_pixel_to_sample (current_x) + _region->start ();
2694
2695                 /* and then to beats */
2696                 current_x = region_frames_to_region_beats (current_x);
2697
2698                 if (at_front && current_x < canvas_note->note()->end_time()) {
2699                         note_diff_add_change (canvas_note, MidiModel::NoteDiffCommand::StartTime, current_x);
2700
2701                         double len = canvas_note->note()->time() - current_x;
2702                         len += canvas_note->note()->length();
2703
2704                         if (len > 0) {
2705                                 /* XXX convert to beats */
2706                                 note_diff_add_change (canvas_note, MidiModel::NoteDiffCommand::Length, len);
2707                         }
2708                 }
2709
2710                 if (!at_front) {
2711                         double len = current_x - canvas_note->note()->time();
2712
2713                         if (len > 0) {
2714                                 /* XXX convert to beats */
2715                                 note_diff_add_change (canvas_note, MidiModel::NoteDiffCommand::Length, len);
2716                         }
2717                 }
2718
2719                 delete resize_rect;
2720                 delete (*i);
2721         }
2722
2723         _resize_data.clear();
2724         apply_diff();
2725 }
2726
2727 void
2728 MidiRegionView::abort_resizing ()
2729 {
2730         for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
2731                 delete (*i)->resize_rect;
2732                 delete *i;
2733         }
2734
2735         _resize_data.clear ();
2736 }
2737
2738 void
2739 MidiRegionView::change_note_velocity(NoteBase* event, int8_t velocity, bool relative)
2740 {
2741         uint8_t new_velocity;
2742
2743         if (relative) {
2744                 new_velocity = event->note()->velocity() + velocity;
2745                 clamp_to_0_127(new_velocity);
2746         } else {
2747                 new_velocity = velocity;
2748         }
2749
2750         event->set_selected (event->selected()); // change color
2751
2752         note_diff_add_change (event, MidiModel::NoteDiffCommand::Velocity, new_velocity);
2753 }
2754
2755 void
2756 MidiRegionView::change_note_note (NoteBase* event, int8_t note, bool relative)
2757 {
2758         uint8_t new_note;
2759
2760         if (relative) {
2761                 new_note = event->note()->note() + note;
2762         } else {
2763                 new_note = note;
2764         }
2765
2766         clamp_to_0_127 (new_note);
2767         note_diff_add_change (event, MidiModel::NoteDiffCommand::NoteNumber, new_note);
2768 }
2769
2770 void
2771 MidiRegionView::trim_note (NoteBase* event, Evoral::MusicalTime front_delta, Evoral::MusicalTime end_delta)
2772 {
2773         bool change_start = false;
2774         bool change_length = false;
2775         Evoral::MusicalTime new_start = 0;
2776         Evoral::MusicalTime new_length = 0;
2777
2778         /* NOTE: the semantics of the two delta arguments are slightly subtle:
2779
2780            front_delta: if positive - move the start of the note later in time (shortening it)
2781            if negative - move the start of the note earlier in time (lengthening it)
2782
2783            end_delta:   if positive - move the end of the note later in time (lengthening it)
2784            if negative - move the end of the note earlier in time (shortening it)
2785         */
2786
2787         if (front_delta) {
2788                 if (front_delta < 0) {
2789
2790                         if (event->note()->time() < -front_delta) {
2791                                 new_start = 0;
2792                         } else {
2793                                 new_start = event->note()->time() + front_delta; // moves earlier
2794                         }
2795
2796                         /* start moved toward zero, so move the end point out to where it used to be.
2797                            Note that front_delta is negative, so this increases the length.
2798                         */
2799
2800                         new_length = event->note()->length() - front_delta;
2801                         change_start = true;
2802                         change_length = true;
2803
2804                 } else {
2805
2806                         Evoral::MusicalTime new_pos = event->note()->time() + front_delta;
2807
2808                         if (new_pos < event->note()->end_time()) {
2809                                 new_start = event->note()->time() + front_delta;
2810                                 /* start moved toward the end, so move the end point back to where it used to be */
2811                                 new_length = event->note()->length() - front_delta;
2812                                 change_start = true;
2813                                 change_length = true;
2814                         }
2815                 }
2816
2817         }
2818
2819         if (end_delta) {
2820                 bool can_change = true;
2821                 if (end_delta < 0) {
2822                         if (event->note()->length() < -end_delta) {
2823                                 can_change = false;
2824                         }
2825                 }
2826
2827                 if (can_change) {
2828                         new_length = event->note()->length() + end_delta;
2829                         change_length = true;
2830                 }
2831         }
2832
2833         if (change_start) {
2834                 note_diff_add_change (event, MidiModel::NoteDiffCommand::StartTime, new_start);
2835         }
2836
2837         if (change_length) {
2838                 note_diff_add_change (event, MidiModel::NoteDiffCommand::Length, new_length);
2839         }
2840 }
2841
2842 void
2843 MidiRegionView::change_note_channel (NoteBase* event, int8_t chn, bool relative)
2844 {
2845         uint8_t new_channel;
2846
2847         if (relative) {
2848                 if (chn < 0.0) {
2849                         if (event->note()->channel() < -chn) {
2850                                 new_channel = 0;
2851                         } else {
2852                                 new_channel = event->note()->channel() + chn;
2853                         }
2854                 } else {
2855                         new_channel = event->note()->channel() + chn;
2856                 }
2857         } else {
2858                 new_channel = (uint8_t) chn;
2859         }
2860
2861         note_diff_add_change (event, MidiModel::NoteDiffCommand::Channel, new_channel);
2862 }
2863
2864 void
2865 MidiRegionView::change_note_time (NoteBase* event, Evoral::MusicalTime delta, bool relative)
2866 {
2867         Evoral::MusicalTime new_time;
2868
2869         if (relative) {
2870                 if (delta < 0.0) {
2871                         if (event->note()->time() < -delta) {
2872                                 new_time = 0;
2873                         } else {
2874                                 new_time = event->note()->time() + delta;
2875                         }
2876                 } else {
2877                         new_time = event->note()->time() + delta;
2878                 }
2879         } else {
2880                 new_time = delta;
2881         }
2882
2883         note_diff_add_change (event, MidiModel::NoteDiffCommand::StartTime, new_time);
2884 }
2885
2886 void
2887 MidiRegionView::change_note_length (NoteBase* event, Evoral::MusicalTime t)
2888 {
2889         note_diff_add_change (event, MidiModel::NoteDiffCommand::Length, t);
2890 }
2891
2892 void
2893 MidiRegionView::change_velocities (bool up, bool fine, bool allow_smush, bool all_together)
2894 {
2895         int8_t delta;
2896         int8_t value = 0;
2897
2898         if (_selection.empty()) {
2899                 return;
2900         }
2901
2902         if (fine) {
2903                 delta = 1;
2904         } else {
2905                 delta = 10;
2906         }
2907
2908         if (!up) {
2909                 delta = -delta;
2910         }
2911
2912         if (!allow_smush) {
2913                 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2914                         if ((*i)->note()->velocity() < -delta || (*i)->note()->velocity() + delta > 127) {
2915                                 goto cursor_label;
2916                         }
2917                 }
2918         }
2919
2920         start_note_diff_command (_("change velocities"));
2921
2922         for (Selection::iterator i = _selection.begin(); i != _selection.end();) {
2923                 Selection::iterator next = i;
2924                 ++next;
2925
2926                 if (all_together) {
2927                         if (i == _selection.begin()) {
2928                                 change_note_velocity (*i, delta, true);
2929                                 value = (*i)->note()->velocity() + delta;
2930                         } else {
2931                                 change_note_velocity (*i, value, false);
2932                         }
2933
2934                 } else {
2935                         change_note_velocity (*i, delta, true);
2936                 }
2937
2938                 i = next;
2939         }
2940
2941         apply_diff();
2942
2943   cursor_label:
2944         if (!_selection.empty()) {
2945                 char buf[24];
2946                 snprintf (buf, sizeof (buf), "Vel %d",
2947                           (int) (*_selection.begin())->note()->velocity());
2948                 show_verbose_cursor (buf, 10, 10);
2949         }
2950 }
2951
2952
2953 void
2954 MidiRegionView::transpose (bool up, bool fine, bool allow_smush)
2955 {
2956         if (_selection.empty()) {
2957                 return;
2958         }
2959
2960         int8_t delta;
2961
2962         if (fine) {
2963                 delta = 1;
2964         } else {
2965                 delta = 12;
2966         }
2967
2968         if (!up) {
2969                 delta = -delta;
2970         }
2971
2972         if (!allow_smush) {
2973                 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2974                         if (!up) {
2975                                 if ((int8_t) (*i)->note()->note() + delta <= 0) {
2976                                         return;
2977                                 }
2978                         } else {
2979                                 if ((int8_t) (*i)->note()->note() + delta > 127) {
2980                                         return;
2981                                 }
2982                         }
2983                 }
2984         }
2985
2986         start_note_diff_command (_("transpose"));
2987
2988         for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
2989                 Selection::iterator next = i;
2990                 ++next;
2991                 change_note_note (*i, delta, true);
2992                 i = next;
2993         }
2994
2995         apply_diff ();
2996 }
2997
2998 void
2999 MidiRegionView::change_note_lengths (bool fine, bool shorter, Evoral::MusicalTime delta, bool start, bool end)
3000 {
3001         if (delta == 0.0) {
3002                 if (fine) {
3003                         delta = 1.0/128.0;
3004                 } else {
3005                         /* grab the current grid distance */
3006                         bool success;
3007                         delta = trackview.editor().get_grid_type_as_beats (success, _region->position());
3008                         if (!success) {
3009                                 /* XXX cannot get grid type as beats ... should always be possible ... FIX ME */
3010                                 error << string_compose (_("programming error: %1"), "Grid type not available as beats - TO BE FIXED") << endmsg;
3011                                 return;
3012                         }
3013                 }
3014         }
3015
3016         if (shorter) {
3017                 delta = -delta;
3018         }
3019
3020         start_note_diff_command (_("change note lengths"));
3021
3022         for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
3023                 Selection::iterator next = i;
3024                 ++next;
3025
3026                 /* note the negation of the delta for start */
3027
3028                 trim_note (*i, (start ? -delta : 0), (end ? delta : 0));
3029                 i = next;
3030         }
3031
3032         apply_diff ();
3033
3034 }
3035
3036 void
3037 MidiRegionView::nudge_notes (bool forward)
3038 {
3039         if (_selection.empty()) {
3040                 return;
3041         }
3042
3043         /* pick a note as the point along the timeline to get the nudge distance.
3044            its not necessarily the earliest note, so we may want to pull the notes out
3045            into a vector and sort before using the first one.
3046         */
3047
3048         framepos_t ref_point = source_beats_to_absolute_frames ((*(_selection.begin()))->note()->time());
3049         framepos_t unused;
3050         framecnt_t distance;
3051
3052         if (trackview.editor().snap_mode() == Editing::SnapOff) {
3053
3054                 /* grid is off - use nudge distance */
3055
3056                 distance = trackview.editor().get_nudge_distance (ref_point, unused);
3057
3058         } else {
3059
3060                 /* use grid */
3061
3062                 framepos_t next_pos = ref_point;
3063
3064                 if (forward) {
3065                         if (max_framepos - 1 < next_pos) {
3066                                 next_pos += 1;
3067                         }
3068                 } else {
3069                         if (next_pos == 0) {
3070                                 return;
3071                         }
3072                         next_pos -= 1;
3073                 }
3074
3075                 trackview.editor().snap_to (next_pos, (forward ? 1 : -1), false);
3076                 distance = ref_point - next_pos;
3077         }
3078
3079         if (distance == 0) {
3080                 return;
3081         }
3082
3083         Evoral::MusicalTime delta = region_frames_to_region_beats (fabs ((double)distance));
3084
3085         if (!forward) {
3086                 delta = -delta;
3087         }
3088
3089         start_note_diff_command (_("nudge"));
3090
3091         for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
3092                 Selection::iterator next = i;
3093                 ++next;
3094                 change_note_time (*i, delta, true);
3095                 i = next;
3096         }
3097
3098         apply_diff ();
3099 }
3100
3101 void
3102 MidiRegionView::change_channel(uint8_t channel)
3103 {
3104         start_note_diff_command(_("change channel"));
3105         for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3106                 note_diff_add_change (*i, MidiModel::NoteDiffCommand::Channel, channel);
3107         }
3108
3109         apply_diff();
3110 }
3111
3112
3113 void
3114 MidiRegionView::note_entered(NoteBase* ev)
3115 {
3116         Editor* editor = dynamic_cast<Editor*>(&trackview.editor());
3117
3118         pre_enter_cursor = editor->get_canvas_cursor ();
3119
3120         if (_mouse_state == SelectTouchDragging) {
3121                 note_selected (ev, true);
3122         }
3123
3124         show_verbose_cursor (ev->note ());
3125 }
3126
3127 void
3128 MidiRegionView::note_left (NoteBase*)
3129 {
3130         Editor* editor = dynamic_cast<Editor*>(&trackview.editor());
3131
3132         for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3133                 (*i)->hide_velocity ();
3134         }
3135
3136         editor->verbose_cursor()->hide ();
3137
3138         if (pre_enter_cursor) {
3139                 editor->set_canvas_cursor (pre_enter_cursor);
3140                 pre_enter_cursor = 0;
3141         }
3142 }
3143
3144 void
3145 MidiRegionView::patch_entered (PatchChange* p)
3146 {
3147         ostringstream s;
3148         /* XXX should get patch name if we can */
3149         s << _("Bank ") << (p->patch()->bank() + MIDI_BP_ZERO) << '\n' 
3150           << _("Program ") << ((int) p->patch()->program()) + MIDI_BP_ZERO << '\n' 
3151           << _("Channel ") << ((int) p->patch()->channel() + 1);
3152         show_verbose_cursor (s.str(), 10, 20);
3153         p->item().grab_focus();
3154 }
3155
3156 void
3157 MidiRegionView::patch_left (PatchChange *)
3158 {
3159         trackview.editor().verbose_cursor()->hide ();
3160         /* focus will transfer back via the enter-notify event sent to this
3161          * midi region view.
3162          */
3163 }
3164
3165 void
3166 MidiRegionView::sysex_entered (SysEx* p)
3167 {
3168         ostringstream s;
3169         // CAIROCANVAS
3170         // need a way to extract text from p->_flag->_text
3171         // s << p->text();
3172         // show_verbose_cursor (s.str(), 10, 20);
3173         p->item().grab_focus();
3174 }
3175
3176 void
3177 MidiRegionView::sysex_left (SysEx *)
3178 {
3179         trackview.editor().verbose_cursor()->hide ();
3180         /* focus will transfer back via the enter-notify event sent to this
3181          * midi region view.
3182          */
3183 }
3184
3185 void
3186 MidiRegionView::note_mouse_position (float x_fraction, float /*y_fraction*/, bool can_set_cursor)
3187 {
3188         Editor* editor = dynamic_cast<Editor*>(&trackview.editor());
3189         Editing::MouseMode mm = editor->current_mouse_mode();
3190         bool trimmable = (mm == MouseObject || mm == MouseTimeFX || mm == MouseDraw);
3191
3192         if (trimmable && x_fraction > 0.0 && x_fraction < 0.2) {
3193                 editor->set_canvas_cursor (editor->cursors()->left_side_trim);
3194         } else if (trimmable && x_fraction >= 0.8 && x_fraction < 1.0) {
3195                 editor->set_canvas_cursor (editor->cursors()->right_side_trim);
3196         } else {
3197                 if (pre_enter_cursor && can_set_cursor) {
3198                         editor->set_canvas_cursor (pre_enter_cursor);
3199                 }
3200         }
3201 }
3202
3203 void
3204 MidiRegionView::set_frame_color()
3205 {
3206         uint32_t f;
3207
3208         TimeAxisViewItem::set_frame_color ();
3209
3210         if (!frame) {
3211                 return;
3212         }
3213
3214         if (_selected) {
3215                 f = ARDOUR_UI::config()->get_canvasvar_SelectedFrameBase();
3216         } else if (high_enough_for_name) {
3217                 f= ARDOUR_UI::config()->get_canvasvar_MidiFrameBase();
3218         } else {
3219                 f = fill_color;
3220         }
3221
3222         if (!rect_visible) {
3223                 f = UINT_RGBA_CHANGE_A (f, 0);
3224         }
3225
3226         frame->set_fill_color (f);
3227 }
3228
3229 void
3230 MidiRegionView::midi_channel_mode_changed ()
3231 {
3232         MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3233         uint16_t mask = mtv->midi_track()->get_playback_channel_mask();
3234         ChannelMode mode = mtv->midi_track()->get_playback_channel_mode ();
3235
3236         if (mode == ForceChannel) {
3237                 mask = 0xFFFF; // Show all notes as active (below)
3238         }
3239
3240         // Update notes for selection
3241         for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3242                 (*i)->on_channel_selection_change (mask);
3243         }
3244
3245         _patch_changes.clear ();
3246         display_patch_changes ();
3247 }
3248
3249 void
3250 MidiRegionView::instrument_settings_changed ()
3251 {
3252         redisplay_model();
3253 }
3254
3255 void
3256 MidiRegionView::cut_copy_clear (Editing::CutCopyOp op)
3257 {
3258         if (_selection.empty()) {
3259                 return;
3260         }
3261
3262         PublicEditor& editor (trackview.editor());
3263
3264         switch (op) {
3265         case Delete:
3266                 /* XXX what to do ? */
3267                 break;
3268         case Cut:
3269         case Copy:
3270                 editor.get_cut_buffer().add (selection_as_cut_buffer());
3271                 break;
3272         default:
3273                 break;
3274         }
3275
3276         if (op != Copy) {
3277
3278                 start_note_diff_command();
3279
3280                 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3281                         switch (op) {
3282                         case Copy:
3283                                 break;
3284                         case Delete:
3285                         case Cut:
3286                         case Clear:
3287                                 note_diff_remove_note (*i);
3288                                 break;
3289                         }
3290                 }
3291
3292                 apply_diff();
3293         }
3294 }
3295
3296 MidiCutBuffer*
3297 MidiRegionView::selection_as_cut_buffer () const
3298 {
3299         Notes notes;
3300
3301         for (Selection::const_iterator i = _selection.begin(); i != _selection.end(); ++i) {
3302                 NoteType* n = (*i)->note().get();
3303                 notes.insert (boost::shared_ptr<NoteType> (new NoteType (*n)));
3304         }
3305
3306         MidiCutBuffer* cb = new MidiCutBuffer (trackview.session());
3307         cb->set (notes);
3308
3309         return cb;
3310 }
3311
3312 /** This method handles undo */
3313 void
3314 MidiRegionView::paste (framepos_t pos, float times, const MidiCutBuffer& mcb)
3315 {
3316         if (mcb.empty()) {
3317                 return;
3318         }
3319
3320         DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("MIDI paste @ %1 times %2\n", pos, times));
3321
3322         trackview.session()->begin_reversible_command (_("paste"));
3323
3324         start_note_diff_command (_("paste"));
3325
3326         Evoral::MusicalTime beat_delta;
3327         Evoral::MusicalTime paste_pos_beats;
3328         Evoral::MusicalTime duration;
3329         Evoral::MusicalTime end_point = 0;
3330
3331         duration = (*mcb.notes().rbegin())->end_time() - (*mcb.notes().begin())->time();
3332         paste_pos_beats = absolute_frames_to_source_beats (pos);
3333         beat_delta = (*mcb.notes().begin())->time() - paste_pos_beats;
3334         paste_pos_beats = 0;
3335
3336         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",
3337                                                        (*mcb.notes().begin())->time(),
3338                                                        (*mcb.notes().rbegin())->end_time(),
3339                                                        duration, pos, _region->position(),
3340                                                        paste_pos_beats, beat_delta));
3341
3342         clear_selection ();
3343
3344         for (int n = 0; n < (int) times; ++n) {
3345
3346                 for (Notes::const_iterator i = mcb.notes().begin(); i != mcb.notes().end(); ++i) {
3347
3348                         boost::shared_ptr<NoteType> copied_note (new NoteType (*((*i).get())));
3349                         copied_note->set_time (paste_pos_beats + copied_note->time() - beat_delta);
3350
3351                         /* make all newly added notes selected */
3352
3353                         note_diff_add_note (copied_note, true);
3354                         end_point = copied_note->end_time();
3355                 }
3356
3357                 paste_pos_beats += duration;
3358         }
3359
3360         /* if we pasted past the current end of the region, extend the region */
3361
3362         framepos_t end_frame = source_beats_to_absolute_frames (end_point);
3363         framepos_t region_end = _region->position() + _region->length() - 1;
3364
3365         if (end_frame > region_end) {
3366
3367                 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("Paste extended region from %1 to %2\n", region_end, end_frame));
3368
3369                 _region->clear_changes ();
3370                 _region->set_length (end_frame - _region->position());
3371                 trackview.session()->add_command (new StatefulDiffCommand (_region));
3372         }
3373
3374         apply_diff (true);
3375
3376         trackview.session()->commit_reversible_command ();
3377 }
3378
3379 struct EventNoteTimeEarlyFirstComparator {
3380         bool operator() (NoteBase* a, NoteBase* b) {
3381                 return a->note()->time() < b->note()->time();
3382         }
3383 };
3384
3385 void
3386 MidiRegionView::time_sort_events ()
3387 {
3388         if (!_sort_needed) {
3389                 return;
3390         }
3391
3392         EventNoteTimeEarlyFirstComparator cmp;
3393         _events.sort (cmp);
3394
3395         _sort_needed = false;
3396 }
3397
3398 void
3399 MidiRegionView::goto_next_note (bool add_to_selection)
3400 {
3401         bool use_next = false;
3402
3403         if (_events.back()->selected()) {
3404                 return;
3405         }
3406
3407         time_sort_events ();
3408
3409         MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3410         uint16_t const channel_mask = mtv->midi_track()->get_playback_channel_mask();
3411
3412         for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3413                 if ((*i)->selected()) {
3414                         use_next = true;
3415                         continue;
3416                 } else if (use_next) {
3417                         if (channel_mask & (1 << (*i)->note()->channel())) {
3418                                 if (!add_to_selection) {
3419                                         unique_select (*i);
3420                                 } else {
3421                                         note_selected (*i, true, false);
3422                                 }
3423                                 return;
3424                         }
3425                 }
3426         }
3427
3428         /* use the first one */
3429
3430         if (!_events.empty() && (channel_mask & (1 << _events.front()->note()->channel ()))) {
3431                 unique_select (_events.front());
3432         }
3433 }
3434
3435 void
3436 MidiRegionView::goto_previous_note (bool add_to_selection)
3437 {
3438         bool use_next = false;
3439
3440         if (_events.front()->selected()) {
3441                 return;
3442         }
3443
3444         time_sort_events ();
3445
3446         MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3447         uint16_t const channel_mask = mtv->midi_track()->get_playback_channel_mask ();
3448
3449         for (Events::reverse_iterator i = _events.rbegin(); i != _events.rend(); ++i) {
3450                 if ((*i)->selected()) {
3451                         use_next = true;
3452                         continue;
3453                 } else if (use_next) {
3454                         if (channel_mask & (1 << (*i)->note()->channel())) {
3455                                 if (!add_to_selection) {
3456                                         unique_select (*i);
3457                                 } else {
3458                                         note_selected (*i, true, false);
3459                                 }
3460                                 return;
3461                         }
3462                 }
3463         }
3464
3465         /* use the last one */
3466
3467         if (!_events.empty() && (channel_mask & (1 << (*_events.rbegin())->note()->channel ()))) {
3468                 unique_select (*(_events.rbegin()));
3469         }
3470 }
3471
3472 void
3473 MidiRegionView::selection_as_notelist (Notes& selected, bool allow_all_if_none_selected)
3474 {
3475         bool had_selected = false;
3476
3477         time_sort_events ();
3478
3479         for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3480                 if ((*i)->selected()) {
3481                         selected.insert ((*i)->note());
3482                         had_selected = true;
3483                 }
3484         }
3485
3486         if (allow_all_if_none_selected && !had_selected) {
3487                 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3488                         selected.insert ((*i)->note());
3489                 }
3490         }
3491 }
3492
3493 void
3494 MidiRegionView::update_ghost_note (double x, double y)
3495 {
3496         MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3497
3498         _last_ghost_x = x;
3499         _last_ghost_y = y;
3500
3501         _note_group->canvas_to_item (x, y);
3502
3503         PublicEditor& editor = trackview.editor ();
3504         
3505         framepos_t const unsnapped_frame = editor.pixel_to_sample (x);
3506         framecnt_t grid_frames;
3507         framepos_t const f = snap_frame_to_grid_underneath (unsnapped_frame, grid_frames);
3508
3509         /* use region_frames... because we are converting a delta within the region
3510         */
3511          
3512         bool success;
3513         double length = editor.get_grid_type_as_beats (success, unsnapped_frame);
3514
3515         if (!success) {
3516                 length = 1;
3517         }
3518
3519         /* note that this sets the time of the ghost note in beats relative to
3520            the start of the source; that is how all note times are stored.
3521         */
3522         _ghost_note->note()->set_time (absolute_frames_to_source_beats (f + _region->position ()));
3523         _ghost_note->note()->set_length (length);
3524         _ghost_note->note()->set_note (midi_stream_view()->y_to_note (y));
3525         _ghost_note->note()->set_channel (mtv->get_channel_for_add ());
3526
3527         /* the ghost note does not appear in ghost regions, so pass false in here */
3528         update_note (_ghost_note, false);
3529
3530         show_verbose_cursor (_ghost_note->note ());
3531 }
3532
3533 void
3534 MidiRegionView::create_ghost_note (double x, double y)
3535 {
3536         remove_ghost_note ();
3537
3538         boost::shared_ptr<NoteType> g (new NoteType);
3539         _ghost_note = new Note (*this, _note_group, g);
3540         _ghost_note->set_ignore_events (true);
3541         _ghost_note->set_outline_color (0x000000aa);
3542         update_ghost_note (x, y);
3543         _ghost_note->show ();
3544
3545         _last_ghost_x = x;
3546         _last_ghost_y = y;
3547
3548         show_verbose_cursor (_ghost_note->note ());
3549 }
3550
3551 void
3552 MidiRegionView::snap_changed ()
3553 {
3554         if (!_ghost_note) {
3555                 return;
3556         }
3557
3558         create_ghost_note (_last_ghost_x, _last_ghost_y);
3559 }
3560
3561 void
3562 MidiRegionView::drop_down_keys ()
3563 {
3564         _mouse_state = None;
3565 }
3566
3567 void
3568 MidiRegionView::maybe_select_by_position (GdkEventButton* ev, double /*x*/, double y)
3569 {
3570         double note = midi_stream_view()->y_to_note(y);
3571         Events e;
3572         MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3573
3574         uint16_t chn_mask = mtv->midi_track()->get_playback_channel_mask();
3575
3576         if (Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) {
3577                 get_events (e, Evoral::Sequence<Evoral::MusicalTime>::PitchGreaterThanOrEqual, (uint8_t) floor (note), chn_mask);
3578         } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
3579                 get_events (e, Evoral::Sequence<Evoral::MusicalTime>::PitchLessThanOrEqual, (uint8_t) floor (note), chn_mask);
3580         } else {
3581                 return;
3582         }
3583
3584         bool add_mrv_selection = false;
3585
3586         if (_selection.empty()) {
3587                 add_mrv_selection = true;
3588         }
3589
3590         for (Events::iterator i = e.begin(); i != e.end(); ++i) {
3591                 if (_selection.insert (*i).second) {
3592                         (*i)->set_selected (true);
3593                 }
3594         }
3595
3596         if (add_mrv_selection) {
3597                 PublicEditor& editor (trackview.editor());
3598                 editor.get_selection().add (this);
3599         }
3600 }
3601
3602 void
3603 MidiRegionView::color_handler ()
3604 {
3605         RegionView::color_handler ();
3606
3607         for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3608                 (*i)->set_selected ((*i)->selected()); // will change color
3609         }
3610
3611         /* XXX probably more to do here */
3612 }
3613
3614 void
3615 MidiRegionView::enable_display (bool yn)
3616 {
3617         RegionView::enable_display (yn);
3618         if (yn) {
3619                 redisplay_model ();
3620         }
3621 }
3622
3623 void
3624 MidiRegionView::show_step_edit_cursor (Evoral::MusicalTime pos)
3625 {
3626         if (_step_edit_cursor == 0) {
3627                 ArdourCanvas::Group* const group = (ArdourCanvas::Group*)get_canvas_group();
3628
3629                 _step_edit_cursor = new ArdourCanvas::Rectangle (group);
3630                 _step_edit_cursor->set_y0 (0);
3631                 _step_edit_cursor->set_y1 (midi_stream_view()->contents_height());
3632                 _step_edit_cursor->set_fill_color (RGBA_TO_UINT (45,0,0,90));
3633                 _step_edit_cursor->set_outline_color (RGBA_TO_UINT (85,0,0,90));
3634         }
3635
3636         move_step_edit_cursor (pos);
3637         _step_edit_cursor->show ();
3638 }
3639
3640 void
3641 MidiRegionView::move_step_edit_cursor (Evoral::MusicalTime pos)
3642 {
3643         _step_edit_cursor_position = pos;
3644
3645         if (_step_edit_cursor) {
3646                 double pixel = trackview.editor().sample_to_pixel (region_beats_to_region_frames (pos));
3647                 _step_edit_cursor->set_x0 (pixel);
3648                 set_step_edit_cursor_width (_step_edit_cursor_width);
3649         }
3650 }
3651
3652 void
3653 MidiRegionView::hide_step_edit_cursor ()
3654 {
3655         if (_step_edit_cursor) {
3656                 _step_edit_cursor->hide ();
3657         }
3658 }
3659
3660 void
3661 MidiRegionView::set_step_edit_cursor_width (Evoral::MusicalTime beats)
3662 {
3663         _step_edit_cursor_width = beats;
3664
3665         if (_step_edit_cursor) {
3666                 _step_edit_cursor->set_x1 (_step_edit_cursor->x0() + trackview.editor().sample_to_pixel (region_beats_to_region_frames (beats)));
3667         }
3668 }
3669
3670 /** Called when a diskstream on our track has received some data.  Update the view, if applicable.
3671  *  @param w Source that the data will end up in.
3672  */
3673 void
3674 MidiRegionView::data_recorded (boost::weak_ptr<MidiSource> w)
3675 {
3676         if (!_active_notes) {
3677                 /* we aren't actively being recorded to */
3678                 return;
3679         }
3680
3681         boost::shared_ptr<MidiSource> src = w.lock ();
3682         if (!src || src != midi_region()->midi_source()) {
3683                 /* recorded data was not destined for our source */
3684                 return;
3685         }
3686
3687         MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*> (&trackview);
3688
3689         boost::shared_ptr<MidiBuffer> buf = mtv->midi_track()->get_gui_feed_buffer ();
3690
3691         BeatsFramesConverter converter (trackview.session()->tempo_map(), mtv->midi_track()->get_capture_start_frame (0));
3692
3693         framepos_t back = max_framepos;
3694
3695         for (MidiBuffer::iterator i = buf->begin(); i != buf->end(); ++i) {
3696                 Evoral::MIDIEvent<MidiBuffer::TimeType> const ev (*i, false);
3697
3698                 if (ev.is_channel_event()) {
3699                         if (get_channel_mode() == FilterChannels) {
3700                                 if (((uint16_t(1) << ev.channel()) & get_selected_channels()) == 0) {
3701                                         continue;
3702                                 }
3703                         }
3704                 }
3705
3706                 /* ev.time() is in session frames, so (ev.time() - converter.origin_b()) is
3707                    frames from the start of the source, and so time_beats is in terms of the
3708                    source.
3709                 */
3710
3711                 Evoral::MusicalTime const time_beats = converter.from (ev.time () - converter.origin_b ());
3712
3713                 if (ev.type() == MIDI_CMD_NOTE_ON) {
3714                         boost::shared_ptr<NoteType> note (
3715                                 new NoteType (ev.channel(), time_beats, 0, ev.note(), ev.velocity()));
3716
3717                         add_note (note, true);
3718
3719                         /* fix up our note range */
3720                         if (ev.note() < _current_range_min) {
3721                                 midi_stream_view()->apply_note_range (ev.note(), _current_range_max, true);
3722                         } else if (ev.note() > _current_range_max) {
3723                                 midi_stream_view()->apply_note_range (_current_range_min, ev.note(), true);
3724                         }
3725
3726                 } else if (ev.type() == MIDI_CMD_NOTE_OFF) {
3727                         resolve_note (ev.note (), time_beats);
3728                 }
3729
3730                 back = ev.time ();
3731         }
3732
3733         midi_stream_view()->check_record_layers (region(), back);
3734 }
3735
3736 void
3737 MidiRegionView::trim_front_starting ()
3738 {
3739         /* Reparent the note group to the region view's parent, so that it doesn't change
3740            when the region view is trimmed.
3741         */
3742         _temporary_note_group = new ArdourCanvas::Group (group->parent ());
3743         _temporary_note_group->move (group->position ());
3744         _note_group->reparent (_temporary_note_group);
3745 }
3746
3747 void
3748 MidiRegionView::trim_front_ending ()
3749 {
3750         _note_group->reparent (group);
3751         delete _temporary_note_group;
3752         _temporary_note_group = 0;
3753
3754         if (_region->start() < 0) {
3755                 /* Trim drag made start time -ve; fix this */
3756                 midi_region()->fix_negative_start ();
3757         }
3758 }
3759
3760 void
3761 MidiRegionView::edit_patch_change (PatchChange* pc)
3762 {
3763         PatchChangeDialog d (&_source_relative_time_converter, trackview.session(), *pc->patch (), instrument_info(), Gtk::Stock::APPLY, true);
3764
3765         int response = d.run();
3766
3767         switch (response) {
3768         case Gtk::RESPONSE_ACCEPT:
3769                 break;
3770         case Gtk::RESPONSE_REJECT:
3771                 delete_patch_change (pc);
3772                 return;
3773         default:
3774                 return;
3775         }
3776
3777         change_patch_change (pc->patch(), d.patch ());
3778 }
3779
3780 void
3781 MidiRegionView::delete_sysex (SysEx* /*sysex*/)
3782 {
3783         // CAIROCANVAS
3784         // sysyex object doesn't have a pointer to a sysex event
3785         // MidiModel::SysExDiffCommand* c = _model->new_sysex_diff_command (_("delete sysex"));
3786         // c->remove (sysex->sysex());
3787         // _model->apply_command (*trackview.session(), c);
3788
3789         //_sys_exes.clear ();
3790         // display_sysexes();
3791 }
3792
3793 void
3794 MidiRegionView::show_verbose_cursor (boost::shared_ptr<NoteType> n) const
3795 {
3796         using namespace MIDI::Name;
3797
3798         std::string name;
3799
3800         MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3801         if (mtv) {
3802                 boost::shared_ptr<MasterDeviceNames> device_names(mtv->get_device_names());
3803                 if (device_names) {
3804                         MIDI::Name::PatchPrimaryKey patch_key;
3805                         get_patch_key_at(n->time(), n->channel(), patch_key);
3806                         name = device_names->note_name(mtv->gui_property(X_("midnam-custom-device-mode")),
3807                                                        n->channel(),
3808                                                        patch_key.bank_number,
3809                                                        patch_key.program_number,
3810                                                        n->note());
3811                 }
3812         }
3813
3814         char buf[128];
3815         snprintf (buf, sizeof (buf), "%d %s\nCh %d Vel %d",
3816                   (int) n->note (),
3817                   name.empty() ? Evoral::midi_note_name (n->note()).c_str() : name.c_str(),
3818                   (int) n->channel() + 1,
3819                   (int) n->velocity());
3820
3821         show_verbose_cursor(buf, 10, 20);
3822 }
3823
3824 void
3825 MidiRegionView::show_verbose_cursor (string const & text, double xoffset, double yoffset) const
3826 {
3827         double wx, wy;
3828
3829         trackview.editor().verbose_cursor()->set_text (text);
3830         trackview.editor().get_pointer_position (wx, wy);
3831
3832         wx += xoffset;
3833         wy += yoffset;
3834
3835         /* Flip the cursor above the mouse pointer if it would overlap the bottom of the canvas */
3836
3837         boost::optional<ArdourCanvas::Rect> bbo = trackview.editor().verbose_cursor()->item().bounding_box();
3838
3839         assert (bbo);
3840         
3841         ArdourCanvas::Rect bb = bbo.get();
3842
3843         if ((wy + bb.y1 - bb.y0) > trackview.editor().visible_canvas_height()) {
3844                 wy -= (bb.y1 - bb.y0) + 2 * yoffset;
3845         }
3846
3847         trackview.editor().verbose_cursor()->set_position (wx, wy);
3848         trackview.editor().verbose_cursor()->show ();
3849 }
3850
3851 /** @param p A session framepos.
3852  *  @param grid_frames Filled in with the number of frames that a grid interval is at p.
3853  *  @return p snapped to the grid subdivision underneath it.
3854  */
3855 framepos_t
3856 MidiRegionView::snap_frame_to_grid_underneath (framepos_t p, framecnt_t& grid_frames) const
3857 {
3858         PublicEditor& editor = trackview.editor ();
3859         
3860         bool success;
3861         Evoral::MusicalTime grid_beats = editor.get_grid_type_as_beats (success, p);
3862
3863         if (!success) {
3864                 grid_beats = 1;
3865         }
3866         
3867         grid_frames = region_beats_to_region_frames (grid_beats);
3868
3869         /* Hack so that we always snap to the note that we are over, instead of snapping
3870            to the next one if we're more than halfway through the one we're over.
3871         */
3872         if (editor.snap_mode() == SnapNormal && p >= grid_frames / 2) {
3873                 p -= grid_frames / 2;
3874         }
3875
3876         return snap_frame_to_frame (p);
3877 }
3878
3879 /** Called when the selection has been cleared in any MidiRegionView.
3880  *  @param rv MidiRegionView that the selection was cleared in.
3881  */
3882 void
3883 MidiRegionView::selection_cleared (MidiRegionView* rv)
3884 {
3885         if (rv == this) {
3886                 return;
3887         }
3888
3889         /* Clear our selection in sympathy; but don't signal the fact */
3890         clear_selection (false);
3891 }
3892
3893 void
3894 MidiRegionView::note_button_release ()
3895 {
3896         delete _note_player;
3897         _note_player = 0;
3898 }
3899
3900 ChannelMode
3901 MidiRegionView::get_channel_mode () const
3902 {
3903         RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (&trackview);
3904         return rtav->midi_track()->get_playback_channel_mode();
3905 }
3906
3907 uint16_t
3908 MidiRegionView::get_selected_channels () const
3909 {
3910         RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (&trackview);
3911         return rtav->midi_track()->get_playback_channel_mask();
3912 }
3913