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