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