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