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