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