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