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