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