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