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