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