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