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