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