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