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