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