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