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