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