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