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