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