Update GPL boilerplate and (C)
[ardour.git] / gtk2_ardour / midi_region_view.cc
1 /*
2  * Copyright (C) 2006-2016 David Robillard <d@drobilla.net>
3  * Copyright (C) 2007-2012 Carl Hetherington <carl@carlh.net>
4  * Copyright (C) 2007-2018 Paul Davis <paul@linuxaudiosystems.com>
5  * Copyright (C) 2008-2012 Hans Baier <hansfbaier@googlemail.com>
6  * Copyright (C) 2013-2017 John Emmas <john@creativepost.co.uk>
7  * Copyright (C) 2014-2017 Nick Mainsbridge <mainsbridge@gmail.com>
8  * Copyright (C) 2014-2018 Ben Loftis <ben@harrisonconsoles.com>
9  * Copyright (C) 2014-2019 Robin Gareus <robin@gareus.org>
10  * Copyright (C) 2015-2016 Tim Mayberry <mojofunk@gmail.com>
11  * Copyright (C) 2015-2017 AndrĂ© Nusser <andre.nusser@googlemail.com>
12  *
13  * This program is free software; you can redistribute it and/or modify
14  * it under the terms of the GNU General Public License as published by
15  * the Free Software Foundation; either version 2 of the License, or
16  * (at your option) any later version.
17  *
18  * This program is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21  * GNU General Public License for more details.
22  *
23  * You should have received a copy of the GNU General Public License along
24  * with this program; if not, write to the Free Software Foundation, Inc.,
25  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
26  */
27
28 #include <cmath>
29 #include <algorithm>
30 #include <ostream>
31
32 #include <gtkmm.h>
33
34 #include "gtkmm2ext/gtk_ui.h"
35
36 #include <sigc++/signal.h>
37
38 #include "midi++/midnam_patch.h"
39
40 #include "pbd/memento_command.h"
41 #include "pbd/stateful_diff_command.h"
42
43 #include "ardour/midi_model.h"
44 #include "ardour/midi_playlist.h"
45 #include "ardour/midi_region.h"
46 #include "ardour/midi_source.h"
47 #include "ardour/midi_track.h"
48 #include "ardour/operations.h"
49 #include "ardour/session.h"
50
51 #include "evoral/Parameter.hpp"
52 #include "evoral/Event.hpp"
53 #include "evoral/Control.hpp"
54 #include "evoral/midi_util.h"
55
56 #include "canvas/debug.h"
57 #include "canvas/text.h"
58
59 #include "automation_region_view.h"
60 #include "automation_time_axis.h"
61 #include "control_point.h"
62 #include "debug.h"
63 #include "editor.h"
64 #include "editor_drag.h"
65 #include "ghostregion.h"
66 #include "gui_thread.h"
67 #include "item_counts.h"
68 #include "keyboard.h"
69 #include "midi_channel_dialog.h"
70 #include "midi_cut_buffer.h"
71 #include "midi_list_editor.h"
72 #include "midi_region_view.h"
73 #include "midi_streamview.h"
74 #include "midi_time_axis.h"
75 #include "midi_util.h"
76 #include "midi_velocity_dialog.h"
77 #include "mouse_cursors.h"
78 #include "note_player.h"
79 #include "paste_context.h"
80 #include "public_editor.h"
81 #include "route_time_axis.h"
82 #include "rgb_macros.h"
83 #include "selection.h"
84 #include "streamview.h"
85 #include "patch_change_dialog.h"
86 #include "verbose_cursor.h"
87 #include "note.h"
88 #include "hit.h"
89 #include "patch_change.h"
90 #include "sys_ex.h"
91 #include "ui_config.h"
92
93 #include "pbd/i18n.h"
94
95 using namespace ARDOUR;
96 using namespace PBD;
97 using namespace Editing;
98 using namespace std;
99 using Gtkmm2ext::Keyboard;
100
101 #define MIDI_BP_ZERO ((Config->get_first_midi_bank_is_zero())?0:1)
102
103 MidiRegionView::MidiRegionView (ArdourCanvas::Container*      parent,
104                                 RouteTimeAxisView&            tv,
105                                 boost::shared_ptr<MidiRegion> r,
106                                 double                        spu,
107                                 uint32_t                      basic_color)
108         : RegionView (parent, tv, r, spu, basic_color)
109         , _current_range_min(0)
110         , _current_range_max(0)
111         , _region_relative_time_converter(r->session().tempo_map(), r->position())
112         , _source_relative_time_converter(r->session().tempo_map(), r->position() - r->start())
113         , _region_relative_time_converter_double(r->session().tempo_map(), r->position())
114         , _active_notes(0)
115         , _note_group (new ArdourCanvas::Container (group))
116         , _note_diff_command (0)
117         , _ghost_note(0)
118         , _step_edit_cursor (0)
119         , _step_edit_cursor_width (1.0)
120         , _step_edit_cursor_position (0.0)
121         , _channel_selection_scoped_note (0)
122         , _mouse_state(None)
123         , _pressed_button(0)
124         , _optimization_iterator (_events.end())
125         , _list_editor (0)
126         , _no_sound_notes (false)
127         , _last_display_zoom (0)
128         , _last_event_x (0)
129         , _last_event_y (0)
130         , _grabbed_keyboard (false)
131         , _entered (false)
132         , _entered_note (0)
133         , _mouse_changed_selection (false)
134 {
135         CANVAS_DEBUG_NAME (_note_group, string_compose ("note group for %1", get_item_name()));
136
137         _patch_change_outline = UIConfiguration::instance().color ("midi patch change outline");
138         _patch_change_fill = UIConfiguration::instance().color_mod ("midi patch change fill", "midi patch change fill");
139
140         _note_group->raise_to_top();
141         PublicEditor::DropDownKeys.connect (sigc::mem_fun (*this, &MidiRegionView::drop_down_keys));
142
143         Config->ParameterChanged.connect (*this, invalidator (*this), boost::bind (&MidiRegionView::parameter_changed, this, _1), gui_context());
144         UIConfiguration::instance().ParameterChanged.connect (sigc::mem_fun (*this, &MidiRegionView::parameter_changed));
145
146         connect_to_diskstream ();
147 }
148
149 MidiRegionView::MidiRegionView (ArdourCanvas::Container*      parent,
150                                 RouteTimeAxisView&            tv,
151                                 boost::shared_ptr<MidiRegion> r,
152                                 double                        spu,
153                                 uint32_t                      basic_color,
154                                 bool                          recording,
155                                 TimeAxisViewItem::Visibility  visibility)
156         : RegionView (parent, tv, r, spu, basic_color, recording, visibility)
157         , _current_range_min(0)
158         , _current_range_max(0)
159         , _region_relative_time_converter(r->session().tempo_map(), r->position())
160         , _source_relative_time_converter(r->session().tempo_map(), r->position() - r->start())
161         , _region_relative_time_converter_double(r->session().tempo_map(), r->position())
162         , _active_notes(0)
163         , _note_group (new ArdourCanvas::Container (group))
164         , _note_diff_command (0)
165         , _ghost_note(0)
166         , _step_edit_cursor (0)
167         , _step_edit_cursor_width (1.0)
168         , _step_edit_cursor_position (0.0)
169         , _channel_selection_scoped_note (0)
170         , _mouse_state(None)
171         , _pressed_button(0)
172         , _optimization_iterator (_events.end())
173         , _list_editor (0)
174         , _no_sound_notes (false)
175         , _last_display_zoom (0)
176         , _last_event_x (0)
177         , _last_event_y (0)
178         , _grabbed_keyboard (false)
179         , _entered (false)
180         , _entered_note (0)
181         , _mouse_changed_selection (false)
182 {
183         CANVAS_DEBUG_NAME (_note_group, string_compose ("note group for %1", get_item_name()));
184
185         _patch_change_outline = UIConfiguration::instance().color ("midi patch change outline");
186         _patch_change_fill = UIConfiguration::instance().color_mod ("midi patch change fill", "midi patch change fill");
187
188         _note_group->raise_to_top();
189
190         PublicEditor::DropDownKeys.connect (sigc::mem_fun (*this, &MidiRegionView::drop_down_keys));
191
192         connect_to_diskstream ();
193 }
194
195 void
196 MidiRegionView::parameter_changed (std::string const & p)
197 {
198         if (p == "display-first-midi-bank-as-zero") {
199                 if (_enable_display) {
200                         redisplay_model();
201                 }
202         } else if (p == "color-regions-using-track-color") {
203                 set_colors ();
204         } else if (p == "use-note-color-for-velocity") {
205                 color_handler ();
206         }
207 }
208
209 MidiRegionView::MidiRegionView (const MidiRegionView& other)
210         : sigc::trackable(other)
211         , RegionView (other)
212         , _current_range_min(0)
213         , _current_range_max(0)
214         , _region_relative_time_converter(other.region_relative_time_converter())
215         , _source_relative_time_converter(other.source_relative_time_converter())
216         , _region_relative_time_converter_double(other.region_relative_time_converter_double())
217         , _active_notes(0)
218         , _note_group (new ArdourCanvas::Container (get_canvas_group()))
219         , _note_diff_command (0)
220         , _ghost_note(0)
221         , _step_edit_cursor (0)
222         , _step_edit_cursor_width (1.0)
223         , _step_edit_cursor_position (0.0)
224         , _channel_selection_scoped_note (0)
225         , _mouse_state(None)
226         , _pressed_button(0)
227         , _optimization_iterator (_events.end())
228         , _list_editor (0)
229         , _no_sound_notes (false)
230         , _last_display_zoom (0)
231         , _last_event_x (0)
232         , _last_event_y (0)
233         , _grabbed_keyboard (false)
234         , _entered (false)
235         , _entered_note (0)
236         , _mouse_changed_selection (false)
237 {
238         init (false);
239 }
240
241 MidiRegionView::MidiRegionView (const MidiRegionView& other, boost::shared_ptr<MidiRegion> region)
242         : RegionView (other, boost::shared_ptr<Region> (region))
243         , _current_range_min(0)
244         , _current_range_max(0)
245         , _region_relative_time_converter(other.region_relative_time_converter())
246         , _source_relative_time_converter(other.source_relative_time_converter())
247         , _region_relative_time_converter_double(other.region_relative_time_converter_double())
248         , _active_notes(0)
249         , _note_group (new ArdourCanvas::Container (get_canvas_group()))
250         , _note_diff_command (0)
251         , _ghost_note(0)
252         , _step_edit_cursor (0)
253         , _step_edit_cursor_width (1.0)
254         , _step_edit_cursor_position (0.0)
255         , _channel_selection_scoped_note (0)
256         , _mouse_state(None)
257         , _pressed_button(0)
258         , _optimization_iterator (_events.end())
259         , _list_editor (0)
260         , _no_sound_notes (false)
261         , _last_display_zoom (0)
262         , _last_event_x (0)
263         , _last_event_y (0)
264         , _grabbed_keyboard (false)
265         , _entered (false)
266         , _entered_note (0)
267         , _mouse_changed_selection (false)
268 {
269         init (true);
270 }
271
272 void
273 MidiRegionView::init (bool wfd)
274 {
275         PublicEditor::DropDownKeys.connect (sigc::mem_fun (*this, &MidiRegionView::drop_down_keys));
276
277         if (wfd) {
278                 Glib::Threads::Mutex::Lock lm(midi_region()->midi_source(0)->mutex());
279                 midi_region()->midi_source(0)->load_model(lm);
280         }
281
282         _model = midi_region()->midi_source(0)->model();
283         _enable_display = false;
284         fill_color_name = "midi frame base";
285
286         RegionView::init (false);
287
288         //set_height (trackview.current_height());
289
290         region_muted ();
291         region_sync_changed ();
292         region_resized (ARDOUR::bounds_change);
293         //region_locked ();
294
295         set_colors ();
296
297         _enable_display = true;
298         if (_model) {
299                 if (wfd) {
300                         display_model (_model);
301                 }
302         }
303
304         reset_width_dependent_items (_pixel_width);
305
306         group->raise_to_top();
307
308         midi_view()->midi_track()->playback_filter().ChannelModeChanged.connect (_channel_mode_changed_connection, invalidator (*this),
309                                                                        boost::bind (&MidiRegionView::midi_channel_mode_changed, this),
310                                                                        gui_context ());
311
312         instrument_info().Changed.connect (_instrument_changed_connection, invalidator (*this),
313                                            boost::bind (&MidiRegionView::instrument_settings_changed, this), gui_context());
314
315         trackview.editor().SnapChanged.connect(snap_changed_connection, invalidator(*this),
316                                                boost::bind (&MidiRegionView::snap_changed, this),
317                                                gui_context());
318
319         trackview.editor().MouseModeChanged.connect(_mouse_mode_connection, invalidator (*this),
320                                                     boost::bind (&MidiRegionView::mouse_mode_changed, this),
321                                                     gui_context ());
322
323         Config->ParameterChanged.connect (*this, invalidator (*this), boost::bind (&MidiRegionView::parameter_changed, this, _1), gui_context());
324         UIConfiguration::instance().ParameterChanged.connect (sigc::mem_fun (*this, &MidiRegionView::parameter_changed));
325         connect_to_diskstream ();
326 }
327
328 InstrumentInfo&
329 MidiRegionView::instrument_info () const
330 {
331         RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
332         return route_ui->route()->instrument_info();
333 }
334
335 const boost::shared_ptr<ARDOUR::MidiRegion>
336 MidiRegionView::midi_region() const
337 {
338         return boost::dynamic_pointer_cast<ARDOUR::MidiRegion>(_region);
339 }
340
341 void
342 MidiRegionView::connect_to_diskstream ()
343 {
344         midi_view()->midi_track()->DataRecorded.connect(
345                 *this, invalidator(*this),
346                 boost::bind (&MidiRegionView::data_recorded, this, _1),
347                 gui_context());
348 }
349
350 bool
351 MidiRegionView::canvas_group_event(GdkEvent* ev)
352 {
353         if (in_destructor || _recregion) {
354                 return false;
355         }
356
357         if (!trackview.editor().internal_editing()) {
358                 // not in internal edit mode, so just act like a normal region
359                 return RegionView::canvas_group_event (ev);
360         }
361
362         //For now, move the snapped cursor aside so it doesn't bother you during internal editing
363         //trackview.editor().set_snapped_cursor_position(_region->position());
364
365         bool r;
366
367         switch (ev->type) {
368         case GDK_ENTER_NOTIFY:
369                 _last_event_x = ev->crossing.x;
370                 _last_event_y = ev->crossing.y;
371                 enter_notify(&ev->crossing);
372                 // set entered_regionview (among other things)
373                 return RegionView::canvas_group_event (ev);
374
375         case GDK_LEAVE_NOTIFY:
376                 _last_event_x = ev->crossing.x;
377                 _last_event_y = ev->crossing.y;
378                 leave_notify(&ev->crossing);
379                 // reset entered_regionview (among other things)
380                 return RegionView::canvas_group_event (ev);
381
382         case GDK_SCROLL:
383                 if (scroll (&ev->scroll)) {
384                         return true;
385                 }
386                 break;
387
388         case GDK_KEY_PRESS:
389                 return key_press (&ev->key);
390
391         case GDK_KEY_RELEASE:
392                 return key_release (&ev->key);
393
394         case GDK_BUTTON_PRESS:
395                 return button_press (&ev->button);
396
397         case GDK_BUTTON_RELEASE:
398                 r = button_release (&ev->button);
399                 return r;
400
401         case GDK_MOTION_NOTIFY:
402                 _last_event_x = ev->motion.x;
403                 _last_event_y = ev->motion.y;
404                 return motion (&ev->motion);
405
406         default:
407                 break;
408         }
409
410         return RegionView::canvas_group_event (ev);
411 }
412
413 bool
414 MidiRegionView::enter_notify (GdkEventCrossing* ev)
415 {
416         enter_internal (ev->state);
417
418         _entered = true;
419         return false;
420 }
421
422 bool
423 MidiRegionView::leave_notify (GdkEventCrossing*)
424 {
425         leave_internal();
426
427         _entered = false;
428         return false;
429 }
430
431 void
432 MidiRegionView::mouse_mode_changed ()
433 {
434         // Adjust frame colour (become more transparent for internal tools)
435         set_frame_color();
436
437         if (_entered) {
438                 if (!trackview.editor().internal_editing()) {
439                         /* Switched out of internal editing mode while entered.
440                         Only necessary for leave as a mouse_mode_change over a region
441                         automatically triggers an enter event. */
442                         leave_internal();
443                 }
444                 else if (trackview.editor().current_mouse_mode() == MouseContent) {
445                         // hide cursor and ghost note after changing to internal edit mode
446                         remove_ghost_note ();
447
448                         /* XXX This is problematic as the function is executed for every region
449                         and only for one region _entered_note can be true. Still it's
450                         necessary as to hide the verbose cursor when we're changing from
451                         draw mode to internal edit mode. These lines are the reason why
452                         in some situations no verbose cursor is shown when we enter internal
453                         edit mode over a note. */
454                         if (!_entered_note) {
455                                 hide_verbose_cursor ();
456                         }
457                 }
458         }
459 }
460
461 void
462 MidiRegionView::enter_internal (uint32_t state)
463 {
464         if (trackview.editor().current_mouse_mode() == MouseDraw && _mouse_state != AddDragging) {
465                 // Show ghost note under pencil
466                 create_ghost_note(_last_event_x, _last_event_y, state);
467         }
468
469         if (!_selection.empty()) {
470                 // Grab keyboard for moving selected notes with arrow keys
471                 Keyboard::magic_widget_grab_focus();
472                 _grabbed_keyboard = true;
473         }
474
475         // Lower frame handles below notes so they don't steal events
476         if (frame_handle_start) {
477                 frame_handle_start->lower_to_bottom();
478         }
479         if (frame_handle_end) {
480                 frame_handle_end->lower_to_bottom();
481         }
482 }
483
484 void
485 MidiRegionView::leave_internal()
486 {
487         hide_verbose_cursor ();
488         remove_ghost_note ();
489         _entered_note = 0;
490
491         if (_grabbed_keyboard) {
492                 Keyboard::magic_widget_drop_focus();
493                 _grabbed_keyboard = false;
494         }
495
496         // Raise frame handles above notes so they catch events
497         if (frame_handle_start) {
498                 frame_handle_start->raise_to_top();
499         }
500         if (frame_handle_end) {
501                 frame_handle_end->raise_to_top();
502         }
503 }
504
505 bool
506 MidiRegionView::button_press (GdkEventButton* ev)
507 {
508         if (ev->button != 1) {
509                 return false;
510         }
511
512         Editor* editor = dynamic_cast<Editor *> (&trackview.editor());
513         MouseMode m = editor->current_mouse_mode();
514
515         if (m == MouseContent && Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier())) {
516                 _press_cursor_ctx = CursorContext::create(*editor, editor->cursors()->midi_pencil);
517         }
518
519         if (_mouse_state != SelectTouchDragging) {
520
521                 _pressed_button = ev->button;
522
523                 if (m == MouseDraw || (m == MouseContent && Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier()))) {
524
525                         if (midi_view()->note_mode() == Percussive) {
526                                 editor->drags()->set (new HitCreateDrag (dynamic_cast<Editor *> (editor), group, this), (GdkEvent *) ev);
527                         } else {
528                                 editor->drags()->set (new NoteCreateDrag (dynamic_cast<Editor *> (editor), group, this), (GdkEvent *) ev);
529                         }
530
531                         _mouse_state = AddDragging;
532                         remove_ghost_note ();
533                         hide_verbose_cursor ();
534                 } else {
535                         _mouse_state = Pressed;
536                 }
537
538                 return true;
539         }
540
541         _pressed_button = ev->button;
542         _mouse_changed_selection = false;
543
544         return true;
545 }
546
547 bool
548 MidiRegionView::button_release (GdkEventButton* ev)
549 {
550         double event_x, event_y;
551
552         if (ev->button != 1) {
553                 return false;
554         }
555
556         event_x = ev->x;
557         event_y = ev->y;
558
559         group->canvas_to_item (event_x, event_y);
560         group->ungrab ();
561
562         PublicEditor& editor = trackview.editor ();
563
564         _press_cursor_ctx.reset();
565
566         switch (_mouse_state) {
567         case Pressed: // Clicked
568
569                 switch (editor.current_mouse_mode()) {
570                 case MouseRange:
571                         /* no motion occurred - simple click */
572                         clear_editor_note_selection ();
573                         _mouse_changed_selection = true;
574                         break;
575
576                 case MouseContent:
577                 case MouseTimeFX:
578                         {
579                                 _mouse_changed_selection = true;
580                                 clear_editor_note_selection ();
581
582                                 break;
583                         }
584                 case MouseDraw:
585                         break;
586
587                 default:
588                         break;
589                 }
590
591                 _mouse_state = None;
592                 break;
593
594         case AddDragging:
595                 /* Don't a ghost note when we added a note - wait until motion to avoid visual confusion.
596                    we don't want one when we were drag-selecting either. */
597         case SelectRectDragging:
598                 editor.drags()->end_grab ((GdkEvent *) ev);
599                 _mouse_state = None;
600                 break;
601
602
603         default:
604                 break;
605         }
606
607         if (_mouse_changed_selection) {
608                 trackview.editor().begin_reversible_selection_op (X_("Mouse Selection Change"));
609                 trackview.editor().commit_reversible_selection_op ();
610         }
611
612         return false;
613 }
614
615 bool
616 MidiRegionView::motion (GdkEventMotion* ev)
617 {
618         PublicEditor& editor = trackview.editor ();
619
620         if (!_entered_note) {
621
622                 if (_mouse_state == AddDragging) {
623                         if (_ghost_note) {
624                                 remove_ghost_note ();
625                         }
626
627                 } else if (!_ghost_note && editor.current_mouse_mode() == MouseContent &&
628                     Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier()) &&
629                     _mouse_state != AddDragging) {
630
631                         create_ghost_note (ev->x, ev->y, ev->state);
632
633                 } else if (_ghost_note && editor.current_mouse_mode() == MouseContent &&
634                            Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier())) {
635
636                         update_ghost_note (ev->x, ev->y, ev->state);
637
638                 } else if (_ghost_note && editor.current_mouse_mode() == MouseContent) {
639
640                         remove_ghost_note ();
641                         hide_verbose_cursor ();
642
643                 } else if (editor.current_mouse_mode() == MouseDraw) {
644
645                         if (_ghost_note) {
646                                 update_ghost_note (ev->x, ev->y, ev->state);
647                         }
648                         else {
649                                 create_ghost_note (ev->x, ev->y, ev->state);
650                         }
651                 }
652         }
653
654         /* any motion immediately hides velocity text that may have been visible */
655
656         for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
657                 (*i)->hide_velocity ();
658         }
659
660         switch (_mouse_state) {
661         case Pressed:
662
663                 if (_pressed_button == 1) {
664
665                         MouseMode m = editor.current_mouse_mode();
666
667                         if (m == MouseContent && !Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier())) {
668                                 editor.drags()->set (new MidiRubberbandSelectDrag (dynamic_cast<Editor *> (&editor), this), (GdkEvent *) ev);
669                                 if (!Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) {
670                                         clear_editor_note_selection ();
671                                         _mouse_changed_selection = true;
672                                 }
673                                 _mouse_state = SelectRectDragging;
674                                 return true;
675                         } else if (m == MouseRange) {
676                                 editor.drags()->set (new MidiVerticalSelectDrag (dynamic_cast<Editor *> (&editor), this), (GdkEvent *) ev);
677                                 _mouse_state = SelectVerticalDragging;
678                                 return true;
679                         }
680                 }
681
682                 return false;
683
684         case SelectRectDragging:
685         case SelectVerticalDragging:
686         case AddDragging:
687                 editor.drags()->motion_handler ((GdkEvent *) ev, false);
688                 break;
689
690         case SelectTouchDragging:
691                 return false;
692
693         default:
694                 break;
695
696         }
697
698         //let RegionView do it's thing.  drags are handled in here
699         return RegionView::canvas_group_event ((GdkEvent *) ev);
700 }
701
702
703 bool
704 MidiRegionView::scroll (GdkEventScroll* ev)
705 {
706         if (trackview.editor().drags()->active()) {
707                 return false;
708         }
709         if (_selection.empty()) {
710                 return false;
711         }
712
713         if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier) ||
714             Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) {
715                 /* XXX: bit of a hack; allow PrimaryModifier and TertiaryModifier scroll
716                  * through so that it still works for navigation.
717                 */
718                 return false;
719         }
720
721         hide_verbose_cursor ();
722
723         bool fine = !Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
724         Keyboard::ModifierMask mask_together(Keyboard::PrimaryModifier|Keyboard::TertiaryModifier);
725         bool together = Keyboard::modifier_state_contains (ev->state, mask_together);
726
727         if (ev->direction == GDK_SCROLL_UP) {
728                 change_velocities (true, fine, false, together);
729         } else if (ev->direction == GDK_SCROLL_DOWN) {
730                 change_velocities (false, fine, false, together);
731         } else {
732                 /* left, right: we don't use them */
733                 return false;
734         }
735
736         return true;
737 }
738
739 bool
740 MidiRegionView::key_press (GdkEventKey* ev)
741 {
742         /* since GTK bindings are generally activated on press, and since
743            detectable auto-repeat is the name of the game and only sends
744            repeated presses, carry out key actions at key press, not release.
745         */
746         bool unmodified = Keyboard::no_modifier_keys_pressed (ev);
747
748         if (unmodified && (ev->keyval == GDK_Alt_L || ev->keyval == GDK_Alt_R)) {
749
750                 if (_mouse_state != AddDragging) {
751                         _mouse_state = SelectTouchDragging;
752                 }
753
754                 return true;
755
756         } else if (ev->keyval == GDK_Escape && unmodified) {
757                 clear_editor_note_selection ();
758                 _mouse_state = None;
759
760         } else if (ev->keyval == GDK_comma || ev->keyval == GDK_period) {
761
762                 bool start = (ev->keyval == GDK_comma);
763                 bool end = (ev->keyval == GDK_period);
764                 bool shorter = Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier);
765                 bool fine = Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
766
767                 change_note_lengths (fine, shorter, Temporal::Beats(), start, end);
768
769                 return true;
770
771         } else if ((ev->keyval == GDK_BackSpace || ev->keyval == GDK_Delete) && unmodified) {
772
773                 if (_selection.empty()) {
774                         return false;
775                 }
776
777                 delete_selection();
778                 return true;
779
780         } else if (ev->keyval == GDK_Tab || ev->keyval == GDK_ISO_Left_Tab) {
781
782                 trackview.editor().begin_reversible_selection_op (X_("Select Adjacent Note"));
783
784                 if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) {
785                         goto_previous_note (Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier));
786                 } else {
787                         goto_next_note (Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier));
788                 }
789
790                 trackview.editor().commit_reversible_selection_op();
791
792                 return true;
793
794         } else if (ev->keyval == GDK_Up) {
795
796                 bool allow_smush = Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier);
797                 bool fine = !Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
798                 bool together = Keyboard::modifier_state_contains (ev->state, Keyboard::Level4Modifier);
799
800                 if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) {
801                         change_velocities (true, fine, allow_smush, together);
802                 } else {
803                         transpose (true, fine, allow_smush);
804                 }
805                 return true;
806
807         } else if (ev->keyval == GDK_Down) {
808
809                 bool allow_smush = Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier);
810                 bool fine = !Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
811                 bool together = Keyboard::modifier_state_contains (ev->state, Keyboard::Level4Modifier);
812
813                 if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) {
814                         change_velocities (false, fine, allow_smush, together);
815                 } else {
816                         transpose (false, fine, allow_smush);
817                 }
818                 return true;
819
820         } else if (ev->keyval == GDK_Left) {
821
822                 bool fine = !Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
823                 nudge_notes (false, fine);
824                 return true;
825
826         } else if (ev->keyval == GDK_Right) {
827
828                 bool fine = !Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
829                 nudge_notes (true, fine);
830                 return true;
831
832         } else if (ev->keyval == GDK_c && unmodified) {
833                 channel_edit ();
834                 return true;
835
836         } else if (ev->keyval == GDK_v && unmodified) {
837                 velocity_edit ();
838                 return true;
839         }
840
841         return false;
842 }
843
844 bool
845 MidiRegionView::key_release (GdkEventKey* ev)
846 {
847         if ((_mouse_state == SelectTouchDragging) && (ev->keyval == GDK_Alt_L || ev->keyval == GDK_Alt_R)) {
848                 _mouse_state = None;
849                 return true;
850         }
851         return false;
852 }
853
854 void
855 MidiRegionView::channel_edit ()
856 {
857         if (_selection.empty()) {
858                 return;
859         }
860
861         /* pick a note somewhat at random (since Selection is a set<>) to
862          * provide the "current" channel for the dialog.
863          */
864
865         uint8_t current_channel = (*_selection.begin())->note()->channel ();
866         MidiChannelDialog channel_dialog (current_channel);
867         int ret = channel_dialog.run ();
868
869         switch (ret) {
870         case Gtk::RESPONSE_OK:
871                 break;
872         default:
873                 return;
874         }
875
876         uint8_t new_channel = channel_dialog.active_channel ();
877
878         start_note_diff_command (_("channel edit"));
879
880         for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
881                 Selection::iterator next = i;
882                 ++next;
883                 change_note_channel (*i, new_channel);
884                 i = next;
885         }
886
887         apply_diff ();
888 }
889
890 void
891 MidiRegionView::velocity_edit ()
892 {
893         if (_selection.empty()) {
894                 return;
895         }
896
897         /* pick a note somewhat at random (since Selection is a set<>) to
898          * provide the "current" velocity for the dialog.
899          */
900
901         uint8_t current_velocity = (*_selection.begin())->note()->velocity ();
902         MidiVelocityDialog velocity_dialog (current_velocity);
903         int ret = velocity_dialog.run ();
904
905         switch (ret) {
906         case Gtk::RESPONSE_OK:
907                 break;
908         default:
909                 return;
910         }
911
912         uint8_t new_velocity = velocity_dialog.velocity ();
913
914         start_note_diff_command (_("velocity edit"));
915
916         for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
917                 Selection::iterator next = i;
918                 ++next;
919                 change_note_velocity (*i, new_velocity, false);
920                 i = next;
921         }
922
923         apply_diff ();
924 }
925
926 void
927 MidiRegionView::show_list_editor ()
928 {
929         if (!_list_editor) {
930                 _list_editor = new MidiListEditor (trackview.session(), midi_region(), midi_view()->midi_track());
931         }
932         _list_editor->present ();
933 }
934
935 /** Add a note to the model, and the view, at a canvas (click) coordinate.
936  * \param t time in samples relative to the position of the region
937  * \param y vertical position in pixels
938  * \param length duration of the note in beats
939  * \param snap_t true to snap t to the grid, otherwise false.
940  */
941 void
942 MidiRegionView::create_note_at (samplepos_t t, double y, Temporal::Beats length, uint32_t state, bool shift_snap)
943 {
944         if (length < 2 * DBL_EPSILON) {
945                 return;
946         }
947
948         MidiTimeAxisView* const mtv  = dynamic_cast<MidiTimeAxisView*>(&trackview);
949         MidiStreamView* const   view = mtv->midi_view();
950         boost::shared_ptr<MidiRegion> mr  = boost::dynamic_pointer_cast<MidiRegion> (_region);
951
952         if (!mr) {
953                 return;
954         }
955
956         // Start of note in samples relative to region start
957         const int32_t divisions = trackview.editor().get_grid_music_divisions (state);
958         Temporal::Beats beat_time = snap_sample_to_grid_underneath (t, divisions, shift_snap);
959
960         const double  note     = view->y_to_note(y);
961         const uint8_t chan     = mtv->get_channel_for_add();
962         const uint8_t velocity = get_velocity_for_add(beat_time);
963
964         const boost::shared_ptr<NoteType> new_note(
965                 new NoteType (chan, beat_time, length, (uint8_t)note, velocity));
966
967         if (_model->contains (new_note)) {
968                 return;
969         }
970
971         view->update_note_range(new_note->note());
972
973         start_note_diff_command(_("add note"));
974
975         note_diff_add_note (new_note, true, false);
976
977         apply_diff();
978
979         play_midi_note (new_note);
980 }
981
982 void
983 MidiRegionView::clear_events ()
984 {
985         // clear selection without signaling
986         clear_selection_internal ();
987
988         MidiGhostRegion* gr;
989         for (std::vector<GhostRegion*>::iterator g = ghosts.begin(); g != ghosts.end(); ++g) {
990                 if ((gr = dynamic_cast<MidiGhostRegion*>(*g)) != 0) {
991                         gr->clear_events();
992                 }
993         }
994
995
996         _note_group->clear (true);
997         _events.clear();
998         _patch_changes.clear();
999         _sys_exes.clear();
1000         _optimization_iterator = _events.end();
1001 }
1002
1003 void
1004 MidiRegionView::display_model(boost::shared_ptr<MidiModel> model)
1005 {
1006         _model = model;
1007
1008         content_connection.disconnect ();
1009         _model->ContentsChanged.connect (content_connection, invalidator (*this), boost::bind (&MidiRegionView::redisplay_model, this), gui_context());
1010         /* Don't signal as nobody else needs to know until selection has been altered. */
1011         clear_events();
1012
1013         if (_enable_display) {
1014                 redisplay_model();
1015         }
1016 }
1017
1018 void
1019 MidiRegionView::start_note_diff_command (string name)
1020 {
1021         if (!_note_diff_command) {
1022                 trackview.editor().begin_reversible_command (name);
1023                 _note_diff_command = _model->new_note_diff_command (name);
1024         }
1025 }
1026
1027 void
1028 MidiRegionView::note_diff_add_note (const boost::shared_ptr<NoteType> note, bool selected, bool show_velocity)
1029 {
1030         if (_note_diff_command) {
1031                 _note_diff_command->add (note);
1032         }
1033         if (selected) {
1034                 _marked_for_selection.insert(note);
1035         }
1036         if (show_velocity) {
1037                 _marked_for_velocity.insert(note);
1038         }
1039 }
1040
1041 void
1042 MidiRegionView::note_diff_remove_note (NoteBase* ev)
1043 {
1044         if (_note_diff_command && ev->note()) {
1045                 _note_diff_command->remove(ev->note());
1046         }
1047 }
1048
1049 void
1050 MidiRegionView::note_diff_add_change (NoteBase* ev,
1051                                       MidiModel::NoteDiffCommand::Property property,
1052                                       uint8_t val)
1053 {
1054         if (_note_diff_command) {
1055                 _note_diff_command->change (ev->note(), property, val);
1056         }
1057 }
1058
1059 void
1060 MidiRegionView::note_diff_add_change (NoteBase* ev,
1061                                       MidiModel::NoteDiffCommand::Property property,
1062                                       Temporal::Beats val)
1063 {
1064         if (_note_diff_command) {
1065                 _note_diff_command->change (ev->note(), property, val);
1066         }
1067 }
1068
1069 void
1070 MidiRegionView::apply_diff (bool as_subcommand, bool was_copy)
1071 {
1072         bool commit = false;
1073
1074         if (!_note_diff_command) {
1075                 return;
1076         }
1077
1078         bool add_or_remove = _note_diff_command->adds_or_removes();
1079
1080         if (!was_copy && add_or_remove) {
1081                 // Mark all selected notes for selection when model reloads
1082                 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
1083                         _marked_for_selection.insert((*i)->note());
1084                 }
1085         }
1086
1087         midi_view()->midi_track()->midi_playlist()->region_edited (_region, _note_diff_command);
1088
1089         if (as_subcommand) {
1090                 _model->apply_command_as_subcommand (*trackview.session(), _note_diff_command);
1091         } else {
1092                 _model->apply_command (*trackview.session(), _note_diff_command);
1093                 commit = true;
1094         }
1095
1096         _note_diff_command = 0;
1097
1098         if (add_or_remove) {
1099                 _marked_for_selection.clear();
1100         }
1101
1102         _marked_for_velocity.clear();
1103         if (commit) {
1104                 trackview.editor().commit_reversible_command ();
1105         }
1106 }
1107
1108 void
1109 MidiRegionView::abort_command()
1110 {
1111         delete _note_diff_command;
1112         _note_diff_command = 0;
1113         trackview.editor().abort_reversible_command();
1114         clear_editor_note_selection();
1115 }
1116
1117 NoteBase*
1118 MidiRegionView::find_canvas_note (boost::shared_ptr<NoteType> note)
1119 {
1120
1121         if (_optimization_iterator != _events.end()) {
1122                 ++_optimization_iterator;
1123         }
1124
1125         if (_optimization_iterator != _events.end() && _optimization_iterator->first == note) {
1126                 return _optimization_iterator->second;
1127         }
1128
1129         _optimization_iterator = _events.find (note);
1130         if (_optimization_iterator != _events.end()) {
1131                 return _optimization_iterator->second;
1132         }
1133
1134         return 0;
1135 }
1136
1137 /** This version finds any canvas note matching the supplied note. */
1138 NoteBase*
1139 MidiRegionView::find_canvas_note (Evoral::event_id_t id)
1140 {
1141         Events::iterator it;
1142
1143         for (it = _events.begin(); it != _events.end(); ++it) {
1144                 if (it->first->id() == id) {
1145                         return it->second;
1146                 }
1147         }
1148
1149         return 0;
1150 }
1151
1152 boost::shared_ptr<PatchChange>
1153 MidiRegionView::find_canvas_patch_change (MidiModel::PatchChangePtr p)
1154 {
1155         PatchChanges::const_iterator f = _patch_changes.find (p);
1156
1157         if (f != _patch_changes.end()) {
1158                 return f->second;
1159         }
1160
1161         return boost::shared_ptr<PatchChange>();
1162 }
1163
1164 boost::shared_ptr<SysEx>
1165 MidiRegionView::find_canvas_sys_ex (MidiModel::SysExPtr s)
1166 {
1167         SysExes::const_iterator f = _sys_exes.find (s);
1168
1169         if (f != _sys_exes.end()) {
1170                 return f->second;
1171         }
1172
1173         return boost::shared_ptr<SysEx>();
1174 }
1175
1176 void
1177 MidiRegionView::get_events (Events& e, Evoral::Sequence<Temporal::Beats>::NoteOperator op, uint8_t val, int chan_mask)
1178 {
1179         MidiModel::Notes notes;
1180         _model->get_notes (notes, op, val, chan_mask);
1181
1182         for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
1183                 NoteBase* cne = find_canvas_note (*n);
1184                 if (cne) {
1185                         e.insert (make_pair (*n, cne));
1186                 }
1187         }
1188 }
1189
1190 void
1191 MidiRegionView::redisplay_model()
1192 {
1193         if (_active_notes) {
1194                 // Currently recording
1195                 const samplecnt_t zoom = trackview.editor().get_current_zoom();
1196                 if (zoom != _last_display_zoom) {
1197                         /* Update resolved canvas notes to reflect changes in zoom without
1198                            touching model.  Leave active notes (with length 0) alone since
1199                            they are being extended. */
1200                         for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
1201                                 if (i->second->note()->length() > 0) {
1202                                         update_note(i->second);
1203                                 }
1204                         }
1205                         _last_display_zoom = zoom;
1206                 }
1207                 return;
1208         }
1209
1210         if (!_model) {
1211                 return;
1212         }
1213
1214         for (_optimization_iterator = _events.begin(); _optimization_iterator != _events.end(); ++_optimization_iterator) {
1215                 _optimization_iterator->second->invalidate();
1216         }
1217
1218         bool empty_when_starting = _events.empty();
1219         _optimization_iterator = _events.begin();
1220         MidiModel::Notes missing_notes;
1221         Note* sus = NULL;
1222         Hit*  hit = NULL;
1223
1224         MidiModel::ReadLock lock(_model->read_lock());
1225         MidiModel::Notes& notes (_model->notes());
1226
1227         NoteBase* cne;
1228         for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
1229
1230                 boost::shared_ptr<NoteType> note (*n);
1231                 bool visible;
1232
1233                 if (note_in_region_range (note, visible)) {
1234                         if (!empty_when_starting && (cne = find_canvas_note (note)) != 0) {
1235                                 cne->validate ();
1236                                 if (visible) {
1237                                         cne->show ();
1238                                 } else {
1239                                         cne->hide ();
1240                                 }
1241                         } else {
1242                                 missing_notes.insert (note);
1243                         }
1244                 }
1245         }
1246
1247         if (!empty_when_starting) {
1248                 MidiModel::Notes::iterator f;
1249                 for (Events::iterator i = _events.begin(); i != _events.end(); ) {
1250
1251                         NoteBase* cne = i->second;
1252
1253                         /* remove note items that are no longer valid */
1254                         if (!cne->valid()) {
1255
1256                                 for (vector<GhostRegion*>::iterator j = ghosts.begin(); j != ghosts.end(); ++j) {
1257                                         MidiGhostRegion* gr = dynamic_cast<MidiGhostRegion*> (*j);
1258                                         if (gr) {
1259                                                 gr->remove_note (cne);
1260                                         }
1261                                 }
1262
1263                                 delete cne;
1264                                 i = _events.erase (i);
1265
1266                         } else {
1267                                 bool visible = cne->item()->visible();
1268
1269                                 if ((sus = dynamic_cast<Note*>(cne))) {
1270
1271                                         if (visible) {
1272                                                 update_sustained (sus);
1273                                         }
1274
1275                                 } else if ((hit = dynamic_cast<Hit*>(cne))) {
1276
1277                                         if (visible) {
1278                                                 update_hit (hit);
1279                                         }
1280
1281                                 }
1282                                 ++i;
1283                         }
1284                 }
1285         }
1286
1287         for (MidiModel::Notes::iterator n = missing_notes.begin(); n != missing_notes.end(); ++n) {
1288                 boost::shared_ptr<NoteType> note (*n);
1289                 NoteBase* cne;
1290                 bool visible;
1291
1292                 if (note_in_region_range (note, visible)) {
1293                         if (visible) {
1294                                 cne = add_note (note, true);
1295                         } else {
1296                                 cne = add_note (note, false);
1297                         }
1298                 } else {
1299                         cne = add_note (note, false);
1300                 }
1301
1302                 for (set<Evoral::event_id_t>::iterator it = _pending_note_selection.begin(); it != _pending_note_selection.end(); ++it) {
1303                         if ((*it) == note->id()) {
1304                                 add_to_selection (cne);
1305                         }
1306                 }
1307         }
1308
1309         for (vector<GhostRegion*>::iterator j = ghosts.begin(); j != ghosts.end(); ++j) {
1310                 MidiGhostRegion* gr = dynamic_cast<MidiGhostRegion*> (*j);
1311                 if (gr && !gr->trackview.hidden()) {
1312                         gr->redisplay_model ();
1313                 }
1314         }
1315
1316         display_sysexes();
1317         display_patch_changes ();
1318
1319         _marked_for_selection.clear ();
1320         _marked_for_velocity.clear ();
1321         _pending_note_selection.clear ();
1322
1323 }
1324
1325 void
1326 MidiRegionView::display_patch_changes ()
1327 {
1328         MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1329         uint16_t chn_mask = mtv->midi_track()->get_playback_channel_mask();
1330
1331         for (uint8_t i = 0; i < 16; ++i) {
1332                 display_patch_changes_on_channel (i, chn_mask & (1 << i));
1333         }
1334 }
1335
1336 /** @param active_channel true to display patch changes fully, false to display
1337  * them `greyed-out' (as on an inactive channel)
1338  */
1339 void
1340 MidiRegionView::display_patch_changes_on_channel (uint8_t channel, bool active_channel)
1341 {
1342         for (MidiModel::PatchChanges::const_iterator i = _model->patch_changes().begin(); i != _model->patch_changes().end(); ++i) {
1343                 boost::shared_ptr<PatchChange> p;
1344
1345                 if ((*i)->channel() != channel) {
1346                         continue;
1347                 }
1348
1349                 if ((p = find_canvas_patch_change (*i)) != 0) {
1350
1351                         const samplecnt_t region_samples = source_beats_to_region_samples ((*i)->time());
1352
1353                         if (region_samples < 0 || region_samples >= _region->length()) {
1354                                 p->hide();
1355                         } else {
1356                                 const double x = trackview.editor().sample_to_pixel (region_samples);
1357                                 const string patch_name = instrument_info().get_patch_name ((*i)->bank(), (*i)->program(), channel);
1358                                 p->canvas_item()->set_position (ArdourCanvas::Duple (x, 1.0));
1359                                 p->set_text (patch_name);
1360
1361                                 p->show();
1362                         }
1363
1364                 } else {
1365                         const string patch_name = instrument_info().get_patch_name ((*i)->bank(), (*i)->program(), channel);
1366                         add_canvas_patch_change (*i, patch_name, active_channel);
1367                 }
1368         }
1369 }
1370
1371 void
1372 MidiRegionView::display_sysexes()
1373 {
1374         bool have_periodic_system_messages = false;
1375         bool display_periodic_messages = true;
1376
1377         if (!UIConfiguration::instance().get_never_display_periodic_midi()) {
1378
1379                 for (MidiModel::SysExes::const_iterator i = _model->sysexes().begin(); i != _model->sysexes().end(); ++i) {
1380                         if ((*i)->is_spp() || (*i)->is_mtc_quarter() || (*i)->is_mtc_full()) {
1381                                 have_periodic_system_messages = true;
1382                                 break;
1383                         }
1384                 }
1385
1386                 if (have_periodic_system_messages) {
1387                         double zoom = trackview.editor().get_current_zoom (); // samples per pixel
1388
1389                         /* get an approximate value for the number of samples per video frame */
1390
1391                         double video_frame = trackview.session()->sample_rate() * (1.0/30);
1392
1393                         /* if we are zoomed out beyond than the cutoff (i.e. more
1394                          * samples per pixel than samples per 4 video frames), don't
1395                          * show periodic sysex messages.
1396                          */
1397
1398                         if (zoom > (video_frame*4)) {
1399                                 display_periodic_messages = false;
1400                         }
1401                 }
1402         } else {
1403                 display_periodic_messages = false;
1404         }
1405
1406         for (MidiModel::SysExes::const_iterator i = _model->sysexes().begin(); i != _model->sysexes().end(); ++i) {
1407                 MidiModel::SysExPtr sysex_ptr = *i;
1408                 Temporal::Beats time = sysex_ptr->time();
1409
1410                 if ((*i)->is_spp() || (*i)->is_mtc_quarter() || (*i)->is_mtc_full()) {
1411                         if (!display_periodic_messages) {
1412                                 continue;
1413                         }
1414                 }
1415
1416                 ostringstream str;
1417                 str << hex;
1418                 for (uint32_t b = 0; b < (*i)->size(); ++b) {
1419                         str << int((*i)->buffer()[b]);
1420                         if (b != (*i)->size() -1) {
1421                                 str << " ";
1422                         }
1423                 }
1424                 string text = str.str();
1425
1426                 const double x = trackview.editor().sample_to_pixel(source_beats_to_region_samples(time));
1427
1428                 double height = midi_stream_view()->contents_height();
1429
1430                 // CAIROCANVAS: no longer passing *i (the sysex event) to the
1431                 // SysEx canvas object!!!
1432                 boost::shared_ptr<SysEx> sysex = find_canvas_sys_ex (sysex_ptr);
1433
1434                 if (!sysex) {
1435                         sysex = boost::shared_ptr<SysEx>(
1436                                 new SysEx (*this, _note_group, text, height, x, 1.0, sysex_ptr));
1437                         _sys_exes.insert (make_pair (sysex_ptr, sysex));
1438                 } else {
1439                         sysex->set_height (height);
1440                         sysex->item().set_position (ArdourCanvas::Duple (x, 1.0));
1441                 }
1442
1443                 // Show unless message is beyond the region bounds
1444 // XXX REQUIRES APPROPRIATE OPERATORS FOR Temporal::Beats and samplepos? say what?
1445 #warning paul fix this
1446 //              if (time - _region->start() >= _region->length() || time < _region->start()) {
1447 //                      sysex->hide();
1448 //              } else {
1449 //                      sysex->show();
1450 //              }
1451         }
1452 }
1453
1454 MidiRegionView::~MidiRegionView ()
1455 {
1456         in_destructor = true;
1457
1458         hide_verbose_cursor ();
1459
1460         delete _list_editor;
1461
1462         RegionViewGoingAway (this); /* EMIT_SIGNAL */
1463
1464         if (_active_notes) {
1465                 end_write();
1466         }
1467         _entered_note = 0;
1468         clear_events ();
1469
1470         delete _note_group;
1471         delete _note_diff_command;
1472         delete _step_edit_cursor;
1473 }
1474
1475 void
1476 MidiRegionView::region_resized (const PropertyChange& what_changed)
1477 {
1478         RegionView::region_resized(what_changed); // calls RegionView::set_duration()
1479
1480         if (what_changed.contains (ARDOUR::Properties::position)) {
1481                 _region_relative_time_converter.set_origin_b(_region->position());
1482                 _region_relative_time_converter_double.set_origin_b(_region->position());
1483                 /* reset_width dependent_items() redisplays model */
1484
1485         }
1486
1487         if (what_changed.contains (ARDOUR::Properties::start) ||
1488             what_changed.contains (ARDOUR::Properties::position)) {
1489                 _source_relative_time_converter.set_origin_b (_region->position() - _region->start());
1490         }
1491         /* catch end and start trim so we can update the view*/
1492         if (!what_changed.contains (ARDOUR::Properties::start) &&
1493             what_changed.contains (ARDOUR::Properties::length)) {
1494                 enable_display (true);
1495         } else if (what_changed.contains (ARDOUR::Properties::start) &&
1496             what_changed.contains (ARDOUR::Properties::length)) {
1497                 enable_display (true);
1498         }
1499 }
1500
1501 void
1502 MidiRegionView::reset_width_dependent_items (double pixel_width)
1503 {
1504         RegionView::reset_width_dependent_items(pixel_width);
1505
1506         if (_enable_display) {
1507                 redisplay_model();
1508         }
1509
1510         bool hide_all = false;
1511         PatchChanges::iterator x = _patch_changes.begin();
1512         if (x != _patch_changes.end()) {
1513                 hide_all = x->second->width() >= _pixel_width;
1514         }
1515
1516         if (hide_all) {
1517                 for (; x != _patch_changes.end(); ++x) {
1518                         x->second->hide();
1519                 }
1520         }
1521
1522         move_step_edit_cursor (_step_edit_cursor_position);
1523         set_step_edit_cursor_width (_step_edit_cursor_width);
1524 }
1525
1526 void
1527 MidiRegionView::set_height (double height)
1528 {
1529         double old_height = _height;
1530         RegionView::set_height(height);
1531
1532         apply_note_range (midi_stream_view()->lowest_note(),
1533                           midi_stream_view()->highest_note(),
1534                           height != old_height);
1535
1536         if (name_text) {
1537                 name_text->raise_to_top();
1538         }
1539
1540         for (PatchChanges::iterator x = _patch_changes.begin(); x != _patch_changes.end(); ++x) {
1541                 (*x).second->set_height (midi_stream_view()->contents_height());
1542         }
1543
1544         if (_step_edit_cursor) {
1545                 _step_edit_cursor->set_y1 (midi_stream_view()->contents_height());
1546         }
1547 }
1548
1549
1550 /** Apply the current note range from the stream view
1551  * by repositioning/hiding notes as necessary
1552  */
1553 void
1554 MidiRegionView::apply_note_range (uint8_t min, uint8_t max, bool force)
1555 {
1556         if (!_enable_display) {
1557                 return;
1558         }
1559
1560         if (!force && _current_range_min == min && _current_range_max == max) {
1561                 return;
1562         }
1563
1564         _current_range_min = min;
1565         _current_range_max = max;
1566
1567         redisplay_model ();
1568 }
1569
1570 GhostRegion*
1571 MidiRegionView::add_ghost (TimeAxisView& tv)
1572 {
1573         double unit_position = _region->position () / samples_per_pixel;
1574         MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*>(&tv);
1575         MidiGhostRegion* ghost;
1576
1577         if (mtv && mtv->midi_view()) {
1578                 /* if ghost is inserted into midi track, use a dedicated midi ghost canvas group
1579                    to allow having midi notes on top of note lines and waveforms.
1580                 */
1581                 ghost = new MidiGhostRegion (*this, *mtv->midi_view(), trackview, unit_position);
1582         } else {
1583                 ghost = new MidiGhostRegion (*this, tv, trackview, unit_position);
1584         }
1585
1586         ghost->set_colors ();
1587         ghost->set_height ();
1588         ghost->set_duration (_region->length() / samples_per_pixel);
1589
1590         for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
1591                 ghost->add_note(i->second);
1592         }
1593
1594         ghosts.push_back (ghost);
1595         enable_display (true);
1596         return ghost;
1597 }
1598
1599
1600 /** Begin tracking note state for successive calls to add_event
1601  */
1602 void
1603 MidiRegionView::begin_write()
1604 {
1605         if (_active_notes) {
1606                 delete[] _active_notes;
1607         }
1608         _active_notes = new Note*[128];
1609         for (unsigned i = 0; i < 128; ++i) {
1610                 _active_notes[i] = 0;
1611         }
1612 }
1613
1614
1615 /** Destroy note state for add_event
1616  */
1617 void
1618 MidiRegionView::end_write()
1619 {
1620         delete[] _active_notes;
1621         _active_notes = 0;
1622         _marked_for_selection.clear();
1623         _marked_for_velocity.clear();
1624 }
1625
1626
1627 /** Resolve an active MIDI note (while recording).
1628  */
1629 void
1630 MidiRegionView::resolve_note(uint8_t note, Temporal::Beats end_time)
1631 {
1632         if (midi_view()->note_mode() != Sustained) {
1633                 return;
1634         }
1635
1636         if (_active_notes && _active_notes[note]) {
1637                 /* Set note length so update_note() works.  Note this is a local note
1638                    for recording, not from a model, so we can safely mess with it. */
1639                 _active_notes[note]->note()->set_length(
1640                         end_time - _active_notes[note]->note()->time());
1641
1642                 /* End time is relative to the region being recorded. */
1643                 const samplepos_t end_time_samples = region_beats_to_region_samples(end_time);
1644
1645                 _active_notes[note]->set_x1 (trackview.editor().sample_to_pixel(end_time_samples));
1646                 _active_notes[note]->set_outline_all ();
1647                 _active_notes[note] = 0;
1648         }
1649 }
1650
1651
1652 /** Extend active notes to rightmost edge of region (if length is changed)
1653  */
1654 void
1655 MidiRegionView::extend_active_notes()
1656 {
1657         if (!_active_notes) {
1658                 return;
1659         }
1660
1661         for (unsigned i = 0; i < 128; ++i) {
1662                 if (_active_notes[i]) {
1663                         _active_notes[i]->set_x1(
1664                                 trackview.editor().sample_to_pixel(_region->length()));
1665                 }
1666         }
1667 }
1668
1669 void
1670 MidiRegionView::play_midi_note(boost::shared_ptr<NoteType> note)
1671 {
1672         if (_no_sound_notes || !UIConfiguration::instance().get_sound_midi_notes()) {
1673                 return;
1674         }
1675
1676         RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
1677
1678         if (!route_ui || !route_ui->midi_track()) {
1679                 return;
1680         }
1681
1682         NotePlayer* np = new NotePlayer (route_ui->midi_track ());
1683         np->add (note);
1684         np->play ();
1685
1686         /* NotePlayer deletes itself */
1687 }
1688
1689 void
1690 MidiRegionView::start_playing_midi_note(boost::shared_ptr<NoteType> note)
1691 {
1692         const std::vector< boost::shared_ptr<NoteType> > notes(1, note);
1693         start_playing_midi_chord(notes);
1694 }
1695
1696 void
1697 MidiRegionView::start_playing_midi_chord (vector<boost::shared_ptr<NoteType> > notes)
1698 {
1699         if (_no_sound_notes || !UIConfiguration::instance().get_sound_midi_notes()) {
1700                 return;
1701         }
1702
1703         RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
1704
1705         if (!route_ui || !route_ui->midi_track()) {
1706                 return;
1707         }
1708
1709         NotePlayer* player = new NotePlayer (route_ui->midi_track());
1710
1711         for (vector<boost::shared_ptr<NoteType> >::iterator n = notes.begin(); n != notes.end(); ++n) {
1712                 player->add (*n);
1713         }
1714
1715         player->play ();
1716 }
1717
1718
1719 bool
1720 MidiRegionView::note_in_region_range (const boost::shared_ptr<NoteType> note, bool& visible) const
1721 {
1722         const boost::shared_ptr<ARDOUR::MidiRegion> midi_reg = midi_region();
1723
1724         /* must compare double explicitly as Beats::operator< rounds to ppqn */
1725         const bool outside = (note->time().to_double() < midi_reg->start_beats() ||
1726                               note->time().to_double() >= midi_reg->start_beats() + midi_reg->length_beats());
1727
1728         visible = (note->note() >= _current_range_min) &&
1729                 (note->note() <= _current_range_max);
1730
1731         return !outside;
1732 }
1733
1734 void
1735 MidiRegionView::update_note (NoteBase* note, bool update_ghost_regions)
1736 {
1737         Note* sus = NULL;
1738         Hit*  hit = NULL;
1739         if ((sus = dynamic_cast<Note*>(note))) {
1740                 update_sustained(sus, update_ghost_regions);
1741         } else if ((hit = dynamic_cast<Hit*>(note))) {
1742                 update_hit(hit, update_ghost_regions);
1743         }
1744 }
1745
1746 /** Update a canvas note's size from its model note.
1747  *  @param ev Canvas note to update.
1748  *  @param update_ghost_regions true to update the note in any ghost regions that we have, otherwise false.
1749  */
1750 void
1751 MidiRegionView::update_sustained (Note* ev, bool update_ghost_regions)
1752 {
1753         TempoMap& map (trackview.session()->tempo_map());
1754         const boost::shared_ptr<ARDOUR::MidiRegion> mr = midi_region();
1755         boost::shared_ptr<NoteType> note = ev->note();
1756
1757         const double session_source_start = _region->quarter_note() - mr->start_beats();
1758         const samplepos_t note_start_samples = map.sample_at_quarter_note (note->time().to_double() + session_source_start) - _region->position();
1759
1760         const double x0 = trackview.editor().sample_to_pixel (note_start_samples);
1761         double x1;
1762         const double y0 = 1 + floor(note_to_y(note->note()));
1763         double y1;
1764
1765         /* trim note display to not overlap the end of its region */
1766         if (note->length().to_double() > 0.0) {
1767                 double note_end_time = note->end_time().to_double();
1768
1769                 if (note_end_time > mr->start_beats() + mr->length_beats()) {
1770                         note_end_time = mr->start_beats() + mr->length_beats();
1771                 }
1772
1773                 const samplepos_t note_end_samples = map.sample_at_quarter_note (session_source_start + note_end_time) - _region->position();
1774
1775                 x1 = std::max(1., trackview.editor().sample_to_pixel (note_end_samples)) - 1;
1776         } else {
1777                 x1 = std::max(1., trackview.editor().sample_to_pixel (_region->length())) - 1;
1778         }
1779
1780         y1 = y0 + std::max(1., floor(note_height()) - 1);
1781
1782         ev->set (ArdourCanvas::Rect (x0, y0, x1, y1));
1783         ev->set_velocity (note->velocity()/127.0);
1784
1785         if (!note->length()) {
1786                 if (_active_notes && note->note() < 128) {
1787                         Note* const old_rect = _active_notes[note->note()];
1788                         if (old_rect) {
1789                                 /* There is an active note on this key, so we have a stuck
1790                                    note.  Finish the old rectangle here. */
1791                                 old_rect->set_x1 (x1);
1792                                 old_rect->set_outline_all ();
1793                         }
1794                         _active_notes[note->note()] = ev;
1795                 }
1796                 /* outline all but right edge */
1797                 ev->set_outline_what (ArdourCanvas::Rectangle::What (
1798                                               ArdourCanvas::Rectangle::TOP|
1799                                               ArdourCanvas::Rectangle::LEFT|
1800                                               ArdourCanvas::Rectangle::BOTTOM));
1801         } else {
1802                 /* outline all edges */
1803                 ev->set_outline_all ();
1804         }
1805
1806         // Update color in case velocity has changed
1807         const uint32_t base_col = ev->base_color();
1808         ev->set_fill_color(base_col);
1809         ev->set_outline_color(ev->calculate_outline(base_col, ev->selected()));
1810
1811 }
1812
1813 void
1814 MidiRegionView::update_hit (Hit* ev, bool update_ghost_regions)
1815 {
1816         boost::shared_ptr<NoteType> note = ev->note();
1817
1818         const double note_time_qn = note->time().to_double() + (_region->quarter_note() - midi_region()->start_beats());
1819         const samplepos_t note_start_samples = trackview.session()->tempo_map().sample_at_quarter_note (note_time_qn) - _region->position();
1820
1821         const double x = trackview.editor().sample_to_pixel(note_start_samples);
1822         const double diamond_size = std::max(1., floor(note_height()) - 2.);
1823         const double y = 1.5 + floor(note_to_y(note->note())) + diamond_size * .5;
1824
1825         // see DnD note in MidiRegionView::apply_note_range() above
1826         if (y <= 0 || y >= _height) {
1827                 ev->hide();
1828         } else {
1829                 ev->show();
1830         }
1831
1832         ev->set_position (ArdourCanvas::Duple (x, y));
1833         ev->set_height (diamond_size);
1834
1835         // Update color in case velocity has changed
1836         const uint32_t base_col = ev->base_color();
1837         ev->set_fill_color(base_col);
1838         ev->set_outline_color(ev->calculate_outline(base_col, ev->selected()));
1839
1840 }
1841
1842 /** Add a MIDI note to the view (with length).
1843  *
1844  * If in sustained mode, notes with length 0 will be considered active
1845  * notes, and resolve_note should be called when the corresponding note off
1846  * event arrives, to properly display the note.
1847  */
1848 NoteBase*
1849 MidiRegionView::add_note(const boost::shared_ptr<NoteType> note, bool visible)
1850 {
1851         NoteBase* event = 0;
1852
1853         if (midi_view()->note_mode() == Sustained) {
1854
1855                 Note* ev_rect = new Note (*this, _note_group, note); // XXX may leak
1856
1857                 update_sustained (ev_rect);
1858
1859                 event = ev_rect;
1860
1861         } else if (midi_view()->note_mode() == Percussive) {
1862
1863                 const double diamond_size = std::max(1., floor(note_height()) - 2.);
1864
1865                 Hit* ev_diamond = new Hit (*this, _note_group, diamond_size, note); // XXX may leak
1866
1867                 update_hit (ev_diamond);
1868
1869                 event = ev_diamond;
1870
1871         } else {
1872                 event = 0;
1873         }
1874
1875         if (event) {
1876                 MidiGhostRegion* gr;
1877
1878                 for (std::vector<GhostRegion*>::iterator g = ghosts.begin(); g != ghosts.end(); ++g) {
1879                         if ((gr = dynamic_cast<MidiGhostRegion*>(*g)) != 0) {
1880                                 gr->add_note(event);
1881                         }
1882                 }
1883
1884                 if (_marked_for_selection.find(note) != _marked_for_selection.end()) {
1885                         note_selected(event, true);
1886                 }
1887
1888                 if (_marked_for_velocity.find(note) != _marked_for_velocity.end()) {
1889                         event->show_velocity();
1890                 }
1891
1892                 event->on_channel_selection_change (get_selected_channels());
1893                 _events.insert (make_pair (event->note(), event));
1894
1895                 if (visible) {
1896                         event->show();
1897                 } else {
1898                         event->hide ();
1899                 }
1900         }
1901
1902         MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1903         MidiStreamView* const view = mtv->midi_view();
1904
1905         view->update_note_range (note->note());
1906         return event;
1907 }
1908
1909 void
1910 MidiRegionView::step_add_note (uint8_t channel, uint8_t number, uint8_t velocity,
1911                                Temporal::Beats pos, Temporal::Beats len)
1912 {
1913         boost::shared_ptr<NoteType> new_note (new NoteType (channel, pos, len, number, velocity));
1914
1915         /* potentially extend region to hold new note */
1916
1917         samplepos_t end_sample = source_beats_to_absolute_samples (new_note->end_time());
1918         samplepos_t region_end = _region->last_sample();
1919
1920         if (end_sample > region_end) {
1921                 /* XX sets length in beats from audio space. make musical */
1922                 _region->set_length (end_sample - _region->position(), 0);
1923         }
1924
1925         MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1926         MidiStreamView* const view = mtv->midi_view();
1927
1928         view->update_note_range(new_note->note());
1929
1930         _marked_for_selection.clear ();
1931
1932         start_note_diff_command (_("step add"));
1933
1934         clear_editor_note_selection ();
1935         note_diff_add_note (new_note, true, false);
1936
1937         apply_diff();
1938
1939         // last_step_edit_note = new_note;
1940 }
1941
1942 void
1943 MidiRegionView::step_sustain (Temporal::Beats beats)
1944 {
1945         change_note_lengths (false, false, beats, false, true);
1946 }
1947
1948 /** Add a new patch change flag to the canvas.
1949  * @param patch the patch change to add
1950  * @param the text to display in the flag
1951  * @param active_channel true to display the flag as on an active channel, false to grey it out for an inactive channel.
1952  */
1953 void
1954 MidiRegionView::add_canvas_patch_change (MidiModel::PatchChangePtr patch, const string& displaytext, bool /*active_channel*/)
1955 {
1956         samplecnt_t region_samples = source_beats_to_region_samples (patch->time());
1957         const double x = trackview.editor().sample_to_pixel (region_samples);
1958
1959         double const height = midi_stream_view()->contents_height();
1960
1961         // CAIROCANVAS: active_channel info removed from PatcChange constructor
1962         // so we need to do something more sophisticated to keep its color
1963         // appearance (MidiPatchChangeFill/MidiPatchChangeInactiveChannelFill)
1964         // up to date.
1965         boost::shared_ptr<PatchChange> patch_change = boost::shared_ptr<PatchChange>(
1966                 new PatchChange(*this, group,
1967                                 displaytext,
1968                                 height,
1969                                 x, 1.0,
1970                                 instrument_info(),
1971                                 patch,
1972                                 _patch_change_outline,
1973                                 _patch_change_fill)
1974                 );
1975
1976         if (patch_change->item().width() < _pixel_width) {
1977                 // Show unless patch change is beyond the region bounds
1978                 if (region_samples < 0 || region_samples >= _region->length()) {
1979                         patch_change->hide();
1980                 } else {
1981                         patch_change->show();
1982                 }
1983         } else {
1984                 patch_change->hide ();
1985         }
1986
1987         _patch_changes.insert (make_pair (patch, patch_change));
1988 }
1989
1990 void
1991 MidiRegionView::remove_canvas_patch_change (PatchChange* pc)
1992 {
1993         /* remove the canvas item */
1994         for (PatchChanges::iterator x = _patch_changes.begin(); x != _patch_changes.end(); ++x) {
1995                 if (x->second->patch() == pc->patch()) {
1996                         _patch_changes.erase (x);
1997                         break;
1998                 }
1999         }
2000 }
2001
2002 MIDI::Name::PatchPrimaryKey
2003 MidiRegionView::patch_change_to_patch_key (MidiModel::PatchChangePtr p)
2004 {
2005         return MIDI::Name::PatchPrimaryKey (p->program(), p->bank());
2006 }
2007
2008 /// Return true iff @p pc applies to the given time on the given channel.
2009 static bool
2010 patch_applies (const ARDOUR::MidiModel::constPatchChangePtr pc, Temporal::Beats time, uint8_t channel)
2011 {
2012         return pc->time() <= time && pc->channel() == channel;
2013 }
2014
2015 void
2016 MidiRegionView::get_patch_key_at (Temporal::Beats time, uint8_t channel, MIDI::Name::PatchPrimaryKey& key) const
2017 {
2018         // The earliest event not before time
2019         MidiModel::PatchChanges::iterator i = _model->patch_change_lower_bound (time);
2020
2021         // Go backwards until we find the latest PC for this channel, or the start
2022         while (i != _model->patch_changes().begin() &&
2023                (i == _model->patch_changes().end() ||
2024                 !patch_applies(*i, time, channel))) {
2025                 --i;
2026         }
2027
2028         if (i != _model->patch_changes().end() && patch_applies(*i, time, channel)) {
2029                 key.set_bank((*i)->bank());
2030                 key.set_program((*i)->program ());
2031         } else {
2032                 key.set_bank(0);
2033                 key.set_program(0);
2034         }
2035 }
2036
2037 void
2038 MidiRegionView::change_patch_change (PatchChange& pc, const MIDI::Name::PatchPrimaryKey& new_patch)
2039 {
2040         string name = _("alter patch change");
2041         trackview.editor().begin_reversible_command (name);
2042
2043         MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (name);
2044
2045         if (pc.patch()->program() != new_patch.program()) {
2046                 c->change_program (pc.patch (), new_patch.program());
2047         }
2048
2049         int const new_bank = new_patch.bank();
2050         if (pc.patch()->bank() != new_bank) {
2051                 c->change_bank (pc.patch (), new_bank);
2052         }
2053
2054         _model->apply_command (*trackview.session(), c);
2055         trackview.editor().commit_reversible_command ();
2056
2057         remove_canvas_patch_change (&pc);
2058         display_patch_changes ();
2059 }
2060
2061 void
2062 MidiRegionView::change_patch_change (MidiModel::PatchChangePtr old_change, const Evoral::PatchChange<Temporal::Beats> & new_change)
2063 {
2064         string name = _("alter patch change");
2065         trackview.editor().begin_reversible_command (name);
2066         MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (name);
2067
2068         if (old_change->time() != new_change.time()) {
2069                 c->change_time (old_change, new_change.time());
2070         }
2071
2072         if (old_change->channel() != new_change.channel()) {
2073                 c->change_channel (old_change, new_change.channel());
2074         }
2075
2076         if (old_change->program() != new_change.program()) {
2077                 c->change_program (old_change, new_change.program());
2078         }
2079
2080         if (old_change->bank() != new_change.bank()) {
2081                 c->change_bank (old_change, new_change.bank());
2082         }
2083
2084         _model->apply_command (*trackview.session(), c);
2085         trackview.editor().commit_reversible_command ();
2086
2087         for (PatchChanges::iterator x = _patch_changes.begin(); x != _patch_changes.end(); ++x) {
2088                 if (x->second->patch() == old_change) {
2089                         _patch_changes.erase (x);
2090                         break;
2091                 }
2092         }
2093
2094         display_patch_changes ();
2095 }
2096
2097 /** Add a patch change to the region.
2098  *  @param t Time in samples relative to region position
2099  *  @param patch Patch to add; time and channel are ignored (time is converted from t, and channel comes from
2100  *  MidiTimeAxisView::get_channel_for_add())
2101  */
2102 void
2103 MidiRegionView::add_patch_change (samplecnt_t t, Evoral::PatchChange<Temporal::Beats> const & patch)
2104 {
2105         string name = _("add patch change");
2106
2107         trackview.editor().begin_reversible_command (name);
2108         MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (name);
2109         c->add (MidiModel::PatchChangePtr (
2110                         new Evoral::PatchChange<Temporal::Beats> (
2111                                 absolute_samples_to_source_beats (_region->position() + t),
2112                                 patch.channel(), patch.program(), patch.bank()
2113                                 )
2114                         )
2115                 );
2116
2117         _model->apply_command (*trackview.session(), c);
2118         trackview.editor().commit_reversible_command ();
2119
2120         display_patch_changes ();
2121 }
2122
2123 void
2124 MidiRegionView::move_patch_change (PatchChange& pc, Temporal::Beats t)
2125 {
2126         trackview.editor().begin_reversible_command (_("move patch change"));
2127         MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("move patch change"));
2128         c->change_time (pc.patch (), t);
2129         _model->apply_command (*trackview.session(), c);
2130         trackview.editor().commit_reversible_command ();
2131
2132         display_patch_changes ();
2133 }
2134
2135 void
2136 MidiRegionView::delete_patch_change (PatchChange* pc)
2137 {
2138         trackview.editor().begin_reversible_command (_("delete patch change"));
2139
2140         MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("delete patch change"));
2141         c->remove (pc->patch ());
2142         _model->apply_command (*trackview.session(), c);
2143         trackview.editor().commit_reversible_command ();
2144
2145         remove_canvas_patch_change (pc);
2146         display_patch_changes ();
2147 }
2148
2149 void
2150 MidiRegionView::step_patch (PatchChange& patch, bool bank, int delta)
2151 {
2152         MIDI::Name::PatchPrimaryKey key = patch_change_to_patch_key(patch.patch());
2153         if (bank) {
2154                 key.set_bank(key.bank() + delta);
2155         } else {
2156                 key.set_program(key.program() + delta);
2157         }
2158         change_patch_change(patch, key);
2159 }
2160
2161 void
2162 MidiRegionView::note_deleted (NoteBase* cne)
2163 {
2164         if (_entered_note && cne == _entered_note) {
2165                 _entered_note = 0;
2166         }
2167
2168         if (_selection.empty()) {
2169                 return;
2170         }
2171
2172         _selection.erase (cne);
2173 }
2174
2175 void
2176 MidiRegionView::delete_selection()
2177 {
2178         if (_selection.empty()) {
2179                 return;
2180         }
2181
2182         if (trackview.editor().drags()->active()) {
2183                 return;
2184         }
2185
2186         start_note_diff_command (_("delete selection"));
2187
2188         for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2189                 if ((*i)->selected()) {
2190                         _note_diff_command->remove((*i)->note());
2191                 }
2192         }
2193
2194         _selection.clear();
2195
2196         apply_diff ();
2197
2198         hide_verbose_cursor ();
2199 }
2200
2201 void
2202 MidiRegionView::delete_note (boost::shared_ptr<NoteType> n)
2203 {
2204         start_note_diff_command (_("delete note"));
2205         _note_diff_command->remove (n);
2206         apply_diff ();
2207
2208         hide_verbose_cursor ();
2209 }
2210
2211 void
2212 MidiRegionView::clear_editor_note_selection ()
2213 {
2214         DEBUG_TRACE(DEBUG::Selection, "MRV::clear_editor_note_selection\n");
2215         PublicEditor& editor(trackview.editor());
2216         editor.get_selection().clear_midi_notes();
2217 }
2218
2219 void
2220 MidiRegionView::clear_selection ()
2221 {
2222         clear_selection_internal();
2223         PublicEditor& editor(trackview.editor());
2224         editor.get_selection().remove(this);
2225 }
2226
2227 void
2228 MidiRegionView::clear_selection_internal ()
2229 {
2230         DEBUG_TRACE(DEBUG::Selection, "MRV::clear_selection_internal\n");
2231
2232         for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2233                 (*i)->set_selected(false);
2234                 (*i)->hide_velocity();
2235         }
2236         _selection.clear();
2237
2238         if (_entered) {
2239                 // Clearing selection entirely, ungrab keyboard
2240                 Keyboard::magic_widget_drop_focus();
2241                 _grabbed_keyboard = false;
2242         }
2243 }
2244
2245 void
2246 MidiRegionView::unique_select(NoteBase* ev)
2247 {
2248         clear_editor_note_selection();
2249         add_to_selection(ev);
2250 }
2251
2252 void
2253 MidiRegionView::select_all_notes ()
2254 {
2255         clear_editor_note_selection ();
2256
2257         for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2258                 add_to_selection (i->second);
2259         }
2260 }
2261
2262 void
2263 MidiRegionView::select_range (samplepos_t start, samplepos_t end)
2264 {
2265         clear_editor_note_selection ();
2266
2267         for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2268                 samplepos_t t = source_beats_to_absolute_samples(i->first->time());
2269                 if (t >= start && t <= end) {
2270                         add_to_selection (i->second);
2271                 }
2272         }
2273 }
2274
2275 void
2276 MidiRegionView::invert_selection ()
2277 {
2278         for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2279                 if (i->second->selected()) {
2280                         remove_from_selection(i->second);
2281                 } else {
2282                         add_to_selection (i->second);
2283                 }
2284         }
2285 }
2286
2287 /** Used for selection undo/redo.
2288     The requested notes most likely won't exist in the view until the next model redisplay.
2289 */
2290 void
2291 MidiRegionView::select_notes (list<Evoral::event_id_t> notes)
2292 {
2293         NoteBase* cne;
2294         list<Evoral::event_id_t>::iterator n;
2295
2296         for (n = notes.begin(); n != notes.end(); ++n) {
2297                 if ((cne = find_canvas_note(*n)) != 0) {
2298                         add_to_selection (cne);
2299                 } else {
2300                         _pending_note_selection.insert(*n);
2301                 }
2302         }
2303 }
2304
2305 void
2306 MidiRegionView::select_matching_notes (uint8_t notenum, uint16_t channel_mask, bool add, bool extend)
2307 {
2308         bool have_selection = !_selection.empty();
2309         uint8_t low_note = 127;
2310         uint8_t high_note = 0;
2311         MidiModel::Notes& notes (_model->notes());
2312         _optimization_iterator = _events.begin();
2313
2314         if (extend && !have_selection) {
2315                 extend = false;
2316         }
2317
2318         /* scan existing selection to get note range */
2319
2320         for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2321                 if ((*i)->note()->note() < low_note) {
2322                         low_note = (*i)->note()->note();
2323                 }
2324                 if ((*i)->note()->note() > high_note) {
2325                         high_note = (*i)->note()->note();
2326                 }
2327         }
2328
2329         if (!add) {
2330                 clear_editor_note_selection ();
2331
2332                 if (!extend && (low_note == high_note) && (high_note == notenum)) {
2333                         /* only note previously selected is the one we are
2334                          * reselecting. treat this as cancelling the selection.
2335                          */
2336                         return;
2337                 }
2338         }
2339
2340         if (extend) {
2341                 low_note = min (low_note, notenum);
2342                 high_note = max (high_note, notenum);
2343         }
2344
2345         _no_sound_notes = true;
2346
2347         for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
2348
2349                 boost::shared_ptr<NoteType> note (*n);
2350                 NoteBase* cne;
2351                 bool select = false;
2352
2353                 if (((1 << note->channel()) & channel_mask) != 0) {
2354                         if (extend) {
2355                                 if ((note->note() >= low_note && note->note() <= high_note)) {
2356                                         select = true;
2357                                 }
2358                         } else if (note->note() == notenum) {
2359                                 select = true;
2360                         }
2361                 }
2362
2363                 if (select) {
2364                         if ((cne = find_canvas_note (note)) != 0) {
2365                                 // extend is false because we've taken care of it,
2366                                 // since it extends by time range, not pitch.
2367                                 note_selected (cne, add, false);
2368                         }
2369                 }
2370
2371                 add = true; // we need to add all remaining matching notes, even if the passed in value was false (for "set")
2372
2373         }
2374
2375         _no_sound_notes = false;
2376 }
2377
2378 void
2379 MidiRegionView::toggle_matching_notes (uint8_t notenum, uint16_t channel_mask)
2380 {
2381         MidiModel::Notes& notes (_model->notes());
2382         _optimization_iterator = _events.begin();
2383
2384         for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
2385
2386                 boost::shared_ptr<NoteType> note (*n);
2387                 NoteBase* cne;
2388
2389                 if (note->note() == notenum && (((0x0001 << note->channel()) & channel_mask) != 0)) {
2390                         if ((cne = find_canvas_note (note)) != 0) {
2391                                 if (cne->selected()) {
2392                                         note_deselected (cne);
2393                                 } else {
2394                                         note_selected (cne, true, false);
2395                                 }
2396                         }
2397                 }
2398         }
2399 }
2400
2401 void
2402 MidiRegionView::note_selected (NoteBase* ev, bool add, bool extend)
2403 {
2404         if (!add) {
2405                 clear_editor_note_selection();
2406                 add_to_selection (ev);
2407         }
2408
2409         if (!extend) {
2410
2411                 if (!ev->selected()) {
2412                         add_to_selection (ev);
2413                 }
2414
2415         } else {
2416                 /* find end of latest note selected, select all between that and the start of "ev" */
2417
2418                 Temporal::Beats earliest = std::numeric_limits<Temporal::Beats>::max();
2419                 Temporal::Beats latest   = Temporal::Beats();
2420
2421                 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2422                         if ((*i)->note()->end_time() > latest) {
2423                                 latest = (*i)->note()->end_time();
2424                         }
2425                         if ((*i)->note()->time() < earliest) {
2426                                 earliest = (*i)->note()->time();
2427                         }
2428                 }
2429
2430                 if (ev->note()->end_time() > latest) {
2431                         latest = ev->note()->end_time();
2432                 }
2433
2434                 if (ev->note()->time() < earliest) {
2435                         earliest = ev->note()->time();
2436                 }
2437
2438                 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2439
2440                         /* find notes entirely within OR spanning the earliest..latest range */
2441
2442                         if ((i->first->time() >= earliest && i->first->end_time() <= latest) ||
2443                             (i->first->time() <= earliest && i->first->end_time() >= latest)) {
2444                                 add_to_selection (i->second);
2445                         }
2446                 }
2447         }
2448 }
2449
2450 void
2451 MidiRegionView::note_deselected(NoteBase* ev)
2452 {
2453         remove_from_selection (ev);
2454 }
2455
2456 void
2457 MidiRegionView::update_drag_selection(samplepos_t start, samplepos_t end, double gy0, double gy1, bool extend)
2458 {
2459         PublicEditor& editor = trackview.editor();
2460
2461         // Convert to local coordinates
2462         const samplepos_t p  = _region->position();
2463         const double     y  = midi_view()->y_position();
2464         const double     x0 = editor.sample_to_pixel(max((samplepos_t)0, start - p));
2465         const double     x1 = editor.sample_to_pixel(max((samplepos_t)0, end - p));
2466         const double     y0 = max(0.0, gy0 - y);
2467         const double     y1 = max(0.0, gy1 - y);
2468
2469         // TODO: Make this faster by storing the last updated selection rect, and only
2470         // adjusting things that are in the area that appears/disappeared.
2471         // We probably need a tree to be able to find events in O(log(n)) time.
2472
2473         for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2474                 if (i->second->x0() < x1 && i->second->x1() > x0 && i->second->y0() < y1 && i->second->y1() > y0) {
2475                         // Rectangles intersect
2476                         if (!i->second->selected()) {
2477                                 add_to_selection (i->second);
2478                         }
2479                 } else if (i->second->selected() && !extend) {
2480                         // Rectangles do not intersect
2481                         remove_from_selection (i->second);
2482                 }
2483         }
2484
2485         typedef RouteTimeAxisView::AutomationTracks ATracks;
2486         typedef std::list<Selectable*>              Selectables;
2487
2488         /* Add control points to selection. */
2489         const ATracks& atracks = midi_view()->automation_tracks();
2490         Selectables    selectables;
2491         editor.get_selection().clear_points();
2492         for (ATracks::const_iterator a = atracks.begin(); a != atracks.end(); ++a) {
2493                 a->second->get_selectables(start, end, gy0, gy1, selectables);
2494                 for (Selectables::const_iterator s = selectables.begin(); s != selectables.end(); ++s) {
2495                         ControlPoint* cp = dynamic_cast<ControlPoint*>(*s);
2496                         if (cp) {
2497                                 editor.get_selection().add(cp);
2498                         }
2499                 }
2500                 a->second->set_selected_points(editor.get_selection().points);
2501         }
2502 }
2503
2504 void
2505 MidiRegionView::update_vertical_drag_selection (double y1, double y2, bool extend)
2506 {
2507         if (y1 > y2) {
2508                 swap (y1, y2);
2509         }
2510
2511         // TODO: Make this faster by storing the last updated selection rect, and only
2512         // adjusting things that are in the area that appears/disappeared.
2513         // We probably need a tree to be able to find events in O(log(n)) time.
2514
2515         for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2516                 if ((i->second->y1() >= y1 && i->second->y1() <= y2)) {
2517                         // within y- (note-) range
2518                         if (!i->second->selected()) {
2519                                 add_to_selection (i->second);
2520                         }
2521                 } else if (i->second->selected() && !extend) {
2522                         remove_from_selection (i->second);
2523                 }
2524         }
2525 }
2526
2527 void
2528 MidiRegionView::remove_from_selection (NoteBase* ev)
2529 {
2530         Selection::iterator i = _selection.find (ev);
2531
2532         if (i != _selection.end()) {
2533                 _selection.erase (i);
2534                 if (_selection.empty() && _grabbed_keyboard) {
2535                         // Ungrab keyboard
2536                         Keyboard::magic_widget_drop_focus();
2537                         _grabbed_keyboard = false;
2538                 }
2539         }
2540
2541         ev->set_selected (false);
2542         ev->hide_velocity ();
2543
2544         if (_selection.empty()) {
2545                 PublicEditor& editor (trackview.editor());
2546                 editor.get_selection().remove (this);
2547         }
2548 }
2549
2550 void
2551 MidiRegionView::add_to_selection (NoteBase* ev)
2552 {
2553         const bool selection_was_empty = _selection.empty();
2554
2555         if (_selection.insert (ev).second) {
2556                 ev->set_selected (true);
2557                 start_playing_midi_note ((ev)->note());
2558                 if (selection_was_empty && _entered) {
2559                         // Grab keyboard for moving notes with arrow keys
2560                         Keyboard::magic_widget_grab_focus();
2561                         _grabbed_keyboard = true;
2562                 }
2563         }
2564
2565         if (selection_was_empty) {
2566                 PublicEditor& editor (trackview.editor());
2567                 editor.get_selection().add (this);
2568         }
2569 }
2570
2571 Temporal::Beats
2572 MidiRegionView::earliest_in_selection ()
2573 {
2574         Temporal::Beats earliest = std::numeric_limits<Temporal::Beats>::max();
2575
2576         for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2577                 if ((*i)->note()->time() < earliest) {
2578                         earliest = (*i)->note()->time();
2579                 }
2580         }
2581
2582         return earliest;
2583 }
2584
2585 void
2586 MidiRegionView::move_selection(double dx_qn, double dy, double cumulative_dy)
2587 {
2588         typedef vector<boost::shared_ptr<NoteType> > PossibleChord;
2589         Editor* editor = dynamic_cast<Editor*> (&trackview.editor());
2590         TempoMap& tmap (editor->session()->tempo_map());
2591         PossibleChord to_play;
2592         Temporal::Beats earliest = earliest_in_selection();
2593
2594         for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2595                 NoteBase* n = *i;
2596                 if (n->note()->time() == earliest) {
2597                         to_play.push_back (n->note());
2598                 }
2599                 double const note_time_qn = session_relative_qn (n->note()->time().to_double());
2600                 double dx = 0.0;
2601                 if (midi_view()->note_mode() == Sustained) {
2602                         dx = editor->sample_to_pixel_unrounded (tmap.sample_at_quarter_note (note_time_qn + dx_qn))
2603                                 - n->item()->item_to_canvas (ArdourCanvas::Duple (n->x0(), 0)).x;
2604                 } else {
2605                         /* Hit::x0() is offset by _position.x, unlike Note::x0() */
2606                         Hit* hit = dynamic_cast<Hit*>(n);
2607                         if (hit) {
2608                                 dx = editor->sample_to_pixel_unrounded (tmap.sample_at_quarter_note (note_time_qn + dx_qn))
2609                                         - n->item()->item_to_canvas (ArdourCanvas::Duple (((hit->x0() + hit->x1()) / 2.0) - hit->position().x, 0)).x;
2610                         }
2611                 }
2612
2613                 (*i)->move_event(dx, dy);
2614
2615                 /* update length */
2616                 if (midi_view()->note_mode() == Sustained) {
2617                         Note* sus = dynamic_cast<Note*> (*i);
2618                         double const len_dx = editor->sample_to_pixel_unrounded (
2619                                 tmap.sample_at_quarter_note (note_time_qn + dx_qn + n->note()->length().to_double()));
2620
2621                         sus->set_x1 (n->item()->canvas_to_item (ArdourCanvas::Duple (len_dx, 0)).x);
2622                 }
2623         }
2624
2625         if (dy && !_selection.empty() && !_no_sound_notes && UIConfiguration::instance().get_sound_midi_notes()) {
2626
2627                 if (to_play.size() > 1) {
2628
2629                         PossibleChord shifted;
2630
2631                         for (PossibleChord::iterator n = to_play.begin(); n != to_play.end(); ++n) {
2632                                 boost::shared_ptr<NoteType> moved_note (new NoteType (**n));
2633                                 moved_note->set_note (moved_note->note() + cumulative_dy);
2634                                 shifted.push_back (moved_note);
2635                         }
2636
2637                         start_playing_midi_chord (shifted);
2638
2639                 } else if (!to_play.empty()) {
2640
2641                         boost::shared_ptr<NoteType> moved_note (new NoteType (*to_play.front()));
2642                         moved_note->set_note (moved_note->note() + cumulative_dy);
2643                         start_playing_midi_note (moved_note);
2644                 }
2645         }
2646 }
2647
2648 NoteBase*
2649 MidiRegionView::copy_selection (NoteBase* primary)
2650 {
2651         _copy_drag_events.clear ();
2652
2653         if (_selection.empty()) {
2654                 return 0;
2655         }
2656
2657         NoteBase* note;
2658         NoteBase* ret = 0;
2659
2660         for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2661                 boost::shared_ptr<NoteType> g (new NoteType (*((*i)->note())));
2662                 if (midi_view()->note_mode() == Sustained) {
2663                         Note* n = new Note (*this, _note_group, g);
2664                         update_sustained (n, false);
2665                         note = n;
2666                 } else {
2667                         Hit* h = new Hit (*this, _note_group, 10, g);
2668                         update_hit (h, false);
2669                         note = h;
2670                 }
2671
2672                 if ((*i) == primary) {
2673                         ret = note;
2674                 }
2675
2676                 _copy_drag_events.push_back (note);
2677         }
2678
2679         return ret;
2680 }
2681
2682 void
2683 MidiRegionView::move_copies (double dx_qn, double dy, double cumulative_dy)
2684 {
2685         typedef vector<boost::shared_ptr<NoteType> > PossibleChord;
2686         Editor* editor = dynamic_cast<Editor*> (&trackview.editor());
2687         TempoMap& tmap (editor->session()->tempo_map());
2688         PossibleChord to_play;
2689         Temporal::Beats earliest = earliest_in_selection();
2690
2691         for (CopyDragEvents::iterator i = _copy_drag_events.begin(); i != _copy_drag_events.end(); ++i) {
2692                 NoteBase* n = *i;
2693                 if (n->note()->time() == earliest) {
2694                         to_play.push_back (n->note());
2695                 }
2696                 double const note_time_qn = session_relative_qn (n->note()->time().to_double());
2697                 double dx = 0.0;
2698                 if (midi_view()->note_mode() == Sustained) {
2699                         dx = editor->sample_to_pixel_unrounded (tmap.sample_at_quarter_note (note_time_qn + dx_qn))
2700                                 - n->item()->item_to_canvas (ArdourCanvas::Duple (n->x0(), 0)).x;
2701                 } else {
2702                         Hit* hit = dynamic_cast<Hit*>(n);
2703                         if (hit) {
2704                                 dx = editor->sample_to_pixel_unrounded (tmap.sample_at_quarter_note (note_time_qn + dx_qn))
2705                                         - n->item()->item_to_canvas (ArdourCanvas::Duple (((hit->x0() + hit->x1()) / 2.0) - hit->position().x, 0)).x;
2706                         }
2707                 }
2708
2709                 (*i)->move_event(dx, dy);
2710
2711                 if (midi_view()->note_mode() == Sustained) {
2712                         Note* sus = dynamic_cast<Note*> (*i);
2713                         double const len_dx = editor->sample_to_pixel_unrounded (
2714                                 tmap.sample_at_quarter_note (note_time_qn + dx_qn + n->note()->length().to_double()));
2715
2716                         sus->set_x1 (n->item()->canvas_to_item (ArdourCanvas::Duple (len_dx, 0)).x);
2717                 }
2718         }
2719
2720         if (dy && !_copy_drag_events.empty() && !_no_sound_notes && UIConfiguration::instance().get_sound_midi_notes()) {
2721
2722                 if (to_play.size() > 1) {
2723
2724                         PossibleChord shifted;
2725
2726                         for (PossibleChord::iterator n = to_play.begin(); n != to_play.end(); ++n) {
2727                                 boost::shared_ptr<NoteType> moved_note (new NoteType (**n));
2728                                 moved_note->set_note (moved_note->note() + cumulative_dy);
2729                                 shifted.push_back (moved_note);
2730                         }
2731
2732                         start_playing_midi_chord (shifted);
2733
2734                 } else if (!to_play.empty()) {
2735
2736                         boost::shared_ptr<NoteType> moved_note (new NoteType (*to_play.front()));
2737                         moved_note->set_note (moved_note->note() + cumulative_dy);
2738                         start_playing_midi_note (moved_note);
2739                 }
2740         }
2741 }
2742
2743 void
2744 MidiRegionView::note_dropped(NoteBase *, double d_qn, int8_t dnote, bool copy)
2745 {
2746         uint8_t lowest_note_in_selection  = 127;
2747         uint8_t highest_note_in_selection = 0;
2748         uint8_t highest_note_difference   = 0;
2749
2750         if (!copy) {
2751                 // find highest and lowest notes first
2752
2753                 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2754                         uint8_t pitch = (*i)->note()->note();
2755                         lowest_note_in_selection  = std::min(lowest_note_in_selection,  pitch);
2756                         highest_note_in_selection = std::max(highest_note_in_selection, pitch);
2757                 }
2758
2759                 /*
2760                   cerr << "dnote: " << (int) dnote << endl;
2761                   cerr << "lowest note (streamview): " << int(midi_stream_view()->lowest_note())
2762                   << " highest note (streamview): " << int(midi_stream_view()->highest_note()) << endl;
2763                   cerr << "lowest note (selection): " << int(lowest_note_in_selection) << " highest note(selection): "
2764                   << int(highest_note_in_selection) << endl;
2765                   cerr << "selection size: " << _selection.size() << endl;
2766                   cerr << "Highest note in selection: " << (int) highest_note_in_selection << endl;
2767                 */
2768
2769                 // Make sure the note pitch does not exceed the MIDI standard range
2770                 if (highest_note_in_selection + dnote > 127) {
2771                         highest_note_difference = highest_note_in_selection - 127;
2772                 }
2773
2774                 start_note_diff_command (_("move notes"));
2775
2776                 for (Selection::iterator i = _selection.begin(); i != _selection.end() ; ++i) {
2777
2778                         Temporal::Beats new_time = Temporal::Beats ((*i)->note()->time().to_double() + d_qn);
2779
2780                         if (new_time < 0) {
2781                                 continue;
2782                         }
2783
2784                         note_diff_add_change (*i, MidiModel::NoteDiffCommand::StartTime, new_time);
2785
2786                         uint8_t original_pitch = (*i)->note()->note();
2787                         uint8_t new_pitch      = original_pitch + dnote - highest_note_difference;
2788
2789                         // keep notes in standard midi range
2790                         clamp_to_0_127(new_pitch);
2791
2792                         lowest_note_in_selection  = std::min(lowest_note_in_selection,  new_pitch);
2793                         highest_note_in_selection = std::max(highest_note_in_selection, new_pitch);
2794
2795                         note_diff_add_change (*i, MidiModel::NoteDiffCommand::NoteNumber, new_pitch);
2796                 }
2797         } else {
2798
2799                 clear_editor_note_selection ();
2800
2801                 for (CopyDragEvents::iterator i = _copy_drag_events.begin(); i != _copy_drag_events.end(); ++i) {
2802                         uint8_t pitch = (*i)->note()->note();
2803                         lowest_note_in_selection  = std::min(lowest_note_in_selection,  pitch);
2804                         highest_note_in_selection = std::max(highest_note_in_selection, pitch);
2805                 }
2806
2807                 // Make sure the note pitch does not exceed the MIDI standard range
2808                 if (highest_note_in_selection + dnote > 127) {
2809                         highest_note_difference = highest_note_in_selection - 127;
2810                 }
2811
2812                 start_note_diff_command (_("copy notes"));
2813
2814                 for (CopyDragEvents::iterator i = _copy_drag_events.begin(); i != _copy_drag_events.end() ; ++i) {
2815
2816                         /* update time */
2817                         Temporal::Beats new_time = Temporal::Beats ((*i)->note()->time().to_double() + d_qn);
2818
2819                         if (new_time < 0) {
2820                                 continue;
2821                         }
2822
2823                         (*i)->note()->set_time (new_time);
2824
2825                         /* update pitch */
2826
2827                         uint8_t original_pitch = (*i)->note()->note();
2828                         uint8_t new_pitch      = original_pitch + dnote - highest_note_difference;
2829
2830                         (*i)->note()->set_note (new_pitch);
2831
2832                         // keep notes in standard midi range
2833                         clamp_to_0_127(new_pitch);
2834
2835                         lowest_note_in_selection  = std::min(lowest_note_in_selection,  new_pitch);
2836                         highest_note_in_selection = std::max(highest_note_in_selection, new_pitch);
2837
2838                         note_diff_add_note ((*i)->note(), true);
2839
2840                         delete *i;
2841                 }
2842
2843                 _copy_drag_events.clear ();
2844         }
2845
2846         apply_diff (false, copy);
2847
2848         // care about notes being moved beyond the upper/lower bounds on the canvas
2849         if (lowest_note_in_selection  < midi_stream_view()->lowest_note() ||
2850             highest_note_in_selection > midi_stream_view()->highest_note()) {
2851                 midi_stream_view()->set_note_range (MidiStreamView::ContentsRange);
2852         }
2853 }
2854
2855 /** @param x Pixel relative to the region position.
2856  *  @param ensure_snap defaults to false. true = snap always, ignoring snap mode and magnetic snap.
2857  *  Used for inverting the snap logic with key modifiers and snap delta calculation.
2858  *  @return Snapped sample relative to the region position.
2859  */
2860 samplepos_t
2861 MidiRegionView::snap_pixel_to_sample(double x, bool ensure_snap)
2862 {
2863         PublicEditor& editor (trackview.editor());
2864         return snap_sample_to_sample (editor.pixel_to_sample (x), ensure_snap).sample;
2865 }
2866
2867 /** @param x Pixel relative to the region position.
2868  *  @param ensure_snap defaults to false. true = ignore magnetic snap and snap mode (used for snap delta calculation).
2869  *  @return Snapped pixel relative to the region position.
2870  */
2871 double
2872 MidiRegionView::snap_to_pixel(double x, bool ensure_snap)
2873 {
2874         return (double) trackview.editor().sample_to_pixel(snap_pixel_to_sample(x, ensure_snap));
2875 }
2876
2877 double
2878 MidiRegionView::get_position_pixels()
2879 {
2880         samplepos_t region_sample = get_position();
2881         return trackview.editor().sample_to_pixel(region_sample);
2882 }
2883
2884 double
2885 MidiRegionView::get_end_position_pixels()
2886 {
2887         samplepos_t sample = get_position() + get_duration ();
2888         return trackview.editor().sample_to_pixel(sample);
2889 }
2890
2891 samplepos_t
2892 MidiRegionView::source_beats_to_absolute_samples(Temporal::Beats beats) const
2893 {
2894         /* the time converter will return the sample corresponding to `beats'
2895            relative to the start of the source. The start of the source
2896            is an implied position given by region->position - region->start
2897         */
2898         const samplepos_t source_start = _region->position() - _region->start();
2899         return  source_start +  _source_relative_time_converter.to (beats);
2900 }
2901
2902 Temporal::Beats
2903 MidiRegionView::absolute_samples_to_source_beats(samplepos_t samples) const
2904 {
2905         /* the `samples' argument needs to be converted into a sample count
2906            relative to the start of the source before being passed in to the
2907            converter.
2908         */
2909         const samplepos_t source_start = _region->position() - _region->start();
2910         return  _source_relative_time_converter.from (samples - source_start);
2911 }
2912
2913 samplepos_t
2914 MidiRegionView::region_beats_to_region_samples(Temporal::Beats beats) const
2915 {
2916         return _region_relative_time_converter.to(beats);
2917 }
2918
2919 Temporal::Beats
2920 MidiRegionView::region_samples_to_region_beats(samplepos_t samples) const
2921 {
2922         return _region_relative_time_converter.from(samples);
2923 }
2924
2925 double
2926 MidiRegionView::region_samples_to_region_beats_double (samplepos_t samples) const
2927 {
2928         return _region_relative_time_converter_double.from(samples);
2929 }
2930
2931 void
2932 MidiRegionView::begin_resizing (bool /*at_front*/)
2933 {
2934         _resize_data.clear();
2935
2936         for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2937                 Note *note = dynamic_cast<Note*> (*i);
2938
2939                 // only insert CanvasNotes into the map
2940                 if (note) {
2941                         NoteResizeData *resize_data = new NoteResizeData();
2942                         resize_data->note = note;
2943
2944                         // create a new SimpleRect from the note which will be the resize preview
2945                         ArdourCanvas::Rectangle *resize_rect = new ArdourCanvas::Rectangle (_note_group,
2946                                                                                             ArdourCanvas::Rect (note->x0(), note->y0(), note->x0(), note->y1()));
2947
2948                         // calculate the colors: get the color settings
2949                         uint32_t fill_color = NoteBase::meter_style_fill_color (note->note()->velocity(), true);
2950
2951                         // make the resize preview notes more transparent and bright
2952                         fill_color = UINT_INTERPOLATE(fill_color, 0xFFFFFF40, 0.5);
2953
2954                         // calculate color based on note velocity
2955                         resize_rect->set_fill_color (UINT_INTERPOLATE(
2956                                 NoteBase::meter_style_fill_color(note->note()->velocity(), note->selected()),
2957                                 fill_color,
2958                                 0.85));
2959
2960                         resize_rect->set_outline_color (NoteBase::calculate_outline (
2961                                                                 UIConfiguration::instance().color ("midi note selected outline")));
2962
2963                         resize_data->resize_rect = resize_rect;
2964                         _resize_data.push_back(resize_data);
2965                 }
2966         }
2967 }
2968
2969 /** Update resizing notes while user drags.
2970  * @param primary `primary' note for the drag; ie the one that is used as the reference in non-relative mode.
2971  * @param at_front which end of the note (true == note on, false == note off)
2972  * @param delta_x change in mouse position since the start of the drag
2973  * @param relative true if relative resizing is taking place, false if absolute resizing.  This only makes
2974  * a difference when multiple notes are being resized; in relative mode, each note's length is changed by the
2975  * amount of the drag.  In non-relative mode, all selected notes are set to have the same start or end point
2976  * as the \a primary note.
2977  * @param snap_delta snap offset of the primary note in pixels. used in SnapRelative SnapDelta mode.
2978  * @param with_snap true if snap is to be used to determine the position, false if no snap is to be used.
2979  */
2980 void
2981 MidiRegionView::update_resizing (NoteBase* primary, bool at_front, double delta_x, bool relative, double snap_delta, bool with_snap)
2982 {
2983         TempoMap& tmap (trackview.session()->tempo_map());
2984         bool cursor_set = false;
2985         bool const ensure_snap = trackview.editor().snap_mode () != SnapMagnetic;
2986
2987         for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
2988                 ArdourCanvas::Rectangle* resize_rect = (*i)->resize_rect;
2989                 Note* canvas_note = (*i)->note;
2990                 double current_x;
2991
2992                 if (at_front) {
2993                         if (relative) {
2994                                 current_x = canvas_note->x0() + delta_x + snap_delta;
2995                         } else {
2996                                 current_x = primary->x0() + delta_x + snap_delta;
2997                         }
2998                 } else {
2999                         if (relative) {
3000                                 current_x = canvas_note->x1() + delta_x + snap_delta;
3001                         } else {
3002                                 current_x = primary->x1() + delta_x + snap_delta;
3003                         }
3004                 }
3005
3006                 if (current_x < 0) {
3007                         /* This works even with snapping because RegionView::snap_sample_to_sample()
3008                          * snaps forward if the snapped sample is before the beginning of the region
3009                          */
3010                         current_x = 0;
3011                 }
3012                 if (current_x > trackview.editor().sample_to_pixel(_region->length())) {
3013                         current_x = trackview.editor().sample_to_pixel(_region->length());
3014                 }
3015
3016                 if (at_front) {
3017                         if (with_snap) {
3018                                 resize_rect->set_x0 (snap_to_pixel (current_x, ensure_snap) - snap_delta);
3019                         } else {
3020                                 resize_rect->set_x0 (current_x - snap_delta);
3021                         }
3022                         resize_rect->set_x1 (canvas_note->x1());
3023                 } else {
3024                         if (with_snap) {
3025                                 resize_rect->set_x1 (snap_to_pixel (current_x, ensure_snap) - snap_delta);
3026                         } else {
3027                                 resize_rect->set_x1 (current_x - snap_delta);
3028                         }
3029                         resize_rect->set_x0 (canvas_note->x0());
3030                 }
3031
3032                 if (!cursor_set) {
3033                         /* Convert snap delta from pixels to beats. */
3034                         samplepos_t snap_delta_samps = trackview.editor().pixel_to_sample (snap_delta);
3035                         double snap_delta_beats = 0.0;
3036                         int sign = 1;
3037
3038                         /* negative beat offsets aren't allowed */
3039                         if (snap_delta_samps > 0) {
3040                                 snap_delta_beats = region_samples_to_region_beats_double (snap_delta_samps);
3041                         } else if (snap_delta_samps < 0) {
3042                                 snap_delta_beats = region_samples_to_region_beats_double (- snap_delta_samps);
3043                                 sign = -1;
3044                         }
3045
3046                         double  snapped_x;
3047                         int32_t divisions = 0;
3048
3049                         if (with_snap) {
3050                                 snapped_x = snap_pixel_to_sample (current_x, ensure_snap);
3051                                 divisions = trackview.editor().get_grid_music_divisions (0);
3052                         } else {
3053                                 snapped_x = trackview.editor ().pixel_to_sample (current_x);
3054                         }
3055
3056                         const Temporal::Beats beats = Temporal::Beats (tmap.exact_beat_at_sample (snapped_x + midi_region()->position(), divisions)
3057                                                                        - midi_region()->beat())
3058                                                       + midi_region()->start_beats();
3059
3060                         Temporal::Beats len         = Temporal::Beats();
3061
3062                         if (at_front) {
3063                                 if (beats < canvas_note->note()->end_time()) {
3064                                         len = canvas_note->note()->time() - beats + (sign * snap_delta_beats);
3065                                         len += canvas_note->note()->length();
3066                                 }
3067                         } else {
3068                                 if (beats >= canvas_note->note()->time()) {
3069                                         len = beats - canvas_note->note()->time() - (sign * snap_delta_beats);
3070                                 }
3071                         }
3072
3073                         len = std::max(Temporal::Beats(1 / 512.0), len);
3074
3075                         char buf[16];
3076                         snprintf (buf, sizeof (buf), "%.3g beats", len.to_double());
3077                         show_verbose_cursor (buf, 0, 0);
3078
3079                         cursor_set = true;
3080
3081                         trackview.editor().set_snapped_cursor_position ( snapped_x + midi_region()->position() );
3082                 }
3083
3084         }
3085 }
3086
3087
3088 /** Finish resizing notes when the user releases the mouse button.
3089  *  Parameters the same as for \a update_resizing().
3090  */
3091 void
3092 MidiRegionView::commit_resizing (NoteBase* primary, bool at_front, double delta_x, bool relative, double snap_delta, bool with_snap)
3093 {
3094         _note_diff_command = _model->new_note_diff_command (_("resize notes"));
3095         TempoMap& tmap (trackview.session()->tempo_map());
3096
3097         /* XX why doesn't snap_pixel_to_sample() handle this properly? */
3098         bool const ensure_snap = trackview.editor().snap_mode () != SnapMagnetic;
3099
3100         for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
3101                 Note*  canvas_note = (*i)->note;
3102                 ArdourCanvas::Rectangle*  resize_rect = (*i)->resize_rect;
3103
3104                 /* Get the new x position for this resize, which is in pixels relative
3105                  * to the region position.
3106                  */
3107
3108                 double current_x;
3109
3110                 if (at_front) {
3111                         if (relative) {
3112                                 current_x = canvas_note->x0() + delta_x + snap_delta;
3113                         } else {
3114                                 current_x = primary->x0() + delta_x + snap_delta;
3115                         }
3116                 } else {
3117                         if (relative) {
3118                                 current_x = canvas_note->x1() + delta_x + snap_delta;
3119                         } else {
3120                                 current_x = primary->x1() + delta_x + snap_delta;
3121                         }
3122                 }
3123
3124                 if (current_x < 0) {
3125                         current_x = 0;
3126                 }
3127                 if (current_x > trackview.editor().sample_to_pixel(_region->length())) {
3128                         current_x = trackview.editor().sample_to_pixel(_region->length());
3129                 }
3130
3131                 /* Convert snap delta from pixels to beats with sign. */
3132                 samplepos_t snap_delta_samps = trackview.editor().pixel_to_sample (snap_delta);
3133                 double snap_delta_beats = 0.0;
3134                 int sign = 1;
3135
3136                 if (snap_delta_samps > 0) {
3137                         snap_delta_beats = region_samples_to_region_beats_double (snap_delta_samps);
3138                 } else if (snap_delta_samps < 0) {
3139                         snap_delta_beats = region_samples_to_region_beats_double ( - snap_delta_samps);
3140                         sign = -1;
3141                 }
3142
3143                 uint32_t divisions = 0;
3144                 /* Convert the new x position to a sample within the source */
3145                 samplepos_t current_fr;
3146                 if (with_snap) {
3147                         current_fr = snap_pixel_to_sample (current_x, ensure_snap);
3148                         divisions = trackview.editor().get_grid_music_divisions (0);
3149                 } else {
3150                         current_fr = trackview.editor().pixel_to_sample (current_x);
3151                 }
3152
3153                 /* and then to beats */
3154                 const double e_qaf = tmap.exact_qn_at_sample (current_fr + midi_region()->position(), divisions);
3155                 const double quarter_note_start = _region->quarter_note() - midi_region()->start_beats();
3156                 const Temporal::Beats x_beats = Temporal::Beats (e_qaf - quarter_note_start);
3157
3158                 if (at_front && x_beats < canvas_note->note()->end_time()) {
3159                         note_diff_add_change (canvas_note, MidiModel::NoteDiffCommand::StartTime, x_beats - (sign * snap_delta_beats));
3160                         Temporal::Beats len = canvas_note->note()->time() - x_beats + (sign * snap_delta_beats);
3161                         len += canvas_note->note()->length();
3162
3163                         if (!!len) {
3164                                 note_diff_add_change (canvas_note, MidiModel::NoteDiffCommand::Length, len);
3165                         }
3166                 }
3167
3168                 if (!at_front) {
3169                         Temporal::Beats len = std::max(Temporal::Beats(1 / 512.0),
3170                                                      x_beats - canvas_note->note()->time() - (sign * snap_delta_beats));
3171                         note_diff_add_change (canvas_note, MidiModel::NoteDiffCommand::Length, len);
3172                 }
3173
3174                 delete resize_rect;
3175                 delete (*i);
3176         }
3177
3178         _resize_data.clear();
3179         apply_diff(true);
3180 }
3181
3182 void
3183 MidiRegionView::abort_resizing ()
3184 {
3185         for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
3186                 delete (*i)->resize_rect;
3187                 delete *i;
3188         }
3189
3190         _resize_data.clear ();
3191 }
3192
3193 void
3194 MidiRegionView::change_note_velocity(NoteBase* event, int8_t velocity, bool relative)
3195 {
3196         uint8_t new_velocity;
3197
3198         if (relative) {
3199                 new_velocity = event->note()->velocity() + velocity;
3200                 clamp_to_0_127(new_velocity);
3201         } else {
3202                 new_velocity = velocity;
3203         }
3204
3205         event->set_selected (event->selected()); // change color
3206
3207         note_diff_add_change (event, MidiModel::NoteDiffCommand::Velocity, new_velocity);
3208 }
3209
3210 void
3211 MidiRegionView::change_note_note (NoteBase* event, int8_t note, bool relative)
3212 {
3213         uint8_t new_note;
3214
3215         if (relative) {
3216                 new_note = event->note()->note() + note;
3217         } else {
3218                 new_note = note;
3219         }
3220
3221         clamp_to_0_127 (new_note);
3222         note_diff_add_change (event, MidiModel::NoteDiffCommand::NoteNumber, new_note);
3223 }
3224
3225 void
3226 MidiRegionView::trim_note (NoteBase* event, Temporal::Beats front_delta, Temporal::Beats end_delta)
3227 {
3228         bool change_start = false;
3229         bool change_length = false;
3230         Temporal::Beats new_start;
3231         Temporal::Beats new_length;
3232
3233         /* NOTE: the semantics of the two delta arguments are slightly subtle:
3234
3235            front_delta: if positive - move the start of the note later in time (shortening it)
3236            if negative - move the start of the note earlier in time (lengthening it)
3237
3238            end_delta:   if positive - move the end of the note later in time (lengthening it)
3239            if negative - move the end of the note earlier in time (shortening it)
3240         */
3241
3242         if (!!front_delta) {
3243                 if (front_delta < 0) {
3244
3245                         if (event->note()->time() < -front_delta) {
3246                                 new_start = Temporal::Beats();
3247                         } else {
3248                                 new_start = event->note()->time() + front_delta; // moves earlier
3249                         }
3250
3251                         /* start moved toward zero, so move the end point out to where it used to be.
3252                            Note that front_delta is negative, so this increases the length.
3253                         */
3254
3255                         new_length = event->note()->length() - front_delta;
3256                         change_start = true;
3257                         change_length = true;
3258
3259                 } else {
3260
3261                         Temporal::Beats new_pos = event->note()->time() + front_delta;
3262
3263                         if (new_pos < event->note()->end_time()) {
3264                                 new_start = event->note()->time() + front_delta;
3265                                 /* start moved toward the end, so move the end point back to where it used to be */
3266                                 new_length = event->note()->length() - front_delta;
3267                                 change_start = true;
3268                                 change_length = true;
3269                         }
3270                 }
3271
3272         }
3273
3274         if (!!end_delta) {
3275                 bool can_change = true;
3276                 if (end_delta < 0) {
3277                         if (event->note()->length() < -end_delta) {
3278                                 can_change = false;
3279                         }
3280                 }
3281
3282                 if (can_change) {
3283                         new_length = event->note()->length() + end_delta;
3284                         change_length = true;
3285                 }
3286         }
3287
3288         if (change_start) {
3289                 note_diff_add_change (event, MidiModel::NoteDiffCommand::StartTime, new_start);
3290         }
3291
3292         if (change_length) {
3293                 note_diff_add_change (event, MidiModel::NoteDiffCommand::Length, new_length);
3294         }
3295 }
3296
3297 void
3298 MidiRegionView::change_note_channel (NoteBase* event, int8_t chn, bool relative)
3299 {
3300         uint8_t new_channel;
3301
3302         if (relative) {
3303                 if (chn < 0.0) {
3304                         if (event->note()->channel() < -chn) {
3305                                 new_channel = 0;
3306                         } else {
3307                                 new_channel = event->note()->channel() + chn;
3308                         }
3309                 } else {
3310                         new_channel = event->note()->channel() + chn;
3311                 }
3312         } else {
3313                 new_channel = (uint8_t) chn;
3314         }
3315
3316         note_diff_add_change (event, MidiModel::NoteDiffCommand::Channel, new_channel);
3317 }
3318
3319 void
3320 MidiRegionView::change_note_time (NoteBase* event, Temporal::Beats delta, bool relative)
3321 {
3322         Temporal::Beats new_time;
3323
3324         if (relative) {
3325                 if (delta < 0.0) {
3326                         if (event->note()->time() < -delta) {
3327                                 new_time = Temporal::Beats();
3328                         } else {
3329                                 new_time = event->note()->time() + delta;
3330                         }
3331                 } else {
3332                         new_time = event->note()->time() + delta;
3333                 }
3334         } else {
3335                 new_time = delta;
3336         }
3337
3338         note_diff_add_change (event, MidiModel::NoteDiffCommand::StartTime, new_time);
3339 }
3340
3341 void
3342 MidiRegionView::change_note_length (NoteBase* event, Temporal::Beats t)
3343 {
3344         note_diff_add_change (event, MidiModel::NoteDiffCommand::Length, t);
3345 }
3346
3347 void
3348 MidiRegionView::change_velocities (bool up, bool fine, bool allow_smush, bool all_together)
3349 {
3350         int8_t delta;
3351         int8_t value = 0;
3352
3353         if (_selection.empty()) {
3354                 return;
3355         }
3356
3357         if (fine) {
3358                 delta = 1;
3359         } else {
3360                 delta = 10;
3361         }
3362
3363         if (!up) {
3364                 delta = -delta;
3365         }
3366
3367         if (!allow_smush) {
3368                 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3369                         if ((*i)->note()->velocity() < -delta || (*i)->note()->velocity() + delta > 127) {
3370                                 goto cursor_label;
3371                         }
3372                 }
3373         }
3374
3375         start_note_diff_command (_("change velocities"));
3376
3377         for (Selection::iterator i = _selection.begin(); i != _selection.end();) {
3378                 Selection::iterator next = i;
3379                 ++next;
3380
3381                 if (all_together) {
3382                         if (i == _selection.begin()) {
3383                                 change_note_velocity (*i, delta, true);
3384                                 value = (*i)->note()->velocity() + delta;
3385                         } else {
3386                                 change_note_velocity (*i, value, false);
3387                         }
3388
3389                 } else {
3390                         change_note_velocity (*i, delta, true);
3391                 }
3392
3393                 i = next;
3394         }
3395
3396         apply_diff();
3397
3398   cursor_label:
3399         if (!_selection.empty()) {
3400                 char buf[24];
3401                 snprintf (buf, sizeof (buf), "Vel %d",
3402                           (int) (*_selection.begin())->note()->velocity());
3403                 show_verbose_cursor (buf, 10, 10);
3404         }
3405 }
3406
3407
3408 void
3409 MidiRegionView::transpose (bool up, bool fine, bool allow_smush)
3410 {
3411         if (_selection.empty()) {
3412                 return;
3413         }
3414
3415         int8_t delta;
3416
3417         if (fine) {
3418                 delta = 1;
3419         } else {
3420                 delta = 12;
3421         }
3422
3423         if (!up) {
3424                 delta = -delta;
3425         }
3426
3427         if (!allow_smush) {
3428                 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3429                         if (!up) {
3430                                 if ((int8_t) (*i)->note()->note() + delta <= 0) {
3431                                         return;
3432                                 }
3433                         } else {
3434                                 if ((int8_t) (*i)->note()->note() + delta > 127) {
3435                                         return;
3436                                 }
3437                         }
3438                 }
3439         }
3440
3441         start_note_diff_command (_("transpose"));
3442
3443         for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
3444                 Selection::iterator next = i;
3445                 ++next;
3446                 change_note_note (*i, delta, true);
3447                 i = next;
3448         }
3449
3450         apply_diff ();
3451 }
3452
3453 void
3454 MidiRegionView::change_note_lengths (bool fine, bool shorter, Temporal::Beats delta, bool start, bool end)
3455 {
3456         if (!delta) {
3457                 if (fine) {
3458                         delta = Temporal::Beats(1.0/128.0);
3459                 } else {
3460                         /* grab the current grid distance */
3461                         delta = get_grid_beats(_region->position());
3462                 }
3463         }
3464
3465         if (shorter) {
3466                 delta = -delta;
3467         }
3468
3469         start_note_diff_command (_("change note lengths"));
3470
3471         for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
3472                 Selection::iterator next = i;
3473                 ++next;
3474
3475                 /* note the negation of the delta for start */
3476
3477                 trim_note (*i,
3478                            (start ? -delta : Temporal::Beats()),
3479                            (end   ? delta  : Temporal::Beats()));
3480                 i = next;
3481         }
3482
3483         apply_diff ();
3484
3485 }
3486
3487 void
3488 MidiRegionView::nudge_notes (bool forward, bool fine)
3489 {
3490         if (_selection.empty()) {
3491                 return;
3492         }
3493
3494         /* pick a note as the point along the timeline to get the nudge distance.
3495            its not necessarily the earliest note, so we may want to pull the notes out
3496            into a vector and sort before using the first one.
3497         */
3498
3499         const samplepos_t ref_point = source_beats_to_absolute_samples ((*(_selection.begin()))->note()->time());
3500         Temporal::Beats  delta;
3501
3502         if (!fine) {
3503
3504                 /* non-fine, move by 1 bar regardless of snap */
3505                 delta = Temporal::Beats(trackview.session()->tempo_map().meter_at_sample (ref_point).divisions_per_bar());
3506
3507         } else if (trackview.editor().snap_mode() == Editing::SnapOff) {
3508
3509                 /* grid is off - use nudge distance */
3510
3511                 samplepos_t       unused;
3512                 const samplecnt_t distance = trackview.editor().get_nudge_distance (ref_point, unused);
3513                 delta = region_samples_to_region_beats (fabs ((double)distance));
3514
3515         } else {
3516
3517                 /* use grid */
3518
3519                 MusicSample next_pos (ref_point, 0);
3520                 if (forward) {
3521                         if (max_samplepos - 1 < next_pos.sample) {
3522                                 next_pos.sample += 1;
3523                         }
3524                 } else {
3525                         if (next_pos.sample == 0) {
3526                                 return;
3527                         }
3528                         next_pos.sample -= 1;
3529                 }
3530
3531                 trackview.editor().snap_to (next_pos, (forward ? RoundUpAlways : RoundDownAlways), SnapToGrid_Unscaled, false);
3532                 const samplecnt_t distance = ref_point - next_pos.sample;
3533                 delta = region_samples_to_region_beats (fabs ((double)distance));
3534         }
3535
3536         if (!delta) {
3537                 return;
3538         }
3539
3540         if (!forward) {
3541                 delta = -delta;
3542         }
3543
3544         start_note_diff_command (_("nudge"));
3545
3546         for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
3547                 Selection::iterator next = i;
3548                 ++next;
3549                 change_note_time (*i, delta, true);
3550                 i = next;
3551         }
3552
3553         apply_diff ();
3554 }
3555
3556 void
3557 MidiRegionView::change_channel(uint8_t channel)
3558 {
3559         start_note_diff_command(_("change channel"));
3560         for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3561                 note_diff_add_change (*i, MidiModel::NoteDiffCommand::Channel, channel);
3562         }
3563
3564         apply_diff();
3565 }
3566
3567
3568 void
3569 MidiRegionView::note_entered(NoteBase* ev)
3570 {
3571         _entered_note = ev;
3572
3573         Editor* editor = dynamic_cast<Editor*>(&trackview.editor());
3574
3575         if (_mouse_state == SelectTouchDragging) {
3576
3577                 note_selected (ev, true);
3578
3579         } else if (editor->current_mouse_mode() == MouseContent) {
3580
3581                 remove_ghost_note ();
3582                 show_verbose_cursor (ev->note ());
3583
3584         } else if (editor->current_mouse_mode() == MouseDraw) {
3585
3586                 remove_ghost_note ();
3587                 show_verbose_cursor (ev->note ());
3588         }
3589 }
3590
3591 void
3592 MidiRegionView::note_left (NoteBase*)
3593 {
3594         _entered_note = 0;
3595
3596         for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3597                 (*i)->hide_velocity ();
3598         }
3599
3600         hide_verbose_cursor ();
3601 }
3602
3603 void
3604 MidiRegionView::patch_entered (PatchChange* p)
3605 {
3606         ostringstream s;
3607         s << _("Bank ") << (p->patch()->bank() + MIDI_BP_ZERO) << '\n'
3608           << instrument_info().get_patch_name_without (p->patch()->bank(), p->patch()->program(), p->patch()->channel()) << '\n'
3609           << _("Channel ") << ((int) p->patch()->channel() + 1);
3610         show_verbose_cursor (s.str(), 10, 20);
3611         p->item().grab_focus();
3612 }
3613
3614 void
3615 MidiRegionView::patch_left (PatchChange *)
3616 {
3617         hide_verbose_cursor ();
3618         /* focus will transfer back via the enter-notify event sent to this
3619          * midi region view.
3620          */
3621 }
3622
3623 void
3624 MidiRegionView::sysex_entered (SysEx* p)
3625 {
3626         // ostringstream s;
3627         // CAIROCANVAS
3628         // need a way to extract text from p->_flag->_text
3629         // s << p->text();
3630         // show_verbose_cursor (s.str(), 10, 20);
3631         p->item().grab_focus();
3632 }
3633
3634 void
3635 MidiRegionView::sysex_left (SysEx *)
3636 {
3637         hide_verbose_cursor ();
3638         /* focus will transfer back via the enter-notify event sent to this
3639          * midi region view.
3640          */
3641 }
3642
3643 void
3644 MidiRegionView::note_mouse_position (float x_fraction, float /*y_fraction*/, bool can_set_cursor)
3645 {
3646         Editor* editor = dynamic_cast<Editor*>(&trackview.editor());
3647         Editing::MouseMode mm = editor->current_mouse_mode();
3648         bool trimmable = (mm == MouseContent || mm == MouseTimeFX || mm == MouseDraw);
3649
3650         Editor::EnterContext* ctx = editor->get_enter_context(NoteItem);
3651         if (can_set_cursor && ctx) {
3652                 if (trimmable && x_fraction > 0.0 && x_fraction < 0.2) {
3653                         ctx->cursor_ctx->change(editor->cursors()->left_side_trim);
3654                 } else if (trimmable && x_fraction >= 0.8 && x_fraction < 1.0) {
3655                         ctx->cursor_ctx->change(editor->cursors()->right_side_trim);
3656                 } else {
3657                         ctx->cursor_ctx->change(editor->cursors()->grabber_note);
3658                 }
3659         }
3660 }
3661
3662 uint32_t
3663 MidiRegionView::get_fill_color() const
3664 {
3665         const std::string mod_name = (_dragging ? "dragging region" :
3666                                       trackview.editor().internal_editing() ? "editable region" :
3667                                       "midi frame base");
3668         if (_selected) {
3669                 return UIConfiguration::instance().color_mod ("selected region base", mod_name);
3670         } else if ((!UIConfiguration::instance().get_show_name_highlight() || high_enough_for_name) &&
3671                    !UIConfiguration::instance().get_color_regions_using_track_color()) {
3672                 return UIConfiguration::instance().color_mod ("midi frame base", mod_name);
3673         }
3674         return UIConfiguration::instance().color_mod (fill_color, mod_name);
3675 }
3676
3677 void
3678 MidiRegionView::midi_channel_mode_changed ()
3679 {
3680         MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3681         uint16_t mask = mtv->midi_track()->get_playback_channel_mask();
3682         ChannelMode mode = mtv->midi_track()->get_playback_channel_mode ();
3683
3684         if (mode == ForceChannel) {
3685                 mask = 0xFFFF; // Show all notes as active (below)
3686         }
3687
3688         // Update notes for selection
3689         for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3690                 i->second->on_channel_selection_change (mask);
3691         }
3692
3693         _patch_changes.clear ();
3694         display_patch_changes ();
3695 }
3696
3697 void
3698 MidiRegionView::instrument_settings_changed ()
3699 {
3700         redisplay_model();
3701 }
3702
3703 void
3704 MidiRegionView::cut_copy_clear (Editing::CutCopyOp op)
3705 {
3706         if (_selection.empty()) {
3707                 return;
3708         }
3709
3710         PublicEditor& editor (trackview.editor());
3711
3712         switch (op) {
3713         case Delete:
3714                 /* XXX what to do ? */
3715                 break;
3716         case Cut:
3717         case Copy:
3718                 editor.get_cut_buffer().add (selection_as_cut_buffer());
3719                 break;
3720         default:
3721                 break;
3722         }
3723
3724         if (op != Copy) {
3725
3726                 start_note_diff_command();
3727
3728                 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3729                         switch (op) {
3730                         case Copy:
3731                                 break;
3732                         case Delete:
3733                         case Cut:
3734                         case Clear:
3735                                 note_diff_remove_note (*i);
3736                                 break;
3737                         }
3738                 }
3739
3740                 apply_diff();
3741         }
3742 }
3743
3744 MidiCutBuffer*
3745 MidiRegionView::selection_as_cut_buffer () const
3746 {
3747         Notes notes;
3748
3749         for (Selection::const_iterator i = _selection.begin(); i != _selection.end(); ++i) {
3750                 NoteType* n = (*i)->note().get();
3751                 notes.insert (boost::shared_ptr<NoteType> (new NoteType (*n)));
3752         }
3753
3754         MidiCutBuffer* cb = new MidiCutBuffer (trackview.session());
3755         cb->set (notes);
3756
3757         return cb;
3758 }
3759
3760 /** This method handles undo */
3761 bool
3762 MidiRegionView::paste (samplepos_t pos, const ::Selection& selection, PasteContext& ctx, const int32_t sub_num)
3763 {
3764         bool commit = false;
3765         // Paste notes, if available
3766         MidiNoteSelection::const_iterator m = selection.midi_notes.get_nth(ctx.counts.n_notes());
3767         if (m != selection.midi_notes.end()) {
3768                 ctx.counts.increase_n_notes();
3769                 if (!(*m)->empty()) {
3770                         commit = true;
3771                 }
3772                 paste_internal(pos, ctx.count, ctx.times, **m);
3773         }
3774
3775         // Paste control points to automation children, if available
3776         typedef RouteTimeAxisView::AutomationTracks ATracks;
3777         const ATracks& atracks = midi_view()->automation_tracks();
3778         for (ATracks::const_iterator a = atracks.begin(); a != atracks.end(); ++a) {
3779                 if (a->second->paste(pos, selection, ctx, sub_num)) {
3780                         if(!commit) {
3781                                 trackview.editor().begin_reversible_command (Operations::paste);
3782                         }
3783                         commit = true;
3784                 }
3785         }
3786
3787         if (commit) {
3788                 trackview.editor().commit_reversible_command ();
3789         }
3790         return true;
3791 }
3792
3793 /** This method handles undo */
3794 void
3795 MidiRegionView::paste_internal (samplepos_t pos, unsigned paste_count, float times, const MidiCutBuffer& mcb)
3796 {
3797         if (mcb.empty()) {
3798                 return;
3799         }
3800
3801         start_note_diff_command (_("paste"));
3802
3803         const Temporal::Beats snap_beats    = get_grid_beats(pos);
3804         const Temporal::Beats first_time    = (*mcb.notes().begin())->time();
3805         const Temporal::Beats last_time     = (*mcb.notes().rbegin())->end_time();
3806         const Temporal::Beats duration      = last_time - first_time;
3807         const Temporal::Beats snap_duration = duration.snap_to(snap_beats);
3808         const Temporal::Beats paste_offset  = snap_duration * paste_count;
3809         const Temporal::Beats quarter_note  = absolute_samples_to_source_beats(pos) + paste_offset;
3810         Temporal::Beats       end_point     = Temporal::Beats();
3811
3812         DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("Paste data spans from %1 to %2 (%3) ; paste pos beats = %4 (based on %5 - %6)\n",
3813                                                        first_time,
3814                                                        last_time,
3815                                                        duration, pos, _region->position(),
3816                                                        quarter_note));
3817
3818         clear_editor_note_selection ();
3819
3820         for (int n = 0; n < (int) times; ++n) {
3821
3822                 for (Notes::const_iterator i = mcb.notes().begin(); i != mcb.notes().end(); ++i) {
3823
3824                         boost::shared_ptr<NoteType> copied_note (new NoteType (*((*i).get())));
3825                         copied_note->set_time (quarter_note + copied_note->time() - first_time);
3826                         copied_note->set_id (Evoral::next_event_id());
3827
3828                         /* make all newly added notes selected */
3829
3830                         note_diff_add_note (copied_note, true);
3831                         end_point = copied_note->end_time();
3832                 }
3833         }
3834
3835         /* if we pasted past the current end of the region, extend the region */
3836
3837         samplepos_t end_sample = source_beats_to_absolute_samples (end_point);
3838         samplepos_t region_end = _region->position() + _region->length() - 1;
3839
3840         if (end_sample > region_end) {
3841
3842                 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("Paste extended region from %1 to %2\n", region_end, end_sample));
3843
3844                 _region->clear_changes ();
3845                 /* we probably need to get the snap modifier somehow to make this correct for non-musical use */
3846                 _region->set_length (end_sample - _region->position(), trackview.editor().get_grid_music_divisions (0));
3847                 trackview.session()->add_command (new StatefulDiffCommand (_region));
3848         }
3849
3850         apply_diff (true);
3851 }
3852
3853 struct EventNoteTimeEarlyFirstComparator {
3854         bool operator() (NoteBase* a, NoteBase* b) {
3855                 return a->note()->time() < b->note()->time();
3856         }
3857 };
3858
3859 void
3860 MidiRegionView::goto_next_note (bool add_to_selection)
3861 {
3862         bool use_next = false;
3863
3864         MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3865         uint16_t const channel_mask = mtv->midi_track()->get_playback_channel_mask();
3866         NoteBase* first_note = 0;
3867
3868         MidiModel::ReadLock lock(_model->read_lock());
3869         MidiModel::Notes& notes (_model->notes());
3870
3871         for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
3872                 NoteBase* cne = 0;
3873                 if ((cne = find_canvas_note (*n))) {
3874
3875                         if (!first_note && (channel_mask & (1 << (*n)->channel()))) {
3876                                 first_note = cne;
3877                         }
3878
3879                         if (cne->selected()) {
3880                                 use_next = true;
3881                                 continue;
3882                         } else if (use_next) {
3883                                 if (channel_mask & (1 << (*n)->channel())) {
3884                                         if (!add_to_selection) {
3885                                                 unique_select (cne);
3886                                         } else {
3887                                                 note_selected (cne, true, false);
3888                                         }
3889
3890                                         return;
3891                                 }
3892                         }
3893                 }
3894         }
3895
3896         /* use the first one */
3897
3898         if (!_events.empty() && first_note) {
3899                 unique_select (first_note);
3900         }
3901 }
3902
3903 void
3904 MidiRegionView::goto_previous_note (bool add_to_selection)
3905 {
3906         bool use_next = false;
3907
3908         MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3909         uint16_t const channel_mask = mtv->midi_track()->get_playback_channel_mask ();
3910         NoteBase* last_note = 0;
3911
3912         MidiModel::ReadLock lock(_model->read_lock());
3913         MidiModel::Notes& notes (_model->notes());
3914
3915         for (MidiModel::Notes::reverse_iterator n = notes.rbegin(); n != notes.rend(); ++n) {
3916                 NoteBase* cne = 0;
3917                 if ((cne = find_canvas_note (*n))) {
3918
3919                         if (!last_note && (channel_mask & (1 << (*n)->channel()))) {
3920                                 last_note = cne;
3921                         }
3922
3923                         if (cne->selected()) {
3924                                 use_next = true;
3925                                 continue;
3926
3927                         } else if (use_next) {
3928                                 if (channel_mask & (1 << (*n)->channel())) {
3929                                         if (!add_to_selection) {
3930                                                 unique_select (cne);
3931                                         } else {
3932                                                 note_selected (cne, true, false);
3933                                         }
3934
3935                                         return;
3936                                 }
3937                         }
3938                 }
3939         }
3940
3941         /* use the last one */
3942
3943         if (!_events.empty() && last_note) {
3944                 unique_select (last_note);
3945         }
3946 }
3947
3948 void
3949 MidiRegionView::selection_as_notelist (Notes& selected, bool allow_all_if_none_selected)
3950 {
3951         bool had_selected = false;
3952
3953         /* we previously time sorted events here, but Notes is a multiset sorted by time */
3954
3955         for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3956                 if (i->second->selected()) {
3957                         selected.insert (i->first);
3958                         had_selected = true;
3959                 }
3960         }
3961
3962         if (allow_all_if_none_selected && !had_selected) {
3963                 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3964                         selected.insert (i->first);
3965                 }
3966         }
3967 }
3968
3969 void
3970 MidiRegionView::update_ghost_note (double x, double y, uint32_t state)
3971 {
3972         x = std::max(0.0, x);
3973
3974         MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3975
3976         _last_ghost_x = x;
3977         _last_ghost_y = y;
3978
3979         _note_group->canvas_to_item (x, y);
3980
3981         PublicEditor& editor = trackview.editor ();
3982
3983         samplepos_t const unsnapped_sample = editor.pixel_to_sample (x);
3984
3985         const int32_t divisions = editor.get_grid_music_divisions (state);
3986         const bool shift_snap = midi_view()->note_mode() != Percussive;
3987         const Temporal::Beats snapped_beats = snap_sample_to_grid_underneath (unsnapped_sample, divisions, shift_snap);
3988
3989         /* prevent Percussive mode from displaying a ghost hit at region end */
3990         if (!shift_snap && snapped_beats >= midi_region()->start_beats() + midi_region()->length_beats()) {
3991                 _ghost_note->hide();
3992                 hide_verbose_cursor ();
3993                 return;
3994         }
3995
3996         /* ghost note may have been snapped before region */
3997         if (_ghost_note && snapped_beats.to_double() < 0.0) {
3998                 _ghost_note->hide();
3999                 return;
4000
4001         } else if (_ghost_note) {
4002                 _ghost_note->show();
4003         }
4004
4005         /* calculate time in beats relative to start of source */
4006         const Temporal::Beats length = get_grid_beats(unsnapped_sample + _region->position());
4007
4008         _ghost_note->note()->set_time (snapped_beats);
4009         _ghost_note->note()->set_length (length);
4010         _ghost_note->note()->set_note (y_to_note (y));
4011         _ghost_note->note()->set_channel (mtv->get_channel_for_add ());
4012         _ghost_note->note()->set_velocity (get_velocity_for_add (snapped_beats));
4013         /* the ghost note does not appear in ghost regions, so pass false in here */
4014         update_note (_ghost_note, false);
4015
4016         show_verbose_cursor (_ghost_note->note ());
4017 }
4018
4019 void
4020 MidiRegionView::create_ghost_note (double x, double y, uint32_t state)
4021 {
4022         remove_ghost_note ();
4023
4024         boost::shared_ptr<NoteType> g (new NoteType);
4025         if (midi_view()->note_mode() == Sustained) {
4026                 _ghost_note = new Note (*this, _note_group, g);
4027         } else {
4028                 _ghost_note = new Hit (*this, _note_group, 10, g);
4029         }
4030         _ghost_note->set_ignore_events (true);
4031         _ghost_note->set_outline_color (0x000000aa);
4032         update_ghost_note (x, y, state);
4033         _ghost_note->show ();
4034
4035         show_verbose_cursor (_ghost_note->note ());
4036 }
4037
4038 void
4039 MidiRegionView::remove_ghost_note ()
4040 {
4041         delete _ghost_note;
4042         _ghost_note = 0;
4043 }
4044
4045 void
4046 MidiRegionView::hide_verbose_cursor ()
4047 {
4048         trackview.editor().verbose_cursor()->hide ();
4049         MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
4050         if (mtv) {
4051                 mtv->set_note_highlight (NO_MIDI_NOTE);
4052         }
4053 }
4054
4055 void
4056 MidiRegionView::snap_changed ()
4057 {
4058         if (!_ghost_note) {
4059                 return;
4060         }
4061
4062         create_ghost_note (_last_ghost_x, _last_ghost_y, 0);
4063 }
4064
4065 void
4066 MidiRegionView::drop_down_keys ()
4067 {
4068         _mouse_state = None;
4069 }
4070
4071 void
4072 MidiRegionView::maybe_select_by_position (GdkEventButton* ev, double /*x*/, double y)
4073 {
4074         /* XXX: This is dead code.  What was it for? */
4075
4076         double note = y_to_note(y);
4077         Events e;
4078         MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
4079
4080         uint16_t chn_mask = mtv->midi_track()->get_playback_channel_mask();
4081
4082         if (Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) {
4083                 get_events (e, Evoral::Sequence<Temporal::Beats>::PitchGreaterThanOrEqual, (uint8_t) floor (note), chn_mask);
4084         } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
4085                 get_events (e, Evoral::Sequence<Temporal::Beats>::PitchLessThanOrEqual, (uint8_t) floor (note), chn_mask);
4086         } else {
4087                 return;
4088         }
4089
4090         bool add_mrv_selection = false;
4091
4092         if (_selection.empty()) {
4093                 add_mrv_selection = true;
4094         }
4095
4096         for (Events::iterator i = e.begin(); i != e.end(); ++i) {
4097                 if (_selection.insert (i->second).second) {
4098                         i->second->set_selected (true);
4099                 }
4100         }
4101
4102         if (add_mrv_selection) {
4103                 PublicEditor& editor (trackview.editor());
4104                 editor.get_selection().add (this);
4105         }
4106 }
4107
4108 void
4109 MidiRegionView::color_handler ()
4110 {
4111         RegionView::color_handler ();
4112
4113         _patch_change_outline = UIConfiguration::instance().color ("midi patch change outline");
4114         _patch_change_fill = UIConfiguration::instance().color_mod ("midi patch change fill", "midi patch change fill");
4115
4116         for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
4117                 i->second->set_selected (i->second->selected()); // will change color
4118         }
4119
4120         /* XXX probably more to do here */
4121 }
4122
4123 void
4124 MidiRegionView::enable_display (bool yn)
4125 {
4126         RegionView::enable_display (yn);
4127 }
4128
4129 void
4130 MidiRegionView::show_step_edit_cursor (Temporal::Beats pos)
4131 {
4132         if (_step_edit_cursor == 0) {
4133                 ArdourCanvas::Item* const group = get_canvas_group();
4134
4135                 _step_edit_cursor = new ArdourCanvas::Rectangle (group);
4136                 _step_edit_cursor->set_y0 (0);
4137                 _step_edit_cursor->set_y1 (midi_stream_view()->contents_height());
4138                 _step_edit_cursor->set_fill_color (RGBA_TO_UINT (45,0,0,90));
4139                 _step_edit_cursor->set_outline_color (RGBA_TO_UINT (85,0,0,90));
4140         }
4141
4142         move_step_edit_cursor (pos);
4143         _step_edit_cursor->show ();
4144 }
4145
4146 void
4147 MidiRegionView::move_step_edit_cursor (Temporal::Beats pos)
4148 {
4149         _step_edit_cursor_position = pos;
4150
4151         if (_step_edit_cursor) {
4152                 double pixel = trackview.editor().sample_to_pixel (region_beats_to_region_samples (pos));
4153                 _step_edit_cursor->set_x0 (pixel);
4154                 set_step_edit_cursor_width (_step_edit_cursor_width);
4155         }
4156 }
4157
4158 void
4159 MidiRegionView::hide_step_edit_cursor ()
4160 {
4161         if (_step_edit_cursor) {
4162                 _step_edit_cursor->hide ();
4163         }
4164 }
4165
4166 void
4167 MidiRegionView::set_step_edit_cursor_width (Temporal::Beats beats)
4168 {
4169         _step_edit_cursor_width = beats;
4170
4171         if (_step_edit_cursor) {
4172                 _step_edit_cursor->set_x1 (_step_edit_cursor->x0()
4173                                            + trackview.editor().sample_to_pixel (
4174                                              region_beats_to_region_samples (_step_edit_cursor_position + beats)
4175                                              - region_beats_to_region_samples (_step_edit_cursor_position)));
4176         }
4177 }
4178
4179 /** Called when a diskstream on our track has received some data.  Update the view, if applicable.
4180  *  @param w Source that the data will end up in.
4181  */
4182 void
4183 MidiRegionView::data_recorded (boost::weak_ptr<MidiSource> w)
4184 {
4185         if (!_active_notes) {
4186                 /* we aren't actively being recorded to */
4187                 return;
4188         }
4189
4190         boost::shared_ptr<MidiSource> src = w.lock ();
4191         if (!src || src != midi_region()->midi_source()) {
4192                 /* recorded data was not destined for our source */
4193                 return;
4194         }
4195
4196         MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*> (&trackview);
4197
4198         boost::shared_ptr<MidiBuffer> buf = mtv->midi_track()->get_gui_feed_buffer ();
4199
4200         samplepos_t back = max_samplepos;
4201
4202         for (MidiBuffer::iterator i = buf->begin(); i != buf->end(); ++i) {
4203                 const Evoral::Event<MidiBuffer::TimeType>& ev = *i;
4204
4205                 if (ev.is_channel_event()) {
4206                         if (get_channel_mode() == FilterChannels) {
4207                                 if (((uint16_t(1) << ev.channel()) & get_selected_channels()) == 0) {
4208                                         continue;
4209                                 }
4210                         }
4211                 }
4212
4213                 /* convert from session samples to source beats */
4214                 Temporal::Beats const time_beats = _source_relative_time_converter.from(
4215                         ev.time() - src->natural_position() + _region->start());
4216
4217                 if (ev.type() == MIDI_CMD_NOTE_ON) {
4218                         boost::shared_ptr<NoteType> note (
4219                                 new NoteType (ev.channel(), time_beats, Temporal::Beats(), ev.note(), ev.velocity()));
4220
4221                         add_note (note, true);
4222
4223                         /* fix up our note range */
4224                         if (ev.note() < _current_range_min) {
4225                                 midi_stream_view()->apply_note_range (ev.note(), _current_range_max, true);
4226                         } else if (ev.note() > _current_range_max) {
4227                                 midi_stream_view()->apply_note_range (_current_range_min, ev.note(), true);
4228                         }
4229
4230                 } else if (ev.type() == MIDI_CMD_NOTE_OFF) {
4231                         resolve_note (ev.note (), time_beats);
4232                 }
4233
4234                 back = ev.time ();
4235         }
4236
4237         midi_stream_view()->check_record_layers (region(), back);
4238 }
4239
4240 void
4241 MidiRegionView::trim_front_starting ()
4242 {
4243         /* We used to eparent the note group to the region view's parent, so that it didn't change.
4244            now we update it.
4245         */
4246 }
4247
4248 void
4249 MidiRegionView::trim_front_ending ()
4250 {
4251         if (_region->start() < 0) {
4252                 /* Trim drag made start time -ve; fix this */
4253                 midi_region()->fix_negative_start ();
4254         }
4255 }
4256
4257 void
4258 MidiRegionView::edit_patch_change (PatchChange* pc)
4259 {
4260         PatchChangeDialog d (&_source_relative_time_converter, trackview.session(), *pc->patch (), instrument_info(), Gtk::Stock::APPLY, true);
4261
4262         int response = d.run();
4263
4264         switch (response) {
4265         case Gtk::RESPONSE_ACCEPT:
4266                 break;
4267         case Gtk::RESPONSE_REJECT:
4268                 delete_patch_change (pc);
4269                 return;
4270         default:
4271                 return;
4272         }
4273
4274         change_patch_change (pc->patch(), d.patch ());
4275 }
4276
4277 void
4278 MidiRegionView::delete_sysex (SysEx* /*sysex*/)
4279 {
4280         // CAIROCANVAS
4281         // sysyex object doesn't have a pointer to a sysex event
4282         // MidiModel::SysExDiffCommand* c = _model->new_sysex_diff_command (_("delete sysex"));
4283         // c->remove (sysex->sysex());
4284         // _model->apply_command (*trackview.session(), c);
4285
4286         //_sys_exes.clear ();
4287         // display_sysexes();
4288 }
4289
4290 std::string
4291 MidiRegionView::get_note_name (boost::shared_ptr<NoteType> n, uint8_t note_value) const
4292 {
4293         using namespace MIDI::Name;
4294         std::string name;
4295
4296         MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
4297         if (mtv) {
4298                 boost::shared_ptr<MasterDeviceNames> device_names(mtv->get_device_names());
4299                 if (device_names) {
4300                         MIDI::Name::PatchPrimaryKey patch_key;
4301                         get_patch_key_at(n->time(), n->channel(), patch_key);
4302                         name = device_names->note_name(mtv->gui_property(X_("midnam-custom-device-mode")),
4303                                                        n->channel(),
4304                                                        patch_key.bank(),
4305                                                        patch_key.program(),
4306                                                        note_value);
4307                 }
4308         }
4309
4310         char buf[128];
4311         snprintf (buf, sizeof (buf), "%d %s\nCh %d Vel %d",
4312                   (int) note_value,
4313                   name.empty() ? ParameterDescriptor::midi_note_name (note_value).c_str() : name.c_str(),
4314                   (int) n->channel() + 1,
4315                   (int) n->velocity());
4316
4317         return buf;
4318 }
4319
4320 void
4321 MidiRegionView::show_verbose_cursor_for_new_note_value(boost::shared_ptr<NoteType> current_note,
4322                                                        uint8_t new_value) const
4323 {
4324         MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
4325         if (mtv) {
4326                 mtv->set_note_highlight (new_value);
4327         }
4328
4329         show_verbose_cursor(get_note_name(current_note, new_value), 10, 20);
4330 }
4331
4332 void
4333 MidiRegionView::show_verbose_cursor (boost::shared_ptr<NoteType> n) const
4334 {
4335         show_verbose_cursor_for_new_note_value(n, n->note());
4336 }
4337
4338 void
4339 MidiRegionView::show_verbose_cursor (string const & text, double xoffset, double yoffset) const
4340 {
4341         trackview.editor().verbose_cursor()->set (text);
4342         trackview.editor().verbose_cursor()->show ();
4343         trackview.editor().verbose_cursor()->set_offset (ArdourCanvas::Duple (xoffset, yoffset));
4344 }
4345
4346 uint8_t
4347 MidiRegionView::get_velocity_for_add (MidiModel::TimeType time) const
4348 {
4349         if (_model->notes().empty()) {
4350                 return 0x40;  // No notes, use default
4351         }
4352
4353         MidiModel::Notes::const_iterator m = _model->note_lower_bound(time);
4354         if (m == _model->notes().begin()) {
4355                 // Before the start, use the velocity of the first note
4356                 return (*m)->velocity();
4357         } else if (m == _model->notes().end()) {
4358                 // Past the end, use the velocity of the last note
4359                 --m;
4360                 return (*m)->velocity();
4361         }
4362
4363         // Interpolate velocity of surrounding notes
4364         MidiModel::Notes::const_iterator n = m;
4365         --n;
4366
4367         const double frac = ((time - (*n)->time()).to_double() /
4368                              ((*m)->time() - (*n)->time()).to_double());
4369
4370         return (*n)->velocity() + (frac * ((*m)->velocity() - (*n)->velocity()));
4371 }
4372
4373 /** @param p A session samplepos.
4374  *  @param divisions beat division to snap given by Editor::get_grid_music_divisions() where
4375  *  bar is -1, 0 is audio samples and a positive integer is beat subdivisions.
4376  *  @return beat duration of p snapped to the grid subdivision underneath it.
4377  */
4378 Temporal::Beats
4379 MidiRegionView::snap_sample_to_grid_underneath (samplepos_t p, int32_t divisions, bool shift_snap) const
4380 {
4381         TempoMap& map (trackview.session()->tempo_map());
4382         double eqaf = map.exact_qn_at_sample (p + _region->position(), divisions);
4383
4384         if (divisions != 0 && shift_snap) {
4385                 const double qaf = map.quarter_note_at_sample (p + _region->position());
4386                 /* Hack so that we always snap to the note that we are over, instead of snapping
4387                    to the next one if we're more than halfway through the one we're over.
4388                 */
4389                 const Temporal::Beats grid_beats = get_grid_beats (p + _region->position());
4390                 const double rem = eqaf - qaf;
4391                 if (rem >= 0.0) {
4392                         eqaf -= grid_beats.to_double();
4393                 }
4394         }
4395         const double session_start_off = _region->quarter_note() - midi_region()->start_beats();
4396
4397         return Temporal::Beats (eqaf - session_start_off);
4398 }
4399
4400 ChannelMode
4401 MidiRegionView::get_channel_mode () const
4402 {
4403         RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (&trackview);
4404         return rtav->midi_track()->get_playback_channel_mode();
4405 }
4406
4407 uint16_t
4408 MidiRegionView::get_selected_channels () const
4409 {
4410         RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (&trackview);
4411         return rtav->midi_track()->get_playback_channel_mask();
4412 }
4413
4414
4415 Temporal::Beats
4416 MidiRegionView::get_grid_beats(samplepos_t pos) const
4417 {
4418         PublicEditor& editor  = trackview.editor();
4419         bool          success = false;
4420         Temporal::Beats beats   = editor.get_grid_type_as_beats (success, pos);
4421         if (!success) {
4422                 beats = Temporal::Beats(1);
4423         }
4424         return beats;
4425 }
4426 uint8_t
4427 MidiRegionView::y_to_note (double y) const
4428 {
4429         int const n = ((contents_height() - y) / contents_height() * (double)(_current_range_max - _current_range_min + 1))
4430                 + _current_range_min;
4431
4432         if (n < 0) {
4433                 return 0;
4434         } else if (n > 127) {
4435                 return 127;
4436         }
4437
4438         /* min due to rounding and/or off-by-one errors */
4439         return min ((uint8_t) n, _current_range_max);
4440 }
4441
4442 double
4443 MidiRegionView::note_to_y(uint8_t note) const
4444 {
4445         return contents_height() - (note + 1 - _current_range_min) * note_height() + 1;
4446 }
4447
4448 double
4449 MidiRegionView::session_relative_qn (double qn) const
4450 {
4451         return qn + (region()->quarter_note() - midi_region()->start_beats());
4452 }