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