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