add Delete button to patch change dialog when editing it
[ardour.git] / gtk2_ardour / midi_region_view.cc
1 /*
2     Copyright (C) 2001-2011 Paul Davis
3     Author: David Robillard
4
5     This program is free software; you can redistribute it and/or modify
6     it under the terms of the GNU General Public License as published by
7     the Free Software Foundation; either version 2 of the License, or
8     (at your option) any later version.
9
10     This program is distributed in the hope that it will be useful,
11     but WITHOUT ANY WARRANTY; without even the implied warranty of
12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13     GNU General Public License for more details.
14
15     You should have received a copy of the GNU General Public License
16     along with this program; if not, write to the Free Software
17     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 */
19
20 #include <cmath>
21 #include <cassert>
22 #include <algorithm>
23 #include <ostream>
24
25 #include <gtkmm.h>
26
27 #include "gtkmm2ext/gtk_ui.h"
28
29 #include <sigc++/signal.h>
30
31 #include "pbd/memento_command.h"
32 #include "pbd/stateful_diff_command.h"
33
34 #include "ardour/midi_region.h"
35 #include "ardour/midi_source.h"
36 #include "ardour/midi_model.h"
37 #include "ardour/midi_patch_manager.h"
38 #include "ardour/session.h"
39
40 #include "evoral/Parameter.hpp"
41 #include "evoral/MIDIParameters.hpp"
42 #include "evoral/MIDIEvent.hpp"
43 #include "evoral/Control.hpp"
44 #include "evoral/midi_util.h"
45
46 #include "automation_region_view.h"
47 #include "automation_time_axis.h"
48 #include "canvas-hit.h"
49 #include "canvas-note.h"
50 #include "canvas_patch_change.h"
51 #include "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         MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
919         MidiStreamView* const view = mtv->midi_view();
920
921         double note = view->y_to_note(y);
922
923         assert(note >= 0.0);
924         assert(note <= 127.0);
925
926         // Start of note in frames relative to region start
927         if (snap_t) {
928                 framecnt_t grid_frames;
929                 t = snap_frame_to_grid_underneath (t, grid_frames);
930         }
931
932         assert (t >= 0);
933         assert (length != 0);
934
935         const boost::shared_ptr<NoteType> new_note (new NoteType (mtv->get_channel_for_add (),
936                                                                   region_frames_to_region_beats(t + _region->start()), 
937                                                                   length,
938                                                                   (uint8_t)note, 0x40));
939
940         if (_model->contains (new_note)) {
941                 return;
942         }
943
944         view->update_note_range(new_note->note());
945
946         MidiModel::NoteDiffCommand* cmd = _model->new_note_diff_command(_("add note"));
947         cmd->add (new_note);
948         _model->apply_command(*trackview.session(), cmd);
949
950         play_midi_note (new_note);
951 }
952
953 void
954 MidiRegionView::clear_events (bool with_selection_signal)
955 {
956         clear_selection (with_selection_signal);
957
958         MidiGhostRegion* gr;
959         for (std::vector<GhostRegion*>::iterator g = ghosts.begin(); g != ghosts.end(); ++g) {
960                 if ((gr = dynamic_cast<MidiGhostRegion*>(*g)) != 0) {
961                         gr->clear_events();
962                 }
963         }
964
965         for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
966                 delete *i;
967         }
968
969         _events.clear();
970         _patch_changes.clear();
971         _sys_exes.clear();
972         _optimization_iterator = _events.end();
973 }
974
975 void
976 MidiRegionView::display_model(boost::shared_ptr<MidiModel> model)
977 {
978         _model = model;
979
980         content_connection.disconnect ();
981         _model->ContentsChanged.connect (content_connection, invalidator (*this), boost::bind (&MidiRegionView::redisplay_model, this), gui_context());
982
983         clear_events ();
984
985         if (_enable_display) {
986                 redisplay_model();
987         }
988 }
989
990 void
991 MidiRegionView::start_note_diff_command (string name)
992 {
993         if (!_note_diff_command) {
994                 _note_diff_command = _model->new_note_diff_command (name);
995         }
996 }
997
998 void
999 MidiRegionView::note_diff_add_note (const boost::shared_ptr<NoteType> note, bool selected, bool show_velocity)
1000 {
1001         if (_note_diff_command) {
1002                 _note_diff_command->add (note);
1003         }
1004         if (selected) {
1005                 _marked_for_selection.insert(note);
1006         }
1007         if (show_velocity) {
1008                 _marked_for_velocity.insert(note);
1009         }
1010 }
1011
1012 void
1013 MidiRegionView::note_diff_remove_note (ArdourCanvas::CanvasNoteEvent* ev)
1014 {
1015         if (_note_diff_command && ev->note()) {
1016                 _note_diff_command->remove(ev->note());
1017         }
1018 }
1019
1020 void
1021 MidiRegionView::note_diff_add_change (ArdourCanvas::CanvasNoteEvent* ev,
1022                                       MidiModel::NoteDiffCommand::Property property,
1023                                       uint8_t val)
1024 {
1025         if (_note_diff_command) {
1026                 _note_diff_command->change (ev->note(), property, val);
1027         }
1028 }
1029
1030 void
1031 MidiRegionView::note_diff_add_change (ArdourCanvas::CanvasNoteEvent* ev,
1032                                       MidiModel::NoteDiffCommand::Property property,
1033                                       Evoral::MusicalTime val)
1034 {
1035         if (_note_diff_command) {
1036                 _note_diff_command->change (ev->note(), property, val);
1037         }
1038 }
1039
1040 void
1041 MidiRegionView::apply_diff (bool as_subcommand)
1042 {
1043         bool add_or_remove;
1044
1045         if (!_note_diff_command) {
1046                 return;
1047         }
1048
1049         if ((add_or_remove = _note_diff_command->adds_or_removes())) {
1050                 // Mark all selected notes for selection when model reloads
1051                 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
1052                         _marked_for_selection.insert((*i)->note());
1053                 }
1054         }
1055
1056         if (as_subcommand) {
1057                 _model->apply_command_as_subcommand (*trackview.session(), _note_diff_command);
1058         } else {
1059                 _model->apply_command (*trackview.session(), _note_diff_command);
1060         }
1061
1062         _note_diff_command = 0;
1063         midi_view()->midi_track()->playlist_modified();
1064
1065         if (add_or_remove) {
1066                 _marked_for_selection.clear();
1067         }
1068
1069         _marked_for_velocity.clear();
1070 }
1071
1072 void
1073 MidiRegionView::abort_command()
1074 {
1075         delete _note_diff_command;
1076         _note_diff_command = 0;
1077         clear_selection();
1078 }
1079
1080 CanvasNoteEvent*
1081 MidiRegionView::find_canvas_note (boost::shared_ptr<NoteType> note)
1082 {
1083         if (_optimization_iterator != _events.end()) {
1084                 ++_optimization_iterator;
1085         }
1086
1087         if (_optimization_iterator != _events.end() && (*_optimization_iterator)->note() == note) {
1088                 return *_optimization_iterator;
1089         }
1090
1091         for (_optimization_iterator = _events.begin(); _optimization_iterator != _events.end(); ++_optimization_iterator) {
1092                 if ((*_optimization_iterator)->note() == note) {
1093                         return *_optimization_iterator;
1094                 }
1095         }
1096
1097         return 0;
1098 }
1099
1100 void
1101 MidiRegionView::get_events (Events& e, Evoral::Sequence<Evoral::MusicalTime>::NoteOperator op, uint8_t val, int chan_mask)
1102 {
1103         MidiModel::Notes notes;
1104         _model->get_notes (notes, op, val, chan_mask);
1105
1106         for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
1107                 CanvasNoteEvent* cne = find_canvas_note (*n);
1108                 if (cne) {
1109                         e.push_back (cne);
1110                 }
1111         }
1112 }
1113
1114 void
1115 MidiRegionView::redisplay_model()
1116 {
1117         // Don't redisplay the model if we're currently recording and displaying that
1118         if (_active_notes) {
1119                 return;
1120         }
1121
1122         if (!_model) {
1123                 return;
1124         }
1125
1126         for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
1127                 (*i)->invalidate ();
1128         }
1129
1130         MidiModel::ReadLock lock(_model->read_lock());
1131
1132         MidiModel::Notes& notes (_model->notes());
1133         _optimization_iterator = _events.begin();
1134
1135         for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
1136
1137                 boost::shared_ptr<NoteType> note (*n);
1138                 CanvasNoteEvent* cne;
1139                 bool visible;
1140
1141                 if (note_in_region_range (note, visible)) {
1142
1143                         if ((cne = find_canvas_note (note)) != 0) {
1144
1145                                 cne->validate ();
1146
1147                                 CanvasNote* cn;
1148                                 CanvasHit* ch;
1149
1150                                 if ((cn = dynamic_cast<CanvasNote*>(cne)) != 0) {
1151                                         update_note (cn);
1152                                 } else if ((ch = dynamic_cast<CanvasHit*>(cne)) != 0) {
1153                                         update_hit (ch);
1154                                 }
1155
1156                                 if (visible) {
1157                                         cne->show ();
1158                                 } else {
1159                                         cne->hide ();
1160                                 }
1161
1162                         } else {
1163
1164                                 add_note (note, visible);
1165                         }
1166
1167                 } else {
1168
1169                         if ((cne = find_canvas_note (note)) != 0) {
1170                                 cne->validate ();
1171                                 cne->hide ();
1172                         }
1173                 }
1174         }
1175
1176
1177         /* remove note items that are no longer valid */
1178
1179         for (Events::iterator i = _events.begin(); i != _events.end(); ) {
1180                 if (!(*i)->valid ()) {
1181
1182                         for (vector<GhostRegion*>::iterator j = ghosts.begin(); j != ghosts.end(); ++j) {
1183                                 MidiGhostRegion* gr = dynamic_cast<MidiGhostRegion*> (*j);
1184                                 if (gr) {
1185                                         gr->remove_note (*i);
1186                                 }
1187                         }
1188                         
1189                         delete *i;
1190                         i = _events.erase (i);
1191                         
1192                 } else {
1193                         ++i;
1194                 }
1195         }
1196
1197         _patch_changes.clear();
1198         _sys_exes.clear();
1199
1200         display_sysexes();
1201         display_patch_changes ();
1202
1203         _marked_for_selection.clear ();
1204         _marked_for_velocity.clear ();
1205
1206         /* we may have caused _events to contain things out of order (e.g. if a note
1207            moved earlier or later). we don't generally need them in time order, but
1208            make a note that a sort is required for those cases that require it.
1209         */
1210
1211         _sort_needed = true;
1212 }
1213
1214 void
1215 MidiRegionView::display_patch_changes ()
1216 {
1217         MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1218         uint16_t chn_mask = mtv->channel_selector().get_selected_channels();
1219
1220         for (uint8_t i = 0; i < 16; ++i) {
1221                 display_patch_changes_on_channel (i, chn_mask & (1 << i));
1222         }
1223 }
1224
1225 /** @param active_channel true to display patch changes fully, false to display
1226  * them `greyed-out' (as on an inactive channel)
1227  */
1228 void
1229 MidiRegionView::display_patch_changes_on_channel (uint8_t channel, bool active_channel)
1230 {
1231         for (MidiModel::PatchChanges::const_iterator i = _model->patch_changes().begin(); i != _model->patch_changes().end(); ++i) {
1232
1233                 if ((*i)->channel() != channel) {
1234                         continue;
1235                 }
1236
1237                 string patch_name = instrument_info().get_patch_name ((*i)->bank(), (*i)->program(), channel);
1238                 add_canvas_patch_change (*i, patch_name, active_channel);
1239         }
1240 }
1241
1242 void
1243 MidiRegionView::display_sysexes()
1244 {
1245         bool have_periodic_system_messages = false;
1246         bool display_periodic_messages = true;
1247
1248         if (!Config->get_never_display_periodic_midi()) {
1249
1250                 for (MidiModel::SysExes::const_iterator i = _model->sysexes().begin(); i != _model->sysexes().end(); ++i) {
1251                         const boost::shared_ptr<const Evoral::MIDIEvent<Evoral::MusicalTime> > mev = 
1252                                 boost::static_pointer_cast<const Evoral::MIDIEvent<Evoral::MusicalTime> > (*i);
1253                         
1254                         if (mev) {
1255                                 if (mev->is_spp() || mev->is_mtc_quarter() || mev->is_mtc_full()) {
1256                                         have_periodic_system_messages = true;
1257                                         break;
1258                                 }
1259                         }
1260                 }
1261                 
1262                 if (have_periodic_system_messages) {
1263                         double zoom = trackview.editor().get_current_zoom (); // frames per pixel
1264                         
1265                         /* get an approximate value for the number of samples per video frame */
1266                         
1267                         double video_frame = trackview.session()->frame_rate() * (1.0/30);
1268                         
1269                         /* if we are zoomed out beyond than the cutoff (i.e. more
1270                          * frames per pixel than frames per 4 video frames), don't
1271                          * show periodic sysex messages.
1272                          */
1273                         
1274                         if (zoom > (video_frame*4)) {
1275                                 display_periodic_messages = false;
1276                         } 
1277                 }
1278         } else {
1279                 display_periodic_messages = false;
1280         }
1281
1282         for (MidiModel::SysExes::const_iterator i = _model->sysexes().begin(); i != _model->sysexes().end(); ++i) {
1283
1284                 const boost::shared_ptr<const Evoral::MIDIEvent<Evoral::MusicalTime> > mev = 
1285                         boost::static_pointer_cast<const Evoral::MIDIEvent<Evoral::MusicalTime> > (*i);
1286
1287                 Evoral::MusicalTime time = (*i)->time();
1288                 assert (time >= 0);
1289
1290                 if (mev) {
1291                         if (mev->is_spp() || mev->is_mtc_quarter() || mev->is_mtc_full()) {
1292                                 if (!display_periodic_messages) {
1293                                         continue;
1294                                 }
1295                         }
1296                 }
1297
1298                 ostringstream str;
1299                 str << hex;
1300                 for (uint32_t b = 0; b < (*i)->size(); ++b) {
1301                         str << int((*i)->buffer()[b]);
1302                         if (b != (*i)->size() -1) {
1303                                 str << " ";
1304                         }
1305                 }
1306                 string text = str.str();
1307
1308                 const double x = trackview.editor().frame_to_pixel(source_beats_to_region_frames(time));
1309
1310                 double height = midi_stream_view()->contents_height();
1311
1312                 boost::shared_ptr<CanvasSysEx> sysex = boost::shared_ptr<CanvasSysEx>(
1313                         new CanvasSysEx(*this, *_note_group, text, height, x, 1.0, (*i)));
1314
1315                 // Show unless message is beyond the region bounds
1316                 if (time - _region->start() >= _region->length() || time < _region->start()) {
1317                         sysex->hide();
1318                 } else {
1319                         sysex->show();
1320                 }
1321
1322                 _sys_exes.push_back(sysex);
1323         }
1324 }
1325
1326 MidiRegionView::~MidiRegionView ()
1327 {
1328         in_destructor = true;
1329
1330         trackview.editor().verbose_cursor()->hide ();
1331
1332         note_delete_connection.disconnect ();
1333
1334         delete _list_editor;
1335
1336         RegionViewGoingAway (this); /* EMIT_SIGNAL */
1337
1338         if (_active_notes) {
1339                 end_write();
1340         }
1341
1342         _selection_cleared_connection.disconnect ();
1343
1344         _selection.clear();
1345         clear_events (false);
1346
1347         delete _note_group;
1348         delete _note_diff_command;
1349         delete _step_edit_cursor;
1350         delete _temporary_note_group;
1351 }
1352
1353 void
1354 MidiRegionView::region_resized (const PropertyChange& what_changed)
1355 {
1356         RegionView::region_resized(what_changed);
1357
1358         if (what_changed.contains (ARDOUR::Properties::position)) {
1359                 set_duration(_region->length(), 0);
1360                 if (_enable_display) {
1361                         redisplay_model();
1362                 }
1363         }
1364 }
1365
1366 void
1367 MidiRegionView::reset_width_dependent_items (double pixel_width)
1368 {
1369         RegionView::reset_width_dependent_items(pixel_width);
1370         assert(_pixel_width == pixel_width);
1371
1372         if (_enable_display) {
1373                 redisplay_model();
1374         }
1375
1376         for (PatchChanges::iterator x = _patch_changes.begin(); x != _patch_changes.end(); ++x) {
1377                 if ((*x)->width() >= _pixel_width) {
1378                         (*x)->hide();
1379                 } else {
1380                         (*x)->show();
1381                 }
1382         }
1383
1384         move_step_edit_cursor (_step_edit_cursor_position);
1385         set_step_edit_cursor_width (_step_edit_cursor_width);
1386 }
1387
1388 void
1389 MidiRegionView::set_height (double height)
1390 {
1391         static const double FUDGE = 2.0;
1392         const double old_height = _height;
1393         RegionView::set_height(height);
1394         _height = height - FUDGE;
1395
1396         apply_note_range(midi_stream_view()->lowest_note(),
1397                          midi_stream_view()->highest_note(),
1398                          height != old_height + FUDGE);
1399
1400         if (name_pixbuf) {
1401                 name_pixbuf->raise_to_top();
1402         }
1403
1404         for (PatchChanges::iterator x = _patch_changes.begin(); x != _patch_changes.end(); ++x) {
1405                 (*x)->set_height (midi_stream_view()->contents_height());
1406         }
1407
1408         if (_step_edit_cursor) {
1409                 _step_edit_cursor->property_y2() = midi_stream_view()->contents_height();
1410         }
1411 }
1412
1413
1414 /** Apply the current note range from the stream view
1415  * by repositioning/hiding notes as necessary
1416  */
1417 void
1418 MidiRegionView::apply_note_range (uint8_t min, uint8_t max, bool force)
1419 {
1420         if (!_enable_display) {
1421                 return;
1422         }
1423
1424         if (!force && _current_range_min == min && _current_range_max == max) {
1425                 return;
1426         }
1427
1428         _current_range_min = min;
1429         _current_range_max = max;
1430
1431         for (Events::const_iterator i = _events.begin(); i != _events.end(); ++i) {
1432                 CanvasNoteEvent* event = *i;
1433                 boost::shared_ptr<NoteType> note (event->note());
1434
1435                 if (note->note() < _current_range_min ||
1436                     note->note() > _current_range_max) {
1437                         event->hide();
1438                 } else {
1439                         event->show();
1440                 }
1441
1442                 if (CanvasNote* cnote = dynamic_cast<CanvasNote*>(event)) {
1443
1444                         const double y1 = midi_stream_view()->note_to_y(note->note());
1445                         const double y2 = y1 + floor(midi_stream_view()->note_height());
1446
1447                         cnote->property_y1() = y1;
1448                         cnote->property_y2() = y2;
1449
1450                 } else if (CanvasHit* chit = dynamic_cast<CanvasHit*>(event)) {
1451
1452                         const double diamond_size = update_hit (chit);
1453
1454                         chit->set_height (diamond_size);
1455                 }
1456         }
1457 }
1458
1459 GhostRegion*
1460 MidiRegionView::add_ghost (TimeAxisView& tv)
1461 {
1462         CanvasNote* note;
1463
1464         double unit_position = _region->position () / samples_per_unit;
1465         MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*>(&tv);
1466         MidiGhostRegion* ghost;
1467
1468         if (mtv && mtv->midi_view()) {
1469                 /* if ghost is inserted into midi track, use a dedicated midi ghost canvas group
1470                    to allow having midi notes on top of note lines and waveforms.
1471                 */
1472                 ghost = new MidiGhostRegion (*mtv->midi_view(), trackview, unit_position);
1473         } else {
1474                 ghost = new MidiGhostRegion (tv, trackview, unit_position);
1475         }
1476
1477         for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
1478                 if ((note = dynamic_cast<CanvasNote*>(*i)) != 0) {
1479                         ghost->add_note(note);
1480                 }
1481         }
1482
1483         ghost->set_height ();
1484         ghost->set_duration (_region->length() / samples_per_unit);
1485         ghosts.push_back (ghost);
1486
1487         GhostRegion::CatchDeletion.connect (*this, invalidator (*this), boost::bind (&RegionView::remove_ghost, this, _1), gui_context());
1488
1489         return ghost;
1490 }
1491
1492
1493 /** Begin tracking note state for successive calls to add_event
1494  */
1495 void
1496 MidiRegionView::begin_write()
1497 {
1498         assert(!_active_notes);
1499         _active_notes = new CanvasNote*[128];
1500         for (unsigned i=0; i < 128; ++i) {
1501                 _active_notes[i] = 0;
1502         }
1503 }
1504
1505
1506 /** Destroy note state for add_event
1507  */
1508 void
1509 MidiRegionView::end_write()
1510 {
1511         delete[] _active_notes;
1512         _active_notes = 0;
1513         _marked_for_selection.clear();
1514         _marked_for_velocity.clear();
1515 }
1516
1517
1518 /** Resolve an active MIDI note (while recording).
1519  */
1520 void
1521 MidiRegionView::resolve_note(uint8_t note, double end_time)
1522 {
1523         if (midi_view()->note_mode() != Sustained) {
1524                 return;
1525         }
1526
1527         if (_active_notes && _active_notes[note]) {
1528
1529                 /* XXX is end_time really region-centric? I think so, because
1530                    this is a new region that we're recording, so source zero is
1531                    the same as region zero
1532                 */
1533                 const framepos_t end_time_frames = region_beats_to_region_frames(end_time);
1534
1535                 _active_notes[note]->property_x2() = trackview.editor().frame_to_pixel(end_time_frames);
1536                 _active_notes[note]->property_outline_what() = (guint32) 0xF; // all edges
1537                 _active_notes[note] = 0;
1538         }
1539 }
1540
1541
1542 /** Extend active notes to rightmost edge of region (if length is changed)
1543  */
1544 void
1545 MidiRegionView::extend_active_notes()
1546 {
1547         if (!_active_notes) {
1548                 return;
1549         }
1550
1551         for (unsigned i=0; i < 128; ++i) {
1552                 if (_active_notes[i]) {
1553                         _active_notes[i]->property_x2() = trackview.editor().frame_to_pixel(_region->length());
1554                 }
1555         }
1556 }
1557
1558
1559 void
1560 MidiRegionView::play_midi_note(boost::shared_ptr<NoteType> note)
1561 {
1562         if (_no_sound_notes || !Config->get_sound_midi_notes()) {
1563                 return;
1564         }
1565
1566         RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
1567
1568         if (!route_ui || !route_ui->midi_track()) {
1569                 return;
1570         }
1571
1572         NotePlayer* np = new NotePlayer (route_ui->midi_track ());
1573         np->add (note);
1574         np->play ();
1575
1576         /* NotePlayer deletes itself */
1577 }
1578
1579 void
1580 MidiRegionView::start_playing_midi_note(boost::shared_ptr<NoteType> note)
1581 {
1582         if (_no_sound_notes || !Config->get_sound_midi_notes()) {
1583                 return;
1584         }
1585
1586         RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
1587
1588         if (!route_ui || !route_ui->midi_track()) {
1589                 return;
1590         }
1591
1592         delete _note_player;
1593         _note_player = new NotePlayer (route_ui->midi_track ());
1594         _note_player->add (note);
1595         _note_player->on ();
1596 }
1597
1598 void
1599 MidiRegionView::start_playing_midi_chord (vector<boost::shared_ptr<NoteType> > notes)
1600 {
1601         if (_no_sound_notes || !Config->get_sound_midi_notes()) {
1602                 return;
1603         }
1604
1605         RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
1606
1607         if (!route_ui || !route_ui->midi_track()) {
1608                 return;
1609         }
1610
1611         delete _note_player;
1612         _note_player = new NotePlayer (route_ui->midi_track());
1613
1614         for (vector<boost::shared_ptr<NoteType> >::iterator n = notes.begin(); n != notes.end(); ++n) {
1615                 _note_player->add (*n);
1616         }
1617
1618         _note_player->on ();
1619 }
1620
1621
1622 bool
1623 MidiRegionView::note_in_region_range (const boost::shared_ptr<NoteType> note, bool& visible) const
1624 {
1625         const framepos_t note_start_frames = source_beats_to_region_frames (note->time());
1626         bool outside = (note_start_frames  < 0) || (note_start_frames > _region->last_frame());
1627
1628         visible = (note->note() >= midi_stream_view()->lowest_note()) &&
1629                 (note->note() <= midi_stream_view()->highest_note());
1630
1631         return !outside;
1632 }
1633
1634 /** Update a canvas note's size from its model note.
1635  *  @param ev Canvas note to update.
1636  *  @param update_ghost_regions true to update the note in any ghost regions that we have, otherwise false.
1637  */
1638 void
1639 MidiRegionView::update_note (CanvasNote* ev, bool update_ghost_regions)
1640 {
1641         boost::shared_ptr<NoteType> note = ev->note();
1642         const double x = trackview.editor().frame_to_pixel (source_beats_to_region_frames (note->time()));
1643         const double y1 = midi_stream_view()->note_to_y(note->note());
1644
1645         ev->property_x1() = x;
1646         ev->property_y1() = y1;
1647
1648         /* trim note display to not overlap the end of its region */
1649
1650         if (note->length() > 0) {
1651                 const framepos_t note_end_frames = min (source_beats_to_region_frames (note->end_time()), _region->length());
1652                 ev->property_x2() = trackview.editor().frame_to_pixel (note_end_frames);
1653         } else {
1654                 ev->property_x2() = trackview.editor().frame_to_pixel (_region->length());
1655         }
1656
1657         ev->property_y2() = y1 + floor(midi_stream_view()->note_height());
1658
1659         if (note->length() == 0) {
1660                 if (_active_notes) {
1661                         assert(note->note() < 128);
1662                         // If this note is already active there's a stuck note,
1663                         // finish the old note rectangle
1664                         if (_active_notes[note->note()]) {
1665                                 CanvasNote* const old_rect = _active_notes[note->note()];
1666                                 boost::shared_ptr<NoteType> old_note = old_rect->note();
1667                                 old_rect->property_x2() = x;
1668                                 old_rect->property_outline_what() = (guint32) 0xF;
1669                         }
1670                         _active_notes[note->note()] = ev;
1671                 }
1672                 /* outline all but right edge */
1673                 ev->property_outline_what() = (guint32) (0x1 & 0x4 & 0x8);
1674         } else {
1675                 /* outline all edges */
1676                 ev->property_outline_what() = (guint32) 0xF;
1677         }
1678
1679         if (update_ghost_regions) {
1680                 for (std::vector<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
1681                         MidiGhostRegion* gr = dynamic_cast<MidiGhostRegion*> (*i);
1682                         if (gr) {
1683                                 gr->update_note (ev);
1684                         }
1685                 }
1686         }
1687 }
1688
1689 double
1690 MidiRegionView::update_hit (CanvasHit* ev)
1691 {
1692         boost::shared_ptr<NoteType> note = ev->note();
1693
1694         const framepos_t note_start_frames = source_beats_to_region_frames(note->time());
1695         const double x = trackview.editor().frame_to_pixel(note_start_frames);
1696         const double diamond_size = midi_stream_view()->note_height() / 2.0;
1697         const double y = midi_stream_view()->note_to_y(note->note()) + ((diamond_size-2) / 4.0);
1698
1699         ev->move_to (x, y);
1700
1701         return diamond_size;
1702 }
1703
1704 /** Add a MIDI note to the view (with length).
1705  *
1706  * If in sustained mode, notes with length 0 will be considered active
1707  * notes, and resolve_note should be called when the corresponding note off
1708  * event arrives, to properly display the note.
1709  */
1710 void
1711 MidiRegionView::add_note(const boost::shared_ptr<NoteType> note, bool visible)
1712 {
1713         CanvasNoteEvent* event = 0;
1714
1715         assert(note->time() >= 0);
1716         assert(midi_view()->note_mode() == Sustained || midi_view()->note_mode() == Percussive);
1717
1718         //ArdourCanvas::Group* const group = (ArdourCanvas::Group*) get_canvas_group();
1719
1720         if (midi_view()->note_mode() == Sustained) {
1721
1722                 CanvasNote* ev_rect = new CanvasNote(*this, *_note_group, note);
1723
1724                 update_note (ev_rect);
1725
1726                 event = ev_rect;
1727
1728                 MidiGhostRegion* gr;
1729
1730                 for (std::vector<GhostRegion*>::iterator g = ghosts.begin(); g != ghosts.end(); ++g) {
1731                         if ((gr = dynamic_cast<MidiGhostRegion*>(*g)) != 0) {
1732                                 gr->add_note(ev_rect);
1733                         }
1734                 }
1735
1736         } else if (midi_view()->note_mode() == Percussive) {
1737
1738                 const double diamond_size = midi_stream_view()->note_height() / 2.0;
1739
1740                 CanvasHit* ev_diamond = new CanvasHit (*this, *_note_group, diamond_size, note);
1741
1742                 update_hit (ev_diamond);
1743
1744                 event = ev_diamond;
1745
1746         } else {
1747                 event = 0;
1748         }
1749
1750         if (event) {
1751                 if (_marked_for_selection.find(note) != _marked_for_selection.end()) {
1752                         note_selected(event, true);
1753                 }
1754
1755                 if (_marked_for_velocity.find(note) != _marked_for_velocity.end()) {
1756                         event->show_velocity();
1757                 }
1758
1759                 event->on_channel_selection_change(_last_channel_selection);
1760                 _events.push_back(event);
1761
1762                 if (visible) {
1763                         event->show();
1764                 } else {
1765                         event->hide ();
1766                 }
1767         }
1768
1769         MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1770         MidiStreamView* const view = mtv->midi_view();
1771
1772         view->update_note_range (note->note());
1773 }
1774
1775 void
1776 MidiRegionView::step_add_note (uint8_t channel, uint8_t number, uint8_t velocity,
1777                                Evoral::MusicalTime pos, Evoral::MusicalTime len)
1778 {
1779         boost::shared_ptr<NoteType> new_note (new NoteType (channel, pos, len, number, velocity));
1780
1781         /* potentially extend region to hold new note */
1782
1783         framepos_t end_frame = source_beats_to_absolute_frames (new_note->end_time());
1784         framepos_t region_end = _region->last_frame();
1785
1786         if (end_frame > region_end) {
1787                 _region->set_length (end_frame - _region->position());
1788         }
1789
1790         MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1791         MidiStreamView* const view = mtv->midi_view();
1792
1793         view->update_note_range(new_note->note());
1794
1795         _marked_for_selection.clear ();
1796         clear_selection ();
1797
1798         start_note_diff_command (_("step add"));
1799         note_diff_add_note (new_note, true, false);
1800         apply_diff();
1801
1802         // last_step_edit_note = new_note;
1803 }
1804
1805 void
1806 MidiRegionView::step_sustain (Evoral::MusicalTime beats)
1807 {
1808         change_note_lengths (false, false, beats, false, true);
1809 }
1810
1811 /** Add a new patch change flag to the canvas.
1812  * @param patch the patch change to add
1813  * @param the text to display in the flag
1814  * @param active_channel true to display the flag as on an active channel, false to grey it out for an inactive channel.
1815  */
1816 void
1817 MidiRegionView::add_canvas_patch_change (MidiModel::PatchChangePtr patch, const string& displaytext, bool active_channel)
1818 {
1819         assert (patch->time() >= 0);
1820
1821         framecnt_t region_frames = source_beats_to_region_frames (patch->time());
1822         const double x = trackview.editor().frame_to_pixel (region_frames);
1823
1824         double const height = midi_stream_view()->contents_height();
1825
1826         boost::shared_ptr<CanvasPatchChange> patch_change = boost::shared_ptr<CanvasPatchChange>(
1827                 new CanvasPatchChange(*this, *group,
1828                                       displaytext,
1829                                       height,
1830                                       x, 1.0,
1831                                       instrument_info(),
1832                                       patch,
1833                                       active_channel)
1834                           );
1835
1836         if (patch_change->width() < _pixel_width) {
1837                 // Show unless patch change is beyond the region bounds
1838                 if (region_frames < 0 || region_frames >= _region->length()) {
1839                         patch_change->hide();
1840                 } else {
1841                         patch_change->show();
1842                 }
1843         } else {
1844                 patch_change->hide ();
1845         }
1846
1847         _patch_changes.push_back (patch_change);
1848 }
1849
1850 MIDI::Name::PatchPrimaryKey
1851 MidiRegionView::patch_change_to_patch_key (MidiModel::PatchChangePtr p)
1852 {
1853         return MIDI::Name::PatchPrimaryKey (p->program(), p->bank());
1854 }
1855
1856 void 
1857 MidiRegionView::get_patch_key_at (double time, uint8_t channel, MIDI::Name::PatchPrimaryKey& key)
1858 {
1859         MidiModel::PatchChanges::iterator i = _model->patch_change_lower_bound (time);
1860         while (i != _model->patch_changes().end() && (*i)->channel() != channel) {
1861                 ++i;
1862         }
1863
1864         if (i != _model->patch_changes().end()) {
1865                 key.bank_number = (*i)->bank();
1866                 key.program_number = (*i)->program ();
1867         } else {
1868                 key.bank_number = key.program_number = 0;
1869         }
1870
1871         assert (key.is_sane());
1872 }
1873
1874 void
1875 MidiRegionView::change_patch_change (CanvasPatchChange& pc, const MIDI::Name::PatchPrimaryKey& new_patch)
1876 {
1877         MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("alter patch change"));
1878
1879         if (pc.patch()->program() != new_patch.program_number) {
1880                 c->change_program (pc.patch (), new_patch.program_number);
1881         }
1882
1883         int const new_bank = new_patch.bank_number;
1884         if (pc.patch()->bank() != new_bank) {
1885                 c->change_bank (pc.patch (), new_bank);
1886         }
1887
1888         _model->apply_command (*trackview.session(), c);
1889
1890         _patch_changes.clear ();
1891         display_patch_changes ();
1892 }
1893
1894 void
1895 MidiRegionView::change_patch_change (MidiModel::PatchChangePtr old_change, const Evoral::PatchChange<Evoral::MusicalTime> & new_change)
1896 {
1897         MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("alter patch change"));
1898
1899         if (old_change->time() != new_change.time()) {
1900                 c->change_time (old_change, new_change.time());
1901         }
1902
1903         if (old_change->channel() != new_change.channel()) {
1904                 c->change_channel (old_change, new_change.channel());
1905         }
1906
1907         if (old_change->program() != new_change.program()) {
1908                 c->change_program (old_change, new_change.program());
1909         }
1910
1911         if (old_change->bank() != new_change.bank()) {
1912                 c->change_bank (old_change, new_change.bank());
1913         }
1914
1915         _model->apply_command (*trackview.session(), c);
1916
1917         _patch_changes.clear ();
1918         display_patch_changes ();
1919 }
1920
1921 /** Add a patch change to the region.
1922  *  @param t Time in frames relative to region position
1923  *  @param patch Patch to add; time and channel are ignored (time is converted from t, and channel comes from
1924  *  MidiTimeAxisView::get_channel_for_add())
1925  */
1926 void
1927 MidiRegionView::add_patch_change (framecnt_t t, Evoral::PatchChange<Evoral::MusicalTime> const & patch)
1928 {
1929         MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1930
1931         MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("add patch change"));
1932         c->add (MidiModel::PatchChangePtr (
1933                         new Evoral::PatchChange<Evoral::MusicalTime> (
1934                                 absolute_frames_to_source_beats (_region->position() + t),
1935                                 mtv->get_channel_for_add(), patch.program(), patch.bank()
1936                                 )
1937                         )
1938                 );
1939
1940         _model->apply_command (*trackview.session(), c);
1941
1942         _patch_changes.clear ();
1943         display_patch_changes ();
1944 }
1945
1946 void
1947 MidiRegionView::move_patch_change (CanvasPatchChange& pc, Evoral::MusicalTime t)
1948 {
1949         MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("move patch change"));
1950         c->change_time (pc.patch (), t);
1951         _model->apply_command (*trackview.session(), c);
1952
1953         _patch_changes.clear ();
1954         display_patch_changes ();
1955 }
1956
1957 void
1958 MidiRegionView::delete_patch_change (CanvasPatchChange* pc)
1959 {
1960         MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("delete patch change"));
1961         c->remove (pc->patch ());
1962         _model->apply_command (*trackview.session(), c);
1963
1964         _patch_changes.clear ();
1965         display_patch_changes ();
1966 }
1967
1968 void
1969 MidiRegionView::previous_patch (CanvasPatchChange& patch)
1970 {
1971         if (patch.patch()->program() < 127) {
1972                 MIDI::Name::PatchPrimaryKey key = patch_change_to_patch_key (patch.patch());
1973                 key.program_number++;
1974                 change_patch_change (patch, key);
1975         }
1976 }
1977
1978 void
1979 MidiRegionView::next_patch (CanvasPatchChange& patch)
1980 {
1981         if (patch.patch()->program() > 0) {
1982                 MIDI::Name::PatchPrimaryKey key = patch_change_to_patch_key (patch.patch());
1983                 key.program_number--;
1984                 change_patch_change (patch, key);
1985         }
1986 }
1987
1988 void
1989 MidiRegionView::next_bank (CanvasPatchChange& patch)
1990 {
1991         if (patch.patch()->program() < 127) {
1992                 MIDI::Name::PatchPrimaryKey key = patch_change_to_patch_key (patch.patch());
1993                 if (key.bank_number > 0) {
1994                         key.bank_number--;
1995                         change_patch_change (patch, key);
1996                 }
1997         }
1998 }
1999
2000 void
2001 MidiRegionView::previous_bank (CanvasPatchChange& patch)
2002 {
2003         if (patch.patch()->program() > 0) {
2004                 MIDI::Name::PatchPrimaryKey key = patch_change_to_patch_key (patch.patch());
2005                 if (key.bank_number < 127) {
2006                         key.bank_number++;
2007                         change_patch_change (patch, key);
2008                 }
2009         }
2010 }
2011
2012 void
2013 MidiRegionView::maybe_remove_deleted_note_from_selection (CanvasNoteEvent* cne)
2014 {
2015         if (_selection.empty()) {
2016                 return;
2017         }
2018
2019         _selection.erase (cne);
2020 }
2021
2022 void
2023 MidiRegionView::delete_selection()
2024 {
2025         if (_selection.empty()) {
2026                 return;
2027         }
2028
2029         start_note_diff_command (_("delete selection"));
2030
2031         for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2032                 if ((*i)->selected()) {
2033                         _note_diff_command->remove((*i)->note());
2034                 }
2035         }
2036
2037         _selection.clear();
2038
2039         apply_diff ();
2040 }
2041
2042 void
2043 MidiRegionView::delete_note (boost::shared_ptr<NoteType> n)
2044 {
2045         start_note_diff_command (_("delete note"));
2046         _note_diff_command->remove (n);
2047         apply_diff ();
2048
2049         trackview.editor().verbose_cursor()->hide ();
2050 }
2051
2052 void
2053 MidiRegionView::clear_selection_except (ArdourCanvas::CanvasNoteEvent* ev, bool signal)
2054 {
2055         for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
2056                 if ((*i) != ev) {
2057                         Selection::iterator tmp = i;
2058                         ++tmp;
2059
2060                         (*i)->set_selected (false);
2061                         (*i)->hide_velocity ();
2062                         _selection.erase (i);
2063
2064                         i = tmp;
2065                 } else {
2066                         ++i;
2067                 }
2068         }
2069
2070         /* this does not change the status of this regionview w.r.t the editor
2071            selection.
2072         */
2073
2074         if (signal) {
2075                 SelectionCleared (this); /* EMIT SIGNAL */
2076         }
2077 }
2078
2079 void
2080 MidiRegionView::unique_select(ArdourCanvas::CanvasNoteEvent* ev)
2081 {
2082         clear_selection_except (ev);
2083
2084         /* don't bother with checking to see if we should remove this
2085            regionview from the editor selection, since we're about to add
2086            another note, and thus put/keep this regionview in the editor
2087            selection anyway.
2088         */
2089
2090         if (!ev->selected()) {
2091                 add_to_selection (ev);
2092         }
2093 }
2094
2095 void
2096 MidiRegionView::select_all_notes ()
2097 {
2098         clear_selection ();
2099
2100         for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2101                 add_to_selection (*i);
2102         }
2103 }
2104
2105 void
2106 MidiRegionView::select_range (framepos_t start, framepos_t end)
2107 {
2108         clear_selection ();
2109
2110         for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2111                 framepos_t t = source_beats_to_absolute_frames((*i)->note()->time());
2112                 if (t >= start && t <= end) {
2113                         add_to_selection (*i);
2114                 }
2115         }
2116 }
2117
2118 void
2119 MidiRegionView::invert_selection ()
2120 {
2121         for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2122                 if ((*i)->selected()) {
2123                         remove_from_selection(*i);
2124                 } else {
2125                         add_to_selection (*i);
2126                 }
2127         }
2128 }
2129
2130 void
2131 MidiRegionView::select_matching_notes (uint8_t notenum, uint16_t channel_mask, bool add, bool extend)
2132 {
2133         uint8_t low_note = 127;
2134         uint8_t high_note = 0;
2135         MidiModel::Notes& notes (_model->notes());
2136         _optimization_iterator = _events.begin();
2137
2138         if (!add) {
2139                 clear_selection ();
2140         }
2141
2142         if (extend && _selection.empty()) {
2143                 extend = false;
2144         }
2145
2146         if (extend) {
2147
2148                 /* scan existing selection to get note range */
2149
2150                 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2151                         if ((*i)->note()->note() < low_note) {
2152                                 low_note = (*i)->note()->note();
2153                         }
2154                         if ((*i)->note()->note() > high_note) {
2155                                 high_note = (*i)->note()->note();
2156                         }
2157                 }
2158
2159                 low_note = min (low_note, notenum);
2160                 high_note = max (high_note, notenum);
2161         }
2162
2163         _no_sound_notes = true;
2164
2165         for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
2166
2167                 boost::shared_ptr<NoteType> note (*n);
2168                 CanvasNoteEvent* cne;
2169                 bool select = false;
2170
2171                 if (((1 << note->channel()) & channel_mask) != 0) {
2172                         if (extend) {
2173                                 if ((note->note() >= low_note && note->note() <= high_note)) {
2174                                         select = true;
2175                                 }
2176                         } else if (note->note() == notenum) {
2177                                 select = true;
2178                         }
2179                 }
2180
2181                 if (select) {
2182                         if ((cne = find_canvas_note (note)) != 0) {
2183                                 // extend is false because we've taken care of it,
2184                                 // since it extends by time range, not pitch.
2185                                 note_selected (cne, add, false);
2186                         }
2187                 }
2188
2189                 add = true; // we need to add all remaining matching notes, even if the passed in value was false (for "set")
2190
2191         }
2192
2193         _no_sound_notes = false;
2194 }
2195
2196 void
2197 MidiRegionView::toggle_matching_notes (uint8_t notenum, uint16_t channel_mask)
2198 {
2199         MidiModel::Notes& notes (_model->notes());
2200         _optimization_iterator = _events.begin();
2201
2202         for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
2203
2204                 boost::shared_ptr<NoteType> note (*n);
2205                 CanvasNoteEvent* cne;
2206
2207                 if (note->note() == notenum && (((0x0001 << note->channel()) & channel_mask) != 0)) {
2208                         if ((cne = find_canvas_note (note)) != 0) {
2209                                 if (cne->selected()) {
2210                                         note_deselected (cne);
2211                                 } else {
2212                                         note_selected (cne, true, false);
2213                                 }
2214                         }
2215                 }
2216         }
2217 }
2218
2219 void
2220 MidiRegionView::note_selected (ArdourCanvas::CanvasNoteEvent* ev, bool add, bool extend)
2221 {
2222         if (!add) {
2223                 clear_selection_except (ev);
2224                 if (!_selection.empty()) {
2225                         PublicEditor& editor (trackview.editor());
2226                         editor.get_selection().add (this);
2227                 }
2228         }
2229
2230         if (!extend) {
2231
2232                 if (!ev->selected()) {
2233                         add_to_selection (ev);
2234                 }
2235
2236         } else {
2237                 /* find end of latest note selected, select all between that and the start of "ev" */
2238
2239                 Evoral::MusicalTime earliest = Evoral::MaxMusicalTime;
2240                 Evoral::MusicalTime latest = 0;
2241
2242                 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2243                         if ((*i)->note()->end_time() > latest) {
2244                                 latest = (*i)->note()->end_time();
2245                         }
2246                         if ((*i)->note()->time() < earliest) {
2247                                 earliest = (*i)->note()->time();
2248                         }
2249                 }
2250
2251                 if (ev->note()->end_time() > latest) {
2252                         latest = ev->note()->end_time();
2253                 }
2254
2255                 if (ev->note()->time() < earliest) {
2256                         earliest = ev->note()->time();
2257                 }
2258
2259                 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2260
2261                         /* find notes entirely within OR spanning the earliest..latest range */
2262
2263                         if (((*i)->note()->time() >= earliest && (*i)->note()->end_time() <= latest) ||
2264                             ((*i)->note()->time() <= earliest && (*i)->note()->end_time() >= latest)) {
2265                                 add_to_selection (*i);
2266                         }
2267
2268                 }
2269         }
2270 }
2271
2272 void
2273 MidiRegionView::note_deselected(ArdourCanvas::CanvasNoteEvent* ev)
2274 {
2275         remove_from_selection (ev);
2276 }
2277
2278 void
2279 MidiRegionView::update_drag_selection(double x1, double x2, double y1, double y2, bool extend)
2280 {
2281         if (x1 > x2) {
2282                 swap (x1, x2);
2283         }
2284
2285         if (y1 > y2) {
2286                 swap (y1, y2);
2287         }
2288
2289         // TODO: Make this faster by storing the last updated selection rect, and only
2290         // adjusting things that are in the area that appears/disappeared.
2291         // We probably need a tree to be able to find events in O(log(n)) time.
2292
2293         for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2294
2295                 /* check if any corner of the note is inside the rect
2296
2297                    Notes:
2298                    1) this is computing "touched by", not "contained by" the rect.
2299                    2) this does not require that events be sorted in time.
2300                 */
2301
2302                 const double ix1 = (*i)->x1();
2303                 const double ix2 = (*i)->x2();
2304                 const double iy1 = (*i)->y1();
2305                 const double iy2 = (*i)->y2();
2306
2307                 if ((ix1 >= x1 && ix1 <= x2 && iy1 >= y1 && iy1 <= y2) ||
2308                     (ix1 >= x1 && ix1 <= x2 && iy2 >= y1 && iy2 <= y2) ||
2309                     (ix2 >= x1 && ix2 <= x2 && iy1 >= y1 && iy1 <= y2) ||
2310                     (ix2 >= x1 && ix2 <= x2 && iy2 >= y1 && iy2 <= y2)) {
2311
2312                         // Inside rectangle
2313                         if (!(*i)->selected()) {
2314                                 add_to_selection (*i);
2315                         }
2316                 } else if ((*i)->selected() && !extend) {
2317                         // Not inside rectangle
2318                         remove_from_selection (*i);
2319                 }
2320         }
2321 }
2322
2323 void
2324 MidiRegionView::update_vertical_drag_selection (double y1, double y2, bool extend)
2325 {
2326         if (y1 > y2) {
2327                 swap (y1, y2);
2328         }
2329
2330         // TODO: Make this faster by storing the last updated selection rect, and only
2331         // adjusting things that are in the area that appears/disappeared.
2332         // We probably need a tree to be able to find events in O(log(n)) time.
2333
2334         for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2335
2336                 /* check if any corner of the note is inside the rect
2337
2338                    Notes:
2339                    1) this is computing "touched by", not "contained by" the rect.
2340                    2) this does not require that events be sorted in time.
2341                 */
2342
2343                 if (((*i)->y1() >= y1 && (*i)->y1() <= y2)) {
2344                         // within y- (note-) range
2345                         if (!(*i)->selected()) {
2346                                 add_to_selection (*i);
2347                         }
2348                 } else if ((*i)->selected() && !extend) {
2349                         // Not inside rectangle
2350                         remove_from_selection (*i);
2351                 }
2352         }
2353 }
2354
2355 void
2356 MidiRegionView::remove_from_selection (CanvasNoteEvent* ev)
2357 {
2358         Selection::iterator i = _selection.find (ev);
2359
2360         if (i != _selection.end()) {
2361                 _selection.erase (i);
2362         }
2363
2364         ev->set_selected (false);
2365         ev->hide_velocity ();
2366
2367         if (_selection.empty()) {
2368                 PublicEditor& editor (trackview.editor());
2369                 editor.get_selection().remove (this);
2370         }
2371 }
2372
2373 void
2374 MidiRegionView::add_to_selection (CanvasNoteEvent* ev)
2375 {
2376         bool add_mrv_selection = false;
2377
2378         if (_selection.empty()) {
2379                 add_mrv_selection = true;
2380         }
2381
2382         if (_selection.insert (ev).second) {
2383                 ev->set_selected (true);
2384                 start_playing_midi_note ((ev)->note());
2385         }
2386
2387         if (add_mrv_selection) {
2388                 PublicEditor& editor (trackview.editor());
2389                 editor.get_selection().add (this);
2390         }
2391 }
2392
2393 void
2394 MidiRegionView::move_selection(double dx, double dy, double cumulative_dy)
2395 {
2396         typedef vector<boost::shared_ptr<NoteType> > PossibleChord;
2397         PossibleChord to_play;
2398         Evoral::MusicalTime earliest = Evoral::MaxMusicalTime;
2399
2400         for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2401                 if ((*i)->note()->time() < earliest) {
2402                         earliest = (*i)->note()->time();
2403                 }
2404         }
2405
2406         for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2407                 if (Evoral::musical_time_equal ((*i)->note()->time(), earliest)) {
2408                         to_play.push_back ((*i)->note());
2409                 }
2410                 (*i)->move_event(dx, dy);
2411         }
2412
2413         if (dy && !_selection.empty() && !_no_sound_notes && Config->get_sound_midi_notes()) {
2414
2415                 if (to_play.size() > 1) {
2416
2417                         PossibleChord shifted;
2418
2419                         for (PossibleChord::iterator n = to_play.begin(); n != to_play.end(); ++n) {
2420                                 boost::shared_ptr<NoteType> moved_note (new NoteType (**n));
2421                                 moved_note->set_note (moved_note->note() + cumulative_dy);
2422                                 shifted.push_back (moved_note);
2423                         }
2424
2425                         start_playing_midi_chord (shifted);
2426
2427                 } else if (!to_play.empty()) {
2428
2429                         boost::shared_ptr<NoteType> moved_note (new NoteType (*to_play.front()));
2430                         moved_note->set_note (moved_note->note() + cumulative_dy);
2431                         start_playing_midi_note (moved_note);
2432                 }
2433         }
2434 }
2435
2436 void
2437 MidiRegionView::note_dropped(CanvasNoteEvent *, frameoffset_t dt, int8_t dnote)
2438 {
2439         assert (!_selection.empty());
2440
2441         uint8_t lowest_note_in_selection  = 127;
2442         uint8_t highest_note_in_selection = 0;
2443         uint8_t highest_note_difference = 0;
2444
2445         // find highest and lowest notes first
2446
2447         for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2448                 uint8_t pitch = (*i)->note()->note();
2449                 lowest_note_in_selection  = std::min(lowest_note_in_selection,  pitch);
2450                 highest_note_in_selection = std::max(highest_note_in_selection, pitch);
2451         }
2452
2453         /*
2454           cerr << "dnote: " << (int) dnote << endl;
2455           cerr << "lowest note (streamview): " << int(midi_stream_view()->lowest_note())
2456           << " highest note (streamview): " << int(midi_stream_view()->highest_note()) << endl;
2457           cerr << "lowest note (selection): " << int(lowest_note_in_selection) << " highest note(selection): "
2458           << int(highest_note_in_selection) << endl;
2459           cerr << "selection size: " << _selection.size() << endl;
2460           cerr << "Highest note in selection: " << (int) highest_note_in_selection << endl;
2461         */
2462
2463         // Make sure the note pitch does not exceed the MIDI standard range
2464         if (highest_note_in_selection + dnote > 127) {
2465                 highest_note_difference = highest_note_in_selection - 127;
2466         }
2467
2468         start_note_diff_command (_("move notes"));
2469
2470         for (Selection::iterator i = _selection.begin(); i != _selection.end() ; ++i) {
2471                 
2472                 framepos_t new_frames = source_beats_to_absolute_frames ((*i)->note()->time()) + dt;
2473                 Evoral::MusicalTime new_time = absolute_frames_to_source_beats (new_frames);
2474
2475                 if (new_time < 0) {
2476                         continue;
2477                 }
2478
2479                 note_diff_add_change (*i, MidiModel::NoteDiffCommand::StartTime, new_time);
2480
2481                 uint8_t original_pitch = (*i)->note()->note();
2482                 uint8_t new_pitch      = original_pitch + dnote - highest_note_difference;
2483
2484                 // keep notes in standard midi range
2485                 clamp_to_0_127(new_pitch);
2486
2487                 lowest_note_in_selection  = std::min(lowest_note_in_selection,  new_pitch);
2488                 highest_note_in_selection = std::max(highest_note_in_selection, new_pitch);
2489
2490                 note_diff_add_change (*i, MidiModel::NoteDiffCommand::NoteNumber, new_pitch);
2491         }
2492
2493         apply_diff();
2494
2495         // care about notes being moved beyond the upper/lower bounds on the canvas
2496         if (lowest_note_in_selection  < midi_stream_view()->lowest_note() ||
2497             highest_note_in_selection > midi_stream_view()->highest_note()) {
2498                 midi_stream_view()->set_note_range(MidiStreamView::ContentsRange);
2499         }
2500 }
2501
2502 /** @param x Pixel relative to the region position.
2503  *  @return Snapped frame relative to the region position.
2504  */
2505 framepos_t
2506 MidiRegionView::snap_pixel_to_frame(double x)
2507 {
2508         PublicEditor& editor (trackview.editor());
2509         return snap_frame_to_frame (editor.pixel_to_frame (x));
2510 }
2511
2512 /** @param x Pixel relative to the region position.
2513  *  @return Snapped pixel relative to the region position.
2514  */
2515 double
2516 MidiRegionView::snap_to_pixel(double x)
2517 {
2518         return (double) trackview.editor().frame_to_pixel(snap_pixel_to_frame(x));
2519 }
2520
2521 double
2522 MidiRegionView::get_position_pixels()
2523 {
2524         framepos_t region_frame = get_position();
2525         return trackview.editor().frame_to_pixel(region_frame);
2526 }
2527
2528 double
2529 MidiRegionView::get_end_position_pixels()
2530 {
2531         framepos_t frame = get_position() + get_duration ();
2532         return trackview.editor().frame_to_pixel(frame);
2533 }
2534
2535 framepos_t
2536 MidiRegionView::source_beats_to_absolute_frames(double beats) const
2537 {
2538         /* the time converter will return the frame corresponding to `beats'
2539            relative to the start of the source. The start of the source
2540            is an implied position given by region->position - region->start
2541         */
2542         const framepos_t source_start = _region->position() - _region->start();
2543         return  source_start +  _source_relative_time_converter.to (beats);
2544 }
2545
2546 double
2547 MidiRegionView::absolute_frames_to_source_beats(framepos_t frames) const
2548 {
2549         /* the `frames' argument needs to be converted into a frame count
2550            relative to the start of the source before being passed in to the
2551            converter.
2552         */
2553         const framepos_t source_start = _region->position() - _region->start();
2554         return  _source_relative_time_converter.from (frames - source_start);
2555 }
2556
2557 framepos_t
2558 MidiRegionView::region_beats_to_region_frames(double beats) const
2559 {
2560         return _region_relative_time_converter.to(beats);
2561 }
2562
2563 double
2564 MidiRegionView::region_frames_to_region_beats(framepos_t frames) const
2565 {
2566         return _region_relative_time_converter.from(frames);
2567 }
2568
2569 void
2570 MidiRegionView::begin_resizing (bool /*at_front*/)
2571 {
2572         _resize_data.clear();
2573
2574         for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2575                 CanvasNote *note = dynamic_cast<CanvasNote *> (*i);
2576
2577                 // only insert CanvasNotes into the map
2578                 if (note) {
2579                         NoteResizeData *resize_data = new NoteResizeData();
2580                         resize_data->canvas_note = note;
2581
2582                         // create a new SimpleRect from the note which will be the resize preview
2583                         SimpleRect *resize_rect = new SimpleRect(
2584                                 *_note_group, note->x1(), note->y1(), note->x2(), note->y2());
2585
2586                         // calculate the colors: get the color settings
2587                         uint32_t fill_color = UINT_RGBA_CHANGE_A(
2588                                 ARDOUR_UI::config()->canvasvar_MidiNoteSelected.get(),
2589                                 128);
2590
2591                         // make the resize preview notes more transparent and bright
2592                         fill_color = UINT_INTERPOLATE(fill_color, 0xFFFFFF40, 0.5);
2593
2594                         // calculate color based on note velocity
2595                         resize_rect->property_fill_color_rgba() = UINT_INTERPOLATE(
2596                                 CanvasNoteEvent::meter_style_fill_color(note->note()->velocity(), note->selected()),
2597                                 fill_color,
2598                                 0.85);
2599
2600                         resize_rect->property_outline_color_rgba() = CanvasNoteEvent::calculate_outline(
2601                                 ARDOUR_UI::config()->canvasvar_MidiNoteSelected.get());
2602
2603                         resize_data->resize_rect = resize_rect;
2604                         _resize_data.push_back(resize_data);
2605                 }
2606         }
2607 }
2608
2609 /** Update resizing notes while user drags.
2610  * @param primary `primary' note for the drag; ie the one that is used as the reference in non-relative mode.
2611  * @param at_front which end of the note (true == note on, false == note off)
2612  * @param delta_x change in mouse position since the start of the drag
2613  * @param relative true if relative resizing is taking place, false if absolute resizing.  This only makes
2614  * a difference when multiple notes are being resized; in relative mode, each note's length is changed by the
2615  * amount of the drag.  In non-relative mode, all selected notes are set to have the same start or end point
2616  * as the \a primary note.
2617  */
2618 void
2619 MidiRegionView::update_resizing (ArdourCanvas::CanvasNoteEvent* primary, bool at_front, double delta_x, bool relative)
2620 {
2621         bool cursor_set = false;
2622
2623         for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
2624                 SimpleRect* resize_rect = (*i)->resize_rect;
2625                 CanvasNote* canvas_note = (*i)->canvas_note;
2626                 double current_x;
2627
2628                 if (at_front) {
2629                         if (relative) {
2630                                 current_x = canvas_note->x1() + delta_x;
2631                         } else {
2632                                 current_x = primary->x1() + delta_x;
2633                         }
2634                 } else {
2635                         if (relative) {
2636                                 current_x = canvas_note->x2() + delta_x;
2637                         } else {
2638                                 current_x = primary->x2() + delta_x;
2639                         }
2640                 }
2641
2642                 if (at_front) {
2643                         resize_rect->property_x1() = snap_to_pixel(current_x);
2644                         resize_rect->property_x2() = canvas_note->x2();
2645                 } else {
2646                         resize_rect->property_x2() = snap_to_pixel(current_x);
2647                         resize_rect->property_x1() = canvas_note->x1();
2648                 }
2649
2650                 if (!cursor_set) {
2651                         double beats;
2652
2653                         beats = snap_pixel_to_frame (current_x);
2654                         beats = region_frames_to_region_beats (beats);
2655
2656                         double len;
2657
2658                         if (at_front) {
2659                                 if (beats < canvas_note->note()->end_time()) {
2660                                         len = canvas_note->note()->time() - beats;
2661                                         len += canvas_note->note()->length();
2662                                 } else {
2663                                         len = 0;
2664                                 }
2665                         } else {
2666                                 if (beats >= canvas_note->note()->time()) {
2667                                         len = beats - canvas_note->note()->time();
2668                                 } else {
2669                                         len = 0;
2670                                 }
2671                         }
2672
2673                         char buf[16];
2674                         snprintf (buf, sizeof (buf), "%.3g beats", len);
2675                         show_verbose_cursor (buf, 0, 0);
2676
2677                         cursor_set = true;
2678                 }
2679
2680         }
2681 }
2682
2683
2684 /** Finish resizing notes when the user releases the mouse button.
2685  *  Parameters the same as for \a update_resizing().
2686  */
2687 void
2688 MidiRegionView::commit_resizing (ArdourCanvas::CanvasNoteEvent* primary, bool at_front, double delta_x, bool relative)
2689 {
2690         start_note_diff_command (_("resize notes"));
2691
2692         for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
2693                 CanvasNote*  canvas_note = (*i)->canvas_note;
2694                 SimpleRect*  resize_rect = (*i)->resize_rect;
2695
2696                 /* Get the new x position for this resize, which is in pixels relative
2697                  * to the region position.
2698                  */
2699                 
2700                 double current_x;
2701
2702                 if (at_front) {
2703                         if (relative) {
2704                                 current_x = canvas_note->x1() + delta_x;
2705                         } else {
2706                                 current_x = primary->x1() + delta_x;
2707                         }
2708                 } else {
2709                         if (relative) {
2710                                 current_x = canvas_note->x2() + delta_x;
2711                         } else {
2712                                 current_x = primary->x2() + delta_x;
2713                         }
2714                 }
2715
2716                 /* Convert that to a frame within the source */
2717                 current_x = snap_pixel_to_frame (current_x) + _region->start ();
2718
2719                 /* and then to beats */
2720                 current_x = region_frames_to_region_beats (current_x);
2721
2722                 if (at_front && current_x < canvas_note->note()->end_time()) {
2723                         note_diff_add_change (canvas_note, MidiModel::NoteDiffCommand::StartTime, current_x);
2724
2725                         double len = canvas_note->note()->time() - current_x;
2726                         len += canvas_note->note()->length();
2727
2728                         if (len > 0) {
2729                                 /* XXX convert to beats */
2730                                 note_diff_add_change (canvas_note, MidiModel::NoteDiffCommand::Length, len);
2731                         }
2732                 }
2733
2734                 if (!at_front) {
2735                         double len = current_x - canvas_note->note()->time();
2736
2737                         if (len > 0) {
2738                                 /* XXX convert to beats */
2739                                 note_diff_add_change (canvas_note, MidiModel::NoteDiffCommand::Length, len);
2740                         }
2741                 }
2742
2743                 delete resize_rect;
2744                 delete (*i);
2745         }
2746
2747         _resize_data.clear();
2748         apply_diff();
2749 }
2750
2751 void
2752 MidiRegionView::abort_resizing ()
2753 {
2754         for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
2755                 delete (*i)->resize_rect;
2756                 delete *i;
2757         }
2758
2759         _resize_data.clear ();
2760 }
2761
2762 void
2763 MidiRegionView::change_note_velocity(CanvasNoteEvent* event, int8_t velocity, bool relative)
2764 {
2765         uint8_t new_velocity;
2766
2767         if (relative) {
2768                 new_velocity = event->note()->velocity() + velocity;
2769                 clamp_to_0_127(new_velocity);
2770         } else {
2771                 new_velocity = velocity;
2772         }
2773
2774         event->set_selected (event->selected()); // change color
2775
2776         note_diff_add_change (event, MidiModel::NoteDiffCommand::Velocity, new_velocity);
2777 }
2778
2779 void
2780 MidiRegionView::change_note_note (CanvasNoteEvent* event, int8_t note, bool relative)
2781 {
2782         uint8_t new_note;
2783
2784         if (relative) {
2785                 new_note = event->note()->note() + note;
2786         } else {
2787                 new_note = note;
2788         }
2789
2790         clamp_to_0_127 (new_note);
2791         note_diff_add_change (event, MidiModel::NoteDiffCommand::NoteNumber, new_note);
2792 }
2793
2794 void
2795 MidiRegionView::trim_note (CanvasNoteEvent* event, Evoral::MusicalTime front_delta, Evoral::MusicalTime end_delta)
2796 {
2797         bool change_start = false;
2798         bool change_length = false;
2799         Evoral::MusicalTime new_start = 0;
2800         Evoral::MusicalTime new_length = 0;
2801
2802         /* NOTE: the semantics of the two delta arguments are slightly subtle:
2803
2804            front_delta: if positive - move the start of the note later in time (shortening it)
2805            if negative - move the start of the note earlier in time (lengthening it)
2806
2807            end_delta:   if positive - move the end of the note later in time (lengthening it)
2808            if negative - move the end of the note earlier in time (shortening it)
2809         */
2810
2811         if (front_delta) {
2812                 if (front_delta < 0) {
2813
2814                         if (event->note()->time() < -front_delta) {
2815                                 new_start = 0;
2816                         } else {
2817                                 new_start = event->note()->time() + front_delta; // moves earlier
2818                         }
2819
2820                         /* start moved toward zero, so move the end point out to where it used to be.
2821                            Note that front_delta is negative, so this increases the length.
2822                         */
2823
2824                         new_length = event->note()->length() - front_delta;
2825                         change_start = true;
2826                         change_length = true;
2827
2828                 } else {
2829
2830                         Evoral::MusicalTime new_pos = event->note()->time() + front_delta;
2831
2832                         if (new_pos < event->note()->end_time()) {
2833                                 new_start = event->note()->time() + front_delta;
2834                                 /* start moved toward the end, so move the end point back to where it used to be */
2835                                 new_length = event->note()->length() - front_delta;
2836                                 change_start = true;
2837                                 change_length = true;
2838                         }
2839                 }
2840
2841         }
2842
2843         if (end_delta) {
2844                 bool can_change = true;
2845                 if (end_delta < 0) {
2846                         if (event->note()->length() < -end_delta) {
2847                                 can_change = false;
2848                         }
2849                 }
2850
2851                 if (can_change) {
2852                         new_length = event->note()->length() + end_delta;
2853                         change_length = true;
2854                 }
2855         }
2856
2857         if (change_start) {
2858                 note_diff_add_change (event, MidiModel::NoteDiffCommand::StartTime, new_start);
2859         }
2860
2861         if (change_length) {
2862                 note_diff_add_change (event, MidiModel::NoteDiffCommand::Length, new_length);
2863         }
2864 }
2865
2866 void
2867 MidiRegionView::change_note_channel (CanvasNoteEvent* event, int8_t chn, bool relative)
2868 {
2869         uint8_t new_channel;
2870
2871         if (relative) {
2872                 if (chn < 0.0) {
2873                         if (event->note()->channel() < -chn) {
2874                                 new_channel = 0;
2875                         } else {
2876                                 new_channel = event->note()->channel() + chn;
2877                         }
2878                 } else {
2879                         new_channel = event->note()->channel() + chn;
2880                 }
2881         } else {
2882                 new_channel = (uint8_t) chn;
2883         }
2884
2885         note_diff_add_change (event, MidiModel::NoteDiffCommand::Channel, new_channel);
2886 }
2887
2888 void
2889 MidiRegionView::change_note_time (CanvasNoteEvent* event, Evoral::MusicalTime delta, bool relative)
2890 {
2891         Evoral::MusicalTime new_time;
2892
2893         if (relative) {
2894                 if (delta < 0.0) {
2895                         if (event->note()->time() < -delta) {
2896                                 new_time = 0;
2897                         } else {
2898                                 new_time = event->note()->time() + delta;
2899                         }
2900                 } else {
2901                         new_time = event->note()->time() + delta;
2902                 }
2903         } else {
2904                 new_time = delta;
2905         }
2906
2907         note_diff_add_change (event, MidiModel::NoteDiffCommand::StartTime, new_time);
2908 }
2909
2910 void
2911 MidiRegionView::change_note_length (CanvasNoteEvent* event, Evoral::MusicalTime t)
2912 {
2913         note_diff_add_change (event, MidiModel::NoteDiffCommand::Length, t);
2914 }
2915
2916 void
2917 MidiRegionView::change_velocities (bool up, bool fine, bool allow_smush, bool all_together)
2918 {
2919         int8_t delta;
2920         int8_t value = 0;
2921
2922         if (_selection.empty()) {
2923                 return;
2924         }
2925
2926         if (fine) {
2927                 delta = 1;
2928         } else {
2929                 delta = 10;
2930         }
2931
2932         if (!up) {
2933                 delta = -delta;
2934         }
2935
2936         if (!allow_smush) {
2937                 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2938                         if ((*i)->note()->velocity() < -delta || (*i)->note()->velocity() + delta > 127) {
2939                                 goto cursor_label;
2940                         }
2941                 }
2942         }
2943
2944         start_note_diff_command (_("change velocities"));
2945
2946         for (Selection::iterator i = _selection.begin(); i != _selection.end();) {
2947                 Selection::iterator next = i;
2948                 ++next;
2949
2950                 if (all_together) {
2951                         if (i == _selection.begin()) {
2952                                 change_note_velocity (*i, delta, true);
2953                                 value = (*i)->note()->velocity() + delta;
2954                         } else {
2955                                 change_note_velocity (*i, value, false);
2956                         }
2957
2958                 } else {
2959                         change_note_velocity (*i, delta, true);
2960                 }
2961
2962                 i = next;
2963         }
2964
2965         apply_diff();
2966
2967   cursor_label:
2968         if (!_selection.empty()) {
2969                 char buf[24];
2970                 snprintf (buf, sizeof (buf), "Vel %d",
2971                           (int) (*_selection.begin())->note()->velocity());
2972                 show_verbose_cursor (buf, 10, 10);
2973         }
2974 }
2975
2976
2977 void
2978 MidiRegionView::transpose (bool up, bool fine, bool allow_smush)
2979 {
2980         if (_selection.empty()) {
2981                 return;
2982         }
2983
2984         int8_t delta;
2985
2986         if (fine) {
2987                 delta = 1;
2988         } else {
2989                 delta = 12;
2990         }
2991
2992         if (!up) {
2993                 delta = -delta;
2994         }
2995
2996         if (!allow_smush) {
2997                 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2998                         if (!up) {
2999                                 if ((int8_t) (*i)->note()->note() + delta <= 0) {
3000                                         return;
3001                                 }
3002                         } else {
3003                                 if ((int8_t) (*i)->note()->note() + delta > 127) {
3004                                         return;
3005                                 }
3006                         }
3007                 }
3008         }
3009
3010         start_note_diff_command (_("transpose"));
3011
3012         for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
3013                 Selection::iterator next = i;
3014                 ++next;
3015                 change_note_note (*i, delta, true);
3016                 i = next;
3017         }
3018
3019         apply_diff ();
3020 }
3021
3022 void
3023 MidiRegionView::change_note_lengths (bool fine, bool shorter, Evoral::MusicalTime delta, bool start, bool end)
3024 {
3025         if (delta == 0.0) {
3026                 if (fine) {
3027                         delta = 1.0/128.0;
3028                 } else {
3029                         /* grab the current grid distance */
3030                         bool success;
3031                         delta = trackview.editor().get_grid_type_as_beats (success, _region->position());
3032                         if (!success) {
3033                                 /* XXX cannot get grid type as beats ... should always be possible ... FIX ME */
3034                                 error << string_compose (_("programming error: %1"), "Grid type not available as beats - TO BE FIXED") << endmsg;
3035                                 return;
3036                         }
3037                 }
3038         }
3039
3040         if (shorter) {
3041                 delta = -delta;
3042         }
3043
3044         start_note_diff_command (_("change note lengths"));
3045
3046         for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
3047                 Selection::iterator next = i;
3048                 ++next;
3049
3050                 /* note the negation of the delta for start */
3051
3052                 trim_note (*i, (start ? -delta : 0), (end ? delta : 0));
3053                 i = next;
3054         }
3055
3056         apply_diff ();
3057
3058 }
3059
3060 void
3061 MidiRegionView::nudge_notes (bool forward)
3062 {
3063         if (_selection.empty()) {
3064                 return;
3065         }
3066
3067         /* pick a note as the point along the timeline to get the nudge distance.
3068            its not necessarily the earliest note, so we may want to pull the notes out
3069            into a vector and sort before using the first one.
3070         */
3071
3072         framepos_t ref_point = source_beats_to_absolute_frames ((*(_selection.begin()))->note()->time());
3073         framepos_t unused;
3074         framecnt_t distance;
3075
3076         if (trackview.editor().snap_mode() == Editing::SnapOff) {
3077
3078                 /* grid is off - use nudge distance */
3079
3080                 distance = trackview.editor().get_nudge_distance (ref_point, unused);
3081
3082         } else {
3083
3084                 /* use grid */
3085
3086                 framepos_t next_pos = ref_point;
3087
3088                 if (forward) {
3089                         if (max_framepos - 1 < next_pos) {
3090                                 next_pos += 1;
3091                         }
3092                 } else {
3093                         if (next_pos == 0) {
3094                                 return;
3095                         }
3096                         next_pos -= 1;
3097                 }
3098
3099                 trackview.editor().snap_to (next_pos, (forward ? 1 : -1), false);
3100                 distance = ref_point - next_pos;
3101         }
3102
3103         if (distance == 0) {
3104                 return;
3105         }
3106
3107         Evoral::MusicalTime delta = region_frames_to_region_beats (fabs (distance));
3108
3109         if (!forward) {
3110                 delta = -delta;
3111         }
3112
3113         start_note_diff_command (_("nudge"));
3114
3115         for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
3116                 Selection::iterator next = i;
3117                 ++next;
3118                 change_note_time (*i, delta, true);
3119                 i = next;
3120         }
3121
3122         apply_diff ();
3123 }
3124
3125 void
3126 MidiRegionView::change_channel(uint8_t channel)
3127 {
3128         start_note_diff_command(_("change channel"));
3129         for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3130                 note_diff_add_change (*i, MidiModel::NoteDiffCommand::Channel, channel);
3131         }
3132
3133         apply_diff();
3134 }
3135
3136
3137 void
3138 MidiRegionView::note_entered(ArdourCanvas::CanvasNoteEvent* ev)
3139 {
3140         Editor* editor = dynamic_cast<Editor*>(&trackview.editor());
3141
3142         pre_enter_cursor = editor->get_canvas_cursor ();
3143
3144         if (_mouse_state == SelectTouchDragging) {
3145                 note_selected (ev, true);
3146         }
3147
3148         show_verbose_cursor (ev->note ());
3149 }
3150
3151 void
3152 MidiRegionView::note_left (ArdourCanvas::CanvasNoteEvent*)
3153 {
3154         Editor* editor = dynamic_cast<Editor*>(&trackview.editor());
3155
3156         for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3157                 (*i)->hide_velocity ();
3158         }
3159
3160         editor->verbose_cursor()->hide ();
3161
3162         if (pre_enter_cursor) {
3163                 editor->set_canvas_cursor (pre_enter_cursor);
3164                 pre_enter_cursor = 0;
3165         }
3166 }
3167
3168 void
3169 MidiRegionView::patch_entered (ArdourCanvas::CanvasPatchChange* p)
3170 {
3171         ostringstream s;
3172         /* XXX should get patch name if we can */
3173         s << _("Bank:") << (p->patch()->bank() + MIDI_BP_ZERO) << '\n' 
3174           << _("Program:") << ((int) p->patch()->program()) + MIDI_BP_ZERO << '\n' 
3175           << _("Channel:") << ((int) p->patch()->channel() + 1);
3176         show_verbose_cursor (s.str(), 10, 20);
3177         p->grab_focus();
3178 }
3179
3180 void
3181 MidiRegionView::patch_left (ArdourCanvas::CanvasPatchChange *)
3182 {
3183         trackview.editor().verbose_cursor()->hide ();
3184         /* focus will transfer back via the enter-notify event sent to this
3185          * midi region view.
3186          */
3187 }
3188
3189 void
3190 MidiRegionView::sysex_entered (ArdourCanvas::CanvasSysEx* p)
3191 {
3192         ostringstream s;
3193         s << p->text();
3194         show_verbose_cursor (s.str(), 10, 20);
3195         p->grab_focus();
3196 }
3197
3198 void
3199 MidiRegionView::sysex_left (ArdourCanvas::CanvasSysEx *)
3200 {
3201         trackview.editor().verbose_cursor()->hide ();
3202         /* focus will transfer back via the enter-notify event sent to this
3203          * midi region view.
3204          */
3205 }
3206
3207 void
3208 MidiRegionView::note_mouse_position (float x_fraction, float /*y_fraction*/, bool can_set_cursor)
3209 {
3210         Editor* editor = dynamic_cast<Editor*>(&trackview.editor());
3211         Editing::MouseMode mm = editor->current_mouse_mode();
3212         bool trimmable = (mm == MouseObject || mm == MouseTimeFX || mm == MouseDraw);
3213
3214         if (trimmable && x_fraction > 0.0 && x_fraction < 0.2) {
3215                 editor->set_canvas_cursor (editor->cursors()->left_side_trim);
3216         } else if (trimmable && x_fraction >= 0.8 && x_fraction < 1.0) {
3217                 editor->set_canvas_cursor (editor->cursors()->right_side_trim);
3218         } else {
3219                 if (pre_enter_cursor && can_set_cursor) {
3220                         editor->set_canvas_cursor (pre_enter_cursor);
3221                 }
3222         }
3223 }
3224
3225 void
3226 MidiRegionView::set_frame_color()
3227 {
3228         uint32_t f;
3229
3230         TimeAxisViewItem::set_frame_color ();
3231
3232         if (!frame) {
3233                 return;
3234         }
3235
3236         if (_selected) {
3237                 f = ARDOUR_UI::config()->canvasvar_SelectedFrameBase.get();
3238         } else if (high_enough_for_name) {
3239                 f= ARDOUR_UI::config()->canvasvar_MidiFrameBase.get();
3240         } else {
3241                 f = fill_color;
3242         }
3243
3244         if (!rect_visible) {
3245                 f = UINT_RGBA_CHANGE_A (f, 0);
3246         }
3247
3248         frame->property_fill_color_rgba() = f;
3249 }
3250
3251 void
3252 MidiRegionView::midi_channel_mode_changed(ChannelMode mode, uint16_t mask)
3253 {
3254         if (mode == ForceChannel) {
3255                 mask = 0xFFFF; // Show all notes as active (below)
3256         }
3257
3258         // Update notes for selection
3259         for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3260                 (*i)->on_channel_selection_change(mask);
3261         }
3262
3263         _last_channel_selection = mask;
3264         _last_channel_mode = mode;
3265
3266         _patch_changes.clear ();
3267         display_patch_changes ();
3268 }
3269
3270 void
3271 MidiRegionView::instrument_settings_changed ()
3272 {
3273         redisplay_model();
3274 }
3275
3276 void
3277 MidiRegionView::cut_copy_clear (Editing::CutCopyOp op)
3278 {
3279         if (_selection.empty()) {
3280                 return;
3281         }
3282
3283         PublicEditor& editor (trackview.editor());
3284
3285         switch (op) {
3286         case Delete:
3287                 /* XXX what to do ? */
3288                 break;
3289         case Cut:
3290         case Copy:
3291                 editor.get_cut_buffer().add (selection_as_cut_buffer());
3292                 break;
3293         default:
3294                 break;
3295         }
3296
3297         if (op != Copy) {
3298
3299                 start_note_diff_command();
3300
3301                 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3302                         switch (op) {
3303                         case Copy:
3304                                 break;
3305                         case Delete:
3306                         case Cut:
3307                         case Clear:
3308                                 note_diff_remove_note (*i);
3309                                 break;
3310                         }
3311                 }
3312
3313                 apply_diff();
3314         }
3315 }
3316
3317 MidiCutBuffer*
3318 MidiRegionView::selection_as_cut_buffer () const
3319 {
3320         Notes notes;
3321
3322         for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3323                 NoteType* n = (*i)->note().get();
3324                 notes.insert (boost::shared_ptr<NoteType> (new NoteType (*n)));
3325         }
3326
3327         MidiCutBuffer* cb = new MidiCutBuffer (trackview.session());
3328         cb->set (notes);
3329
3330         return cb;
3331 }
3332
3333 /** This method handles undo */
3334 void
3335 MidiRegionView::paste (framepos_t pos, float times, const MidiCutBuffer& mcb)
3336 {
3337         if (mcb.empty()) {
3338                 return;
3339         }
3340
3341         DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("MIDI paste @ %1 times %2\n", pos, times));
3342
3343         trackview.session()->begin_reversible_command (_("paste"));
3344
3345         start_note_diff_command (_("paste"));
3346
3347         Evoral::MusicalTime beat_delta;
3348         Evoral::MusicalTime paste_pos_beats;
3349         Evoral::MusicalTime duration;
3350         Evoral::MusicalTime end_point = 0;
3351
3352         duration = (*mcb.notes().rbegin())->end_time() - (*mcb.notes().begin())->time();
3353         paste_pos_beats = absolute_frames_to_source_beats (pos);
3354         beat_delta = (*mcb.notes().begin())->time() - paste_pos_beats;
3355         paste_pos_beats = 0;
3356
3357         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",
3358                                                        (*mcb.notes().begin())->time(),
3359                                                        (*mcb.notes().rbegin())->end_time(),
3360                                                        duration, pos, _region->position(),
3361                                                        paste_pos_beats, beat_delta));
3362
3363         clear_selection ();
3364
3365         for (int n = 0; n < (int) times; ++n) {
3366
3367                 for (Notes::const_iterator i = mcb.notes().begin(); i != mcb.notes().end(); ++i) {
3368
3369                         boost::shared_ptr<NoteType> copied_note (new NoteType (*((*i).get())));
3370                         copied_note->set_time (paste_pos_beats + copied_note->time() - beat_delta);
3371
3372                         /* make all newly added notes selected */
3373
3374                         note_diff_add_note (copied_note, true);
3375                         end_point = copied_note->end_time();
3376                 }
3377
3378                 paste_pos_beats += duration;
3379         }
3380
3381         /* if we pasted past the current end of the region, extend the region */
3382
3383         framepos_t end_frame = source_beats_to_absolute_frames (end_point);
3384         framepos_t region_end = _region->position() + _region->length() - 1;
3385
3386         if (end_frame > region_end) {
3387
3388                 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("Paste extended region from %1 to %2\n", region_end, end_frame));
3389
3390                 _region->clear_changes ();
3391                 _region->set_length (end_frame - _region->position());
3392                 trackview.session()->add_command (new StatefulDiffCommand (_region));
3393         }
3394
3395         apply_diff (true);
3396
3397         trackview.session()->commit_reversible_command ();
3398 }
3399
3400 struct EventNoteTimeEarlyFirstComparator {
3401         bool operator() (CanvasNoteEvent* a, CanvasNoteEvent* b) {
3402                 return a->note()->time() < b->note()->time();
3403         }
3404 };
3405
3406 void
3407 MidiRegionView::time_sort_events ()
3408 {
3409         if (!_sort_needed) {
3410                 return;
3411         }
3412
3413         EventNoteTimeEarlyFirstComparator cmp;
3414         _events.sort (cmp);
3415
3416         _sort_needed = false;
3417 }
3418
3419 void
3420 MidiRegionView::goto_next_note (bool add_to_selection)
3421 {
3422         bool use_next = false;
3423
3424         if (_events.back()->selected()) {
3425                 return;
3426         }
3427
3428         time_sort_events ();
3429
3430         MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3431         uint16_t const channel_mask = mtv->channel_selector().get_selected_channels ();
3432
3433         for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3434                 if ((*i)->selected()) {
3435                         use_next = true;
3436                         continue;
3437                 } else if (use_next) {
3438                         if (channel_mask & (1 << (*i)->note()->channel())) {
3439                                 if (!add_to_selection) {
3440                                         unique_select (*i);
3441                                 } else {
3442                                         note_selected (*i, true, false);
3443                                 }
3444                                 return;
3445                         }
3446                 }
3447         }
3448
3449         /* use the first one */
3450
3451         if (!_events.empty() && (channel_mask & (1 << _events.front()->note()->channel ()))) {
3452                 unique_select (_events.front());
3453         }
3454 }
3455
3456 void
3457 MidiRegionView::goto_previous_note (bool add_to_selection)
3458 {
3459         bool use_next = false;
3460
3461         if (_events.front()->selected()) {
3462                 return;
3463         }
3464
3465         time_sort_events ();
3466
3467         MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3468         uint16_t const channel_mask = mtv->channel_selector().get_selected_channels ();
3469
3470         for (Events::reverse_iterator i = _events.rbegin(); i != _events.rend(); ++i) {
3471                 if ((*i)->selected()) {
3472                         use_next = true;
3473                         continue;
3474                 } else if (use_next) {
3475                         if (channel_mask & (1 << (*i)->note()->channel())) {
3476                                 if (!add_to_selection) {
3477                                         unique_select (*i);
3478                                 } else {
3479                                         note_selected (*i, true, false);
3480                                 }
3481                                 return;
3482                         }
3483                 }
3484         }
3485
3486         /* use the last one */
3487
3488         if (!_events.empty() && (channel_mask & (1 << (*_events.rbegin())->note()->channel ()))) {
3489                 unique_select (*(_events.rbegin()));
3490         }
3491 }
3492
3493 void
3494 MidiRegionView::selection_as_notelist (Notes& selected, bool allow_all_if_none_selected)
3495 {
3496         bool had_selected = false;
3497
3498         time_sort_events ();
3499
3500         for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3501                 if ((*i)->selected()) {
3502                         selected.insert ((*i)->note());
3503                         had_selected = true;
3504                 }
3505         }
3506
3507         if (allow_all_if_none_selected && !had_selected) {
3508                 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3509                         selected.insert ((*i)->note());
3510                 }
3511         }
3512 }
3513
3514 void
3515 MidiRegionView::update_ghost_note (double x, double y)
3516 {
3517         MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3518
3519         _last_ghost_x = x;
3520         _last_ghost_y = y;
3521
3522         _note_group->w2i (x, y);
3523
3524         PublicEditor& editor = trackview.editor ();
3525         
3526         framepos_t const unsnapped_frame = editor.pixel_to_frame (x);
3527         framecnt_t grid_frames;
3528         framepos_t const f = snap_frame_to_grid_underneath (unsnapped_frame, grid_frames);
3529
3530         /* use region_frames... because we are converting a delta within the region
3531         */
3532          
3533         bool success;
3534         double length = editor.get_grid_type_as_beats (success, unsnapped_frame);
3535
3536         if (!success) {
3537                 length = 1;
3538         }
3539
3540         /* note that this sets the time of the ghost note in beats relative to
3541            the start of the source; that is how all note times are stored.
3542         */
3543         _ghost_note->note()->set_time (absolute_frames_to_source_beats (f + _region->position ()));
3544         _ghost_note->note()->set_length (length);
3545         _ghost_note->note()->set_note (midi_stream_view()->y_to_note (y));
3546         _ghost_note->note()->set_channel (mtv->get_channel_for_add ());
3547
3548         /* the ghost note does not appear in ghost regions, so pass false in here */
3549         update_note (_ghost_note, false);
3550
3551         show_verbose_cursor (_ghost_note->note ());
3552 }
3553
3554 void
3555 MidiRegionView::create_ghost_note (double x, double y)
3556 {
3557         remove_ghost_note ();
3558
3559         boost::shared_ptr<NoteType> g (new NoteType);
3560         _ghost_note = new NoEventCanvasNote (*this, *_note_group, g);
3561         _ghost_note->property_outline_color_rgba() = 0x000000aa;
3562         update_ghost_note (x, y);
3563         _ghost_note->show ();
3564
3565         _last_ghost_x = x;
3566         _last_ghost_y = y;
3567
3568         show_verbose_cursor (_ghost_note->note ());
3569 }
3570
3571 void
3572 MidiRegionView::snap_changed ()
3573 {
3574         if (!_ghost_note) {
3575                 return;
3576         }
3577
3578         create_ghost_note (_last_ghost_x, _last_ghost_y);
3579 }
3580
3581 void
3582 MidiRegionView::drop_down_keys ()
3583 {
3584         _mouse_state = None;
3585 }
3586
3587 void
3588 MidiRegionView::maybe_select_by_position (GdkEventButton* ev, double /*x*/, double y)
3589 {
3590         double note = midi_stream_view()->y_to_note(y);
3591         Events e;
3592         MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3593
3594         uint16_t chn_mask = mtv->channel_selector().get_selected_channels();
3595
3596         if (Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) {
3597                 get_events (e, Evoral::Sequence<Evoral::MusicalTime>::PitchGreaterThanOrEqual, (uint8_t) floor (note), chn_mask);
3598         } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
3599                 get_events (e, Evoral::Sequence<Evoral::MusicalTime>::PitchLessThanOrEqual, (uint8_t) floor (note), chn_mask);
3600         } else {
3601                 return;
3602         }
3603
3604         bool add_mrv_selection = false;
3605
3606         if (_selection.empty()) {
3607                 add_mrv_selection = true;
3608         }
3609
3610         for (Events::iterator i = e.begin(); i != e.end(); ++i) {
3611                 if (_selection.insert (*i).second) {
3612                         (*i)->set_selected (true);
3613                 }
3614         }
3615
3616         if (add_mrv_selection) {
3617                 PublicEditor& editor (trackview.editor());
3618                 editor.get_selection().add (this);
3619         }
3620 }
3621
3622 void
3623 MidiRegionView::color_handler ()
3624 {
3625         RegionView::color_handler ();
3626
3627         for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3628                 (*i)->set_selected ((*i)->selected()); // will change color
3629         }
3630
3631         /* XXX probably more to do here */
3632 }
3633
3634 void
3635 MidiRegionView::enable_display (bool yn)
3636 {
3637         RegionView::enable_display (yn);
3638         if (yn) {
3639                 redisplay_model ();
3640         }
3641 }
3642
3643 void
3644 MidiRegionView::show_step_edit_cursor (Evoral::MusicalTime pos)
3645 {
3646         if (_step_edit_cursor == 0) {
3647                 ArdourCanvas::Group* const group = (ArdourCanvas::Group*)get_canvas_group();
3648
3649                 _step_edit_cursor = new ArdourCanvas::SimpleRect (*group);
3650                 _step_edit_cursor->property_y1() = 0;
3651                 _step_edit_cursor->property_y2() = midi_stream_view()->contents_height();
3652                 _step_edit_cursor->property_fill_color_rgba() = RGBA_TO_UINT (45,0,0,90);
3653                 _step_edit_cursor->property_outline_color_rgba() = RGBA_TO_UINT (85,0,0,90);
3654         }
3655
3656         move_step_edit_cursor (pos);
3657         _step_edit_cursor->show ();
3658 }
3659
3660 void
3661 MidiRegionView::move_step_edit_cursor (Evoral::MusicalTime pos)
3662 {
3663         _step_edit_cursor_position = pos;
3664
3665         if (_step_edit_cursor) {
3666                 double pixel = trackview.editor().frame_to_pixel (region_beats_to_region_frames (pos));
3667                 _step_edit_cursor->property_x1() = pixel;
3668                 set_step_edit_cursor_width (_step_edit_cursor_width);
3669         }
3670 }
3671
3672 void
3673 MidiRegionView::hide_step_edit_cursor ()
3674 {
3675         if (_step_edit_cursor) {
3676                 _step_edit_cursor->hide ();
3677         }
3678 }
3679
3680 void
3681 MidiRegionView::set_step_edit_cursor_width (Evoral::MusicalTime beats)
3682 {
3683         _step_edit_cursor_width = beats;
3684
3685         if (_step_edit_cursor) {
3686                 _step_edit_cursor->property_x2() = _step_edit_cursor->property_x1() + trackview.editor().frame_to_pixel (region_beats_to_region_frames (beats));
3687         }
3688 }
3689
3690 /** Called when a diskstream on our track has received some data.  Update the view, if applicable.
3691  *  @param w Source that the data will end up in.
3692  */
3693 void
3694 MidiRegionView::data_recorded (boost::weak_ptr<MidiSource> w)
3695 {
3696         if (!_active_notes) {
3697                 /* we aren't actively being recorded to */
3698                 return;
3699         }
3700
3701         boost::shared_ptr<MidiSource> src = w.lock ();
3702         if (!src || src != midi_region()->midi_source()) {
3703                 /* recorded data was not destined for our source */
3704                 return;
3705         }
3706
3707         MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*> (&trackview);
3708
3709         boost::shared_ptr<MidiBuffer> buf = mtv->midi_track()->get_gui_feed_buffer ();
3710
3711         BeatsFramesConverter converter (trackview.session()->tempo_map(), mtv->midi_track()->get_capture_start_frame (0));
3712
3713         framepos_t back = max_framepos;
3714
3715         for (MidiBuffer::iterator i = buf->begin(); i != buf->end(); ++i) {
3716                 Evoral::MIDIEvent<MidiBuffer::TimeType> const ev (*i, false);
3717                 assert (ev.buffer ());
3718
3719                 if(ev.is_channel_event()) {
3720                         if (_last_channel_mode == FilterChannels) {
3721                                 if(((uint16_t(1) << ev.channel()) & _last_channel_selection) == 0) {
3722                                         continue;
3723                                 }
3724                         }
3725                 }
3726
3727                 /* ev.time() is in session frames, so (ev.time() - converter.origin_b()) is
3728                    frames from the start of the source, and so time_beats is in terms of the
3729                    source.
3730                 */
3731
3732                 Evoral::MusicalTime const time_beats = converter.from (ev.time () - converter.origin_b ());
3733
3734                 if (ev.type() == MIDI_CMD_NOTE_ON) {
3735
3736                         boost::shared_ptr<NoteType> note (
3737                                 new NoteType (ev.channel(), time_beats, 0, ev.note(), ev.velocity())
3738                                                           );
3739
3740                         add_note (note, true);
3741
3742                         /* fix up our note range */
3743                         if (ev.note() < _current_range_min) {
3744                                 midi_stream_view()->apply_note_range (ev.note(), _current_range_max, true);
3745                         } else if (ev.note() > _current_range_max) {
3746                                 midi_stream_view()->apply_note_range (_current_range_min, ev.note(), true);
3747                         }
3748
3749                 } else if (ev.type() == MIDI_CMD_NOTE_OFF) {
3750                         resolve_note (ev.note (), time_beats);
3751                 }
3752
3753                 back = ev.time ();
3754         }
3755
3756         midi_stream_view()->check_record_layers (region(), back);
3757 }
3758
3759 void
3760 MidiRegionView::trim_front_starting ()
3761 {
3762         /* Reparent the note group to the region view's parent, so that it doesn't change
3763            when the region view is trimmed.
3764         */
3765         _temporary_note_group = new ArdourCanvas::Group (*group->property_parent ());
3766         _temporary_note_group->move (group->property_x(), group->property_y());
3767         _note_group->reparent (*_temporary_note_group);
3768 }
3769
3770 void
3771 MidiRegionView::trim_front_ending ()
3772 {
3773         _note_group->reparent (*group);
3774         delete _temporary_note_group;
3775         _temporary_note_group = 0;
3776
3777         if (_region->start() < 0) {
3778                 /* Trim drag made start time -ve; fix this */
3779                 midi_region()->fix_negative_start ();
3780         }
3781 }
3782
3783 void
3784 MidiRegionView::edit_patch_change (ArdourCanvas::CanvasPatchChange* pc)
3785 {
3786         PatchChangeDialog d (&_source_relative_time_converter, trackview.session(), *pc->patch (), instrument_info(), Gtk::Stock::APPLY, true);
3787
3788         d.set_position (Gtk::WIN_POS_MOUSE);
3789         
3790         int response = d.run();
3791
3792         switch (response) {
3793         case Gtk::RESPONSE_ACCEPT:
3794                 break;
3795         case Gtk::RESPONSE_REJECT:
3796                 delete_patch_change (pc);
3797                 return;
3798         default:
3799                 return;
3800         }
3801
3802         change_patch_change (pc->patch(), d.patch ());
3803 }
3804
3805 void
3806 MidiRegionView::delete_sysex (CanvasSysEx* sysex)
3807 {
3808         MidiModel::SysExDiffCommand* c = _model->new_sysex_diff_command (_("delete sysex"));
3809         c->remove (sysex->sysex());
3810         _model->apply_command (*trackview.session(), c);
3811
3812         _sys_exes.clear ();
3813         display_sysexes();
3814 }
3815
3816 void
3817 MidiRegionView::show_verbose_cursor (boost::shared_ptr<NoteType> n) const
3818 {
3819         char buf[24];
3820         snprintf (buf, sizeof (buf), "%s (%d) Chn %d\nVel %d",
3821                   Evoral::midi_note_name (n->note()).c_str(),
3822                   (int) n->note (),
3823                   (int) n->channel() + 1,
3824                   (int) n->velocity());
3825
3826         show_verbose_cursor (buf, 10, 20);
3827 }
3828
3829 void
3830 MidiRegionView::show_verbose_cursor (string const & text, double xoffset, double yoffset) const
3831 {
3832         double wx, wy;
3833
3834         trackview.editor().get_pointer_position (wx, wy);
3835
3836         wx += xoffset;
3837         wy += yoffset;
3838
3839         /* Flip the cursor above the mouse pointer if it would overlap the bottom of the canvas */
3840
3841         double x1, y1, x2, y2;
3842         trackview.editor().verbose_cursor()->canvas_item()->get_bounds (x1, y1, x2, y2);
3843
3844         if ((wy + y2 - y1) > trackview.editor().canvas_height()) {
3845                 wy -= (y2 - y1) + 2 * yoffset;
3846         }
3847
3848         trackview.editor().verbose_cursor()->set (text, wx, wy);
3849         trackview.editor().verbose_cursor()->show ();
3850 }
3851
3852 /** @param p A session framepos.
3853  *  @param grid_frames Filled in with the number of frames that a grid interval is at p.
3854  *  @return p snapped to the grid subdivision underneath it.
3855  */
3856 framepos_t
3857 MidiRegionView::snap_frame_to_grid_underneath (framepos_t p, framecnt_t& grid_frames) const
3858 {
3859         PublicEditor& editor = trackview.editor ();
3860         
3861         bool success;
3862         Evoral::MusicalTime grid_beats = editor.get_grid_type_as_beats (success, p);
3863
3864         if (!success) {
3865                 grid_beats = 1;
3866         }
3867         
3868         grid_frames = region_beats_to_region_frames (grid_beats);
3869
3870         /* Hack so that we always snap to the note that we are over, instead of snapping
3871            to the next one if we're more than halfway through the one we're over.
3872         */
3873         if (editor.snap_mode() == SnapNormal && p >= grid_frames / 2) {
3874                 p -= grid_frames / 2;
3875         }
3876
3877         return snap_frame_to_frame (p);
3878 }
3879
3880 /** Called when the selection has been cleared in any MidiRegionView.
3881  *  @param rv MidiRegionView that the selection was cleared in.
3882  */
3883 void
3884 MidiRegionView::selection_cleared (MidiRegionView* rv)
3885 {
3886         if (rv == this) {
3887                 return;
3888         }
3889
3890         /* Clear our selection in sympathy; but don't signal the fact */
3891         clear_selection (false);
3892 }
3893
3894 void
3895 MidiRegionView::note_button_release ()
3896 {
3897         delete _note_player;
3898         _note_player = 0;
3899 }