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