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