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