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