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