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