d90430514641c622bf52a403e35821f0d2c610e3
[ardour.git] / gtk2_ardour / midi_region_view.cc
1 /*
2     Copyright (C) 2001-2007 Paul Davis
3     Author: Dave Robillard
4
5     This program is free software; you can redistribute it and/or modify
6     it under the terms of the GNU General Public License as published by
7     the Free Software Foundation; either version 2 of the License, or
8     (at your option) any later version.
9
10     This program is distributed in the hope that it will be useful,
11     but WITHOUT ANY WARRANTY; without even the implied warranty of
12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13     GNU General Public License for more details.
14
15     You should have received a copy of the GNU General Public License
16     along with this program; if not, write to the Free Software
17     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 */
19
20 #include <cmath>
21 #include <cassert>
22 #include <algorithm>
23 #include <ostream>
24
25 #include <gtkmm.h>
26
27 #include <gtkmm2ext/gtk_ui.h>
28
29 #include <sigc++/signal.h>
30
31 #include "pbd/memento_command.h"
32 #include "pbd/stateful_diff_command.h"
33
34 #include "ardour/playlist.h"
35 #include "ardour/tempo.h"
36 #include "ardour/midi_region.h"
37 #include "ardour/midi_source.h"
38 #include "ardour/midi_model.h"
39 #include "ardour/midi_patch_manager.h"
40 #include "ardour/session.h"
41
42 #include "evoral/Parameter.hpp"
43 #include "evoral/Control.hpp"
44 #include "evoral/midi_util.h"
45
46 #include "automation_region_view.h"
47 #include "automation_time_axis.h"
48 #include "canvas-hit.h"
49 #include "canvas-note.h"
50 #include "canvas-program-change.h"
51 #include "ghostregion.h"
52 #include "gui_thread.h"
53 #include "keyboard.h"
54 #include "midi_cut_buffer.h"
55 #include "midi_list_editor.h"
56 #include "midi_region_view.h"
57 #include "midi_streamview.h"
58 #include "midi_time_axis.h"
59 #include "midi_time_axis.h"
60 #include "midi_util.h"
61 #include "public_editor.h"
62 #include "selection.h"
63 #include "simpleline.h"
64 #include "streamview.h"
65 #include "utils.h"
66
67 #include "i18n.h"
68
69 using namespace ARDOUR;
70 using namespace PBD;
71 using namespace Editing;
72 using namespace ArdourCanvas;
73 using Gtkmm2ext::Keyboard;
74
75 MidiRegionView::MidiRegionView (ArdourCanvas::Group *parent, RouteTimeAxisView &tv,
76                 boost::shared_ptr<MidiRegion> r, double spu, Gdk::Color const & basic_color)
77         : RegionView (parent, tv, r, spu, basic_color)
78         , _force_channel(-1)
79         , _last_channel_selection(0xFFFF)
80         , _current_range_min(0)
81         , _current_range_max(0)
82         , _model_name(string())
83         , _custom_device_mode(string())
84         , _active_notes(0)
85         , _note_group(new ArdourCanvas::Group(*parent))
86         , _delta_command(0)
87         , _diff_command(0)
88         , _mouse_state(None)
89         , _pressed_button(0)
90         , _sort_needed (true)
91         , _optimization_iterator (_events.end())
92         , _list_editor (0)
93         , no_sound_notes (false)
94 {
95         _note_group->raise_to_top();
96 }
97
98 MidiRegionView::MidiRegionView (ArdourCanvas::Group *parent, RouteTimeAxisView &tv,
99                 boost::shared_ptr<MidiRegion> r, double spu, Gdk::Color& basic_color,
100                 TimeAxisViewItem::Visibility visibility)
101         : RegionView (parent, tv, r, spu, basic_color, false, visibility)
102         , _force_channel(-1)
103         , _last_channel_selection(0xFFFF)
104         , _model_name(string())
105         , _custom_device_mode(string())
106         , _active_notes(0)
107         , _note_group(new ArdourCanvas::Group(*parent))
108         , _delta_command(0)
109         , _diff_command(0)
110         , _mouse_state(None)
111         , _pressed_button(0)
112         , _sort_needed (true)
113         , _optimization_iterator (_events.end())
114         , _list_editor (0)
115         , no_sound_notes (false)
116
117 {
118         _note_group->raise_to_top();
119 }
120
121
122 MidiRegionView::MidiRegionView (const MidiRegionView& other)
123         : sigc::trackable(other)
124         , RegionView (other)
125         , _force_channel(-1)
126         , _last_channel_selection(0xFFFF)
127         , _model_name(string())
128         , _custom_device_mode(string())
129         , _active_notes(0)
130         , _note_group(new ArdourCanvas::Group(*get_canvas_group()))
131         , _delta_command(0)
132         , _diff_command(0)
133         , _mouse_state(None)
134         , _pressed_button(0)
135         , _sort_needed (true)
136         , _optimization_iterator (_events.end())
137         , _list_editor (0)
138         , no_sound_notes (false)
139 {
140         Gdk::Color c;
141         int r,g,b,a;
142
143         UINT_TO_RGBA (other.fill_color, &r, &g, &b, &a);
144         c.set_rgb_p (r/255.0, g/255.0, b/255.0);
145
146         init (c, false);
147 }
148
149 MidiRegionView::MidiRegionView (const MidiRegionView& other, boost::shared_ptr<MidiRegion> region)
150         : RegionView (other, boost::shared_ptr<Region> (region))
151         , _force_channel(-1)
152         , _last_channel_selection(0xFFFF)
153         , _model_name(string())
154         , _custom_device_mode(string())
155         , _active_notes(0)
156         , _note_group(new ArdourCanvas::Group(*get_canvas_group()))
157         , _delta_command(0)
158         , _diff_command(0)
159         , _mouse_state(None)
160         , _pressed_button(0)
161         , _sort_needed (true)
162         , _optimization_iterator (_events.end())
163         , _list_editor (0)
164         , no_sound_notes (false)
165 {
166         Gdk::Color c;
167         int r,g,b,a;
168
169         UINT_TO_RGBA (other.fill_color, &r, &g, &b, &a);
170         c.set_rgb_p (r/255.0, g/255.0, b/255.0);
171
172         init (c, true);
173 }
174
175 void
176 MidiRegionView::init (Gdk::Color const & basic_color, bool wfd)
177 {
178         CanvasNoteEvent::CanvasNoteEventDeleted.connect (note_delete_connection, MISSING_INVALIDATOR, 
179                                                          ui_bind (&MidiRegionView::maybe_remove_deleted_note_from_selection, this, _1),
180                                                          gui_context());
181
182         if (wfd) {
183                 midi_region()->midi_source(0)->load_model();
184         }
185
186         _model = midi_region()->midi_source(0)->model();
187         _enable_display = false;
188
189         RegionView::init (basic_color, false);
190
191         compute_colors (basic_color);
192
193         set_height (trackview.current_height());
194
195         region_muted ();
196         region_sync_changed ();
197         region_resized (ARDOUR::bounds_change);
198         region_locked ();
199
200         reset_width_dependent_items (_pixel_width);
201
202         set_colors ();
203
204         _enable_display = true;
205         if (_model) {
206                 if (wfd) {
207                         display_model (_model);
208                 }
209         }
210
211         group->raise_to_top();
212         group->signal_event().connect (sigc::mem_fun (this, &MidiRegionView::canvas_event), false);
213
214         midi_view()->signal_channel_mode_changed().connect(
215                         sigc::mem_fun(this, &MidiRegionView::midi_channel_mode_changed));
216
217         midi_view()->signal_midi_patch_settings_changed().connect(
218                         sigc::mem_fun(this, &MidiRegionView::midi_patch_settings_changed));
219 }
220
221 bool
222 MidiRegionView::canvas_event(GdkEvent* ev)
223 {
224         PublicEditor& editor (trackview.editor());
225
226         if (!editor.internal_editing()) {
227                 return false;
228         }
229
230         static double drag_start_x, drag_start_y;
231         static double last_x, last_y;
232         double event_x, event_y;
233         nframes64_t event_frame = 0;
234         bool fine;
235
236         static ArdourCanvas::SimpleRect* drag_rect = 0;
237
238         /* XXX: note that as of August 2009, the GnomeCanvas does not propagate scroll events
239            to its items, which means that ev->type == GDK_SCROLL will never be seen
240         */
241
242         switch (ev->type) {
243         case GDK_SCROLL:
244                 fine = Keyboard::modifier_state_equals (ev->scroll.state, Keyboard::Level4Modifier);
245
246                 if (ev->scroll.direction == GDK_SCROLL_UP) {
247                         change_velocities (true, fine, false);
248                         return true;
249                 } else if (ev->scroll.direction == GDK_SCROLL_DOWN) {
250                         change_velocities (false, fine, false);
251                         return true;
252                 } else {
253                         return false;
254                 }
255                 break;
256
257         case GDK_KEY_PRESS:
258
259                 /* since GTK bindings are generally activated on press, and since
260                    detectable auto-repeat is the name of the game and only sends
261                    repeated presses, carry out key actions at key press, not release.
262                 */
263
264                 if (ev->key.keyval == GDK_Alt_L || ev->key.keyval == GDK_Alt_R){
265                         _mouse_state = SelectTouchDragging;
266                         return true;
267
268                 } else if (ev->key.keyval == GDK_Escape) {
269                         clear_selection();
270                         _mouse_state = None;
271
272                 } else if (ev->key.keyval == GDK_comma || ev->key.keyval == GDK_period) {
273
274                         bool start = (ev->key.keyval == GDK_comma);
275                         bool end = (ev->key.keyval == GDK_period);
276                         bool shorter = Keyboard::modifier_state_contains (ev->key.state, Keyboard::PrimaryModifier);
277                         fine = Keyboard::modifier_state_contains (ev->key.state, Keyboard::SecondaryModifier);
278
279                         change_note_lengths (fine, shorter, start, end);
280
281                         return true;
282
283                 } else if (ev->key.keyval == GDK_Delete) {
284
285                         delete_selection();
286                         return true;
287
288                 } else if (ev->key.keyval == GDK_Tab) {
289
290                         if (Keyboard::modifier_state_equals (ev->key.state, Keyboard::PrimaryModifier)) {
291                                 goto_previous_note ();
292                         } else {
293                                 goto_next_note ();
294                         }
295                         return true;
296
297                 } else if (ev->key.keyval == GDK_Up) {
298
299                         bool allow_smush = Keyboard::modifier_state_contains (ev->key.state, Keyboard::SecondaryModifier);
300                         bool fine = Keyboard::modifier_state_contains (ev->key.state, Keyboard::TertiaryModifier);
301
302                         if (Keyboard::modifier_state_contains (ev->key.state, Keyboard::PrimaryModifier)) {
303                                 change_velocities (true, fine, allow_smush);
304                         } else {
305                                 transpose (true, fine, allow_smush);
306                         }
307                         return true;
308
309                 } else if (ev->key.keyval == GDK_Down) {
310
311                         bool allow_smush = Keyboard::modifier_state_contains (ev->key.state, Keyboard::SecondaryModifier);
312                         fine = Keyboard::modifier_state_contains (ev->key.state, Keyboard::TertiaryModifier);
313
314                         if (Keyboard::modifier_state_contains (ev->key.state, Keyboard::PrimaryModifier)) {
315                                 change_velocities (false, fine, allow_smush);
316                         } else {
317                                 transpose (false, fine, allow_smush);
318                         }
319                         return true;
320
321                 } else if (ev->key.keyval == GDK_Left) {
322
323                         nudge_notes (false);
324                         return true;
325
326                 } else if (ev->key.keyval == GDK_Right) {
327
328                         nudge_notes (true);
329                         return true;
330
331                 } else if (ev->key.keyval == GDK_Control_L) {
332                         return true;
333
334                 } else if (ev->key.keyval == GDK_r) {
335                         /* if we're not step editing, this really doesn't matter */
336                         midi_view()->step_edit_rest ();
337                         return true;
338                 }
339
340                 return false;
341
342         case GDK_KEY_RELEASE:
343                 if (ev->key.keyval == GDK_Alt_L || ev->key.keyval == GDK_Alt_R) {
344                         _mouse_state = None;
345                         return true;
346                 }
347                 return false;
348
349         case GDK_BUTTON_PRESS:
350                 last_x = ev->button.x;
351                 last_y = ev->button.y;
352                 group->w2i (last_x, last_y);
353
354                 if (_mouse_state != SelectTouchDragging && ev->button.button == 1) {
355                         _pressed_button = ev->button.button;
356                         _mouse_state = Pressed;
357                         return true;
358                 }
359                 _pressed_button = ev->button.button;
360                 return true;
361
362         case GDK_2BUTTON_PRESS:
363                 return true;
364
365         case GDK_ENTER_NOTIFY:
366                 /* FIXME: do this on switch to note tool, too, if the pointer is already in */
367                 Keyboard::magic_widget_grab_focus();
368                 group->grab_focus();
369                 break;
370
371         case GDK_MOTION_NOTIFY:
372                 event_x = ev->motion.x;
373                 event_y = ev->motion.y;
374                 group->w2i(event_x, event_y);
375
376                 // convert event_x to global frame
377                 event_frame = trackview.editor().pixel_to_frame(event_x) + _region->position();
378                 trackview.editor().snap_to(event_frame);
379                 // convert event_frame back to local coordinates relative to position
380                 event_frame -= _region->position();
381
382                 switch (_mouse_state) {
383                 case Pressed: // Maybe start a drag, if we've moved a bit
384
385                         if (fabs (event_x - last_x) < 1 && fabs (event_y - last_y) < 1) {
386                                 /* no appreciable movement since the button was pressed */
387                                 return false;
388                         }
389
390                         // Select drag start
391                         if (_pressed_button == 1 && editor.current_mouse_mode() == MouseObject) {
392                                 group->grab(GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK,
393                                                 Gdk::Cursor(Gdk::FLEUR), ev->motion.time);
394                                 last_x = event_x;
395                                 last_y = event_y;
396                                 drag_start_x = event_x;
397                                 drag_start_y = event_y;
398
399                                 drag_rect = new ArdourCanvas::SimpleRect(*group);
400                                 drag_rect->property_x1() = event_x;
401                                 drag_rect->property_y1() = event_y;
402                                 drag_rect->property_x2() = event_x;
403                                 drag_rect->property_y2() = event_y;
404                                 drag_rect->property_outline_what() = 0xFF;
405                                 drag_rect->property_outline_color_rgba()
406                                         = ARDOUR_UI::config()->canvasvar_MidiSelectRectOutline.get();
407                                 drag_rect->property_fill_color_rgba()
408                                         = ARDOUR_UI::config()->canvasvar_MidiSelectRectFill.get();
409
410                                 _mouse_state = SelectRectDragging;
411                                 return true;
412
413                         // Add note drag start
414                         } else if (editor.current_mouse_mode() == MouseRange) {
415                                 group->grab(GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK,
416                                                 Gdk::Cursor(Gdk::FLEUR), ev->motion.time);
417                                 last_x = event_x;
418                                 last_y = event_y;
419                                 drag_start_x = event_x;
420                                 drag_start_y = event_y;
421
422                                 drag_rect = new ArdourCanvas::SimpleRect(*group);
423                                 drag_rect->property_x1() = trackview.editor().frame_to_pixel(event_frame);
424
425                                 drag_rect->property_y1() = midi_stream_view()->note_to_y(
426                                                 midi_stream_view()->y_to_note(event_y));
427                                 drag_rect->property_x2() = event_x;
428                                 drag_rect->property_y2() = drag_rect->property_y1()
429                                                          + floor(midi_stream_view()->note_height());
430                                 drag_rect->property_outline_what() = 0xFF;
431                                 drag_rect->property_outline_color_rgba() = 0xFFFFFF99;
432                                 drag_rect->property_fill_color_rgba()    = 0xFFFFFF66;
433
434                                 _mouse_state = AddDragging;
435                                 return true;
436                         }
437
438                         return false;
439
440                 case SelectRectDragging: // Select drag motion
441                 case AddDragging: // Add note drag motion
442                         if (ev->motion.is_hint) {
443                                 int t_x;
444                                 int t_y;
445                                 GdkModifierType state;
446                                 gdk_window_get_pointer(ev->motion.window, &t_x, &t_y, &state);
447                                 event_x = t_x;
448                                 event_y = t_y;
449                         }
450
451                         if (_mouse_state == AddDragging)
452                                 event_x = trackview.editor().frame_to_pixel(event_frame);
453
454                         if (drag_rect) {
455                                 if (event_x > drag_start_x)
456                                         drag_rect->property_x2() = event_x;
457                                 else
458                                         drag_rect->property_x1() = event_x;
459                         }
460
461                         if (drag_rect && _mouse_state == SelectRectDragging) {
462                                 if (event_y > drag_start_y)
463                                         drag_rect->property_y2() = event_y;
464                                 else
465                                         drag_rect->property_y1() = event_y;
466
467                                 update_drag_selection(drag_start_x, event_x, drag_start_y, event_y);
468                         }
469
470                         last_x = event_x;
471                         last_y = event_y;
472
473                 case SelectTouchDragging:
474                         return false;
475
476                 default:
477                         break;
478                 }
479                 break;
480
481         case GDK_BUTTON_RELEASE:
482                 event_x = ev->motion.x;
483                 event_y = ev->motion.y;
484                 group->w2i(event_x, event_y);
485                 group->ungrab(ev->button.time);
486                 event_frame = trackview.editor().pixel_to_frame(event_x);
487
488                 if (ev->button.button == 3) {
489                         return false;
490                 } else if (_pressed_button != 1) {
491                         return false;
492                 }
493
494                 switch (_mouse_state) {
495                 case Pressed: // Clicked
496                         switch (editor.current_mouse_mode()) {
497                         case MouseObject:
498                         case MouseTimeFX:
499                                 clear_selection();
500                                 break;
501                         case MouseRange:
502                         {
503                                 bool success;
504                                 Evoral::MusicalTime beats = trackview.editor().get_grid_type_as_beats (success, trackview.editor().pixel_to_frame (event_x));
505                                 if (!success) {
506                                         beats = 1;
507                                 }
508
509                                 create_note_at (event_x, event_y, beats);
510                                 break;
511                         }
512                         default:
513                                 break;
514                         }
515                         _mouse_state = None;
516                         break;
517                 case SelectRectDragging: // Select drag done
518                         _mouse_state = None;
519                         delete drag_rect;
520                         drag_rect = 0;
521                         break;
522                 case AddDragging: // Add drag done
523                         _mouse_state = None;
524                         if (drag_rect->property_x2() > drag_rect->property_x1() + 2) {
525                                 const double x      = drag_rect->property_x1();
526                                 const double length = trackview.editor().pixel_to_frame(
527                                                         drag_rect->property_x2() - drag_rect->property_x1());
528
529                                 create_note_at(x, drag_rect->property_y1(), frames_to_beats(length));
530                         }
531
532                         delete drag_rect;
533                         drag_rect = 0;
534                 default: break;
535                 }
536
537         default: break;
538         }
539
540         return false;
541 }
542
543 void
544 MidiRegionView::show_list_editor ()
545 {
546         if (!_list_editor) {
547                 _list_editor = new MidiListEditor (trackview.session(), midi_region());
548         }
549         _list_editor->present ();
550 }
551
552 /** Add a note to the model, and the view, at a canvas (click) coordinate.
553  * \param x horizontal position in pixels
554  * \param y vertical position in pixels
555  * \param length duration of the note in beats */
556 void
557 MidiRegionView::create_note_at(double x, double y, double length)
558 {
559         MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
560         MidiStreamView* const view = mtv->midi_view();
561
562         double note = midi_stream_view()->y_to_note(y);
563
564         assert(note >= 0.0);
565         assert(note <= 127.0);
566
567         // Start of note in frames relative to region start
568         nframes64_t start_frames = snap_frame_to_frame(trackview.editor().pixel_to_frame(x));
569         assert(start_frames >= 0);
570
571         // Snap length
572         length = frames_to_beats(
573                         snap_frame_to_frame(start_frames + beats_to_frames(length)) - start_frames);
574
575         const boost::shared_ptr<NoteType> new_note(new NoteType(0,
576                         frames_to_beats(start_frames + _region->start()), length,
577                         (uint8_t)note, 0x40));
578
579         if (_model->contains (new_note)) {
580                 return;
581         }
582
583         view->update_note_range(new_note->note());
584
585         MidiModel::DeltaCommand* cmd = _model->new_delta_command("add note");
586         cmd->add(new_note);
587         _model->apply_command(*trackview.session(), cmd);
588
589         play_midi_note (new_note);
590 }
591
592 void
593 MidiRegionView::clear_events()
594 {
595         clear_selection();
596
597         MidiGhostRegion* gr;
598         for (std::vector<GhostRegion*>::iterator g = ghosts.begin(); g != ghosts.end(); ++g) {
599                 if ((gr = dynamic_cast<MidiGhostRegion*>(*g)) != 0) {
600                         gr->clear_events();
601                 }
602         }
603
604         for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
605                 delete *i;
606         }
607
608         _events.clear();
609         _pgm_changes.clear();
610         _sys_exes.clear();
611         _optimization_iterator = _events.end();
612 }
613
614
615 void
616 MidiRegionView::display_model(boost::shared_ptr<MidiModel> model)
617 {
618         _model = model;
619         content_connection.disconnect ();
620         _model->ContentsChanged.connect (content_connection, invalidator (*this), boost::bind (&MidiRegionView::redisplay_model, this), gui_context());
621
622         clear_events ();
623
624         if (_enable_display) {
625                 redisplay_model();
626         }
627 }
628
629
630 void
631 MidiRegionView::start_delta_command(string name)
632 {
633         if (!_delta_command) {
634                 _delta_command = _model->new_delta_command(name);
635         }
636 }
637
638 void
639 MidiRegionView::start_diff_command(string name)
640 {
641         if (!_diff_command) {
642                 _diff_command = _model->new_diff_command(name);
643         }
644 }
645
646 void
647 MidiRegionView::delta_add_note(const boost::shared_ptr<NoteType> note, bool selected, bool show_velocity)
648 {
649         if (_delta_command) {
650                 _delta_command->add(note);
651         }
652         if (selected) {
653                 _marked_for_selection.insert(note);
654         }
655         if (show_velocity) {
656                 _marked_for_velocity.insert(note);
657         }
658 }
659
660 void
661 MidiRegionView::delta_remove_note(ArdourCanvas::CanvasNoteEvent* ev)
662 {
663         if (_delta_command && ev->note()) {
664                 _delta_command->remove(ev->note());
665         }
666 }
667
668 void
669 MidiRegionView::diff_add_change (ArdourCanvas::CanvasNoteEvent* ev,
670                                  MidiModel::DiffCommand::Property property,
671                                  uint8_t val)
672 {
673         if (_diff_command) {
674                 _diff_command->change (ev->note(), property, val);
675         }
676 }
677
678 void
679 MidiRegionView::diff_add_change (ArdourCanvas::CanvasNoteEvent* ev,
680                                  MidiModel::DiffCommand::Property property,
681                                  Evoral::MusicalTime val)
682 {
683         if (_diff_command) {
684                 _diff_command->change (ev->note(), property, val);
685         }
686 }
687
688 void
689 MidiRegionView::apply_delta()
690 {
691         if (!_delta_command) {
692                 return;
693         }
694
695         // Mark all selected notes for selection when model reloads
696         for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
697                 _marked_for_selection.insert((*i)->note());
698         }
699
700         _model->apply_command(*trackview.session(), _delta_command);
701         _delta_command = 0;
702         midi_view()->midi_track()->playlist_modified();
703
704         _marked_for_selection.clear();
705         _marked_for_velocity.clear();
706 }
707
708 void
709 MidiRegionView::apply_diff ()
710 {
711         if (!_diff_command) {
712                 return;
713         }
714
715         _model->apply_command(*trackview.session(), _diff_command);
716         _diff_command = 0;
717         midi_view()->midi_track()->playlist_modified();
718
719         _marked_for_velocity.clear();
720 }
721
722 void
723 MidiRegionView::apply_delta_as_subcommand()
724 {
725         if (!_delta_command) {
726                 return;
727         }
728
729         // Mark all selected notes for selection when model reloads
730         for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
731                 _marked_for_selection.insert((*i)->note());
732         }
733
734         _model->apply_command_as_subcommand(*trackview.session(), _delta_command);
735         _delta_command = 0;
736         midi_view()->midi_track()->playlist_modified();
737
738         _marked_for_selection.clear();
739         _marked_for_velocity.clear();
740 }
741
742 void
743 MidiRegionView::apply_diff_as_subcommand()
744 {
745         if (!_diff_command) {
746                 return;
747         }
748
749         // Mark all selected notes for selection when model reloads
750         for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
751                 _marked_for_selection.insert((*i)->note());
752         }
753
754         _model->apply_command_as_subcommand(*trackview.session(), _diff_command);
755         _diff_command = 0;
756         midi_view()->midi_track()->playlist_modified();
757
758         _marked_for_selection.clear();
759         _marked_for_velocity.clear();
760 }
761
762 void
763 MidiRegionView::abort_command()
764 {
765         delete _delta_command;
766         _delta_command = 0;
767         delete _diff_command;
768         _diff_command = 0;
769         clear_selection();
770 }
771
772 CanvasNoteEvent*
773 MidiRegionView::find_canvas_note (boost::shared_ptr<NoteType> note)
774 {
775         if (_optimization_iterator != _events.end()) {
776                 ++_optimization_iterator;
777         }
778
779         if (_optimization_iterator != _events.end() && (*_optimization_iterator)->note() == note) {
780                 return *_optimization_iterator;
781         }
782
783         for (_optimization_iterator = _events.begin(); _optimization_iterator != _events.end(); ++_optimization_iterator) {
784                 if ((*_optimization_iterator)->note() == note) {
785                         return *_optimization_iterator;
786                 }
787         }
788
789         return 0;
790 }
791
792 void
793 MidiRegionView::redisplay_model()
794 {
795         // Don't redisplay the model if we're currently recording and displaying that
796         if (_active_notes) {
797                 return;
798         }
799
800         if (!_model) {
801                 cerr << "MidiRegionView::redisplay_model called without a model" << endmsg;
802                 return;
803         }
804
805         for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
806                 (*i)->invalidate ();
807         }
808
809         MidiModel::ReadLock lock(_model->read_lock());
810
811         MidiModel::Notes& notes (_model->notes());
812         _optimization_iterator = _events.begin();
813
814         for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
815
816                 boost::shared_ptr<NoteType> note (*n);
817                 CanvasNoteEvent* cne;
818                 bool visible;
819
820                 if (note_in_region_range (note, visible)) {
821
822                         if ((cne = find_canvas_note (note)) != 0) {
823
824                                 cne->validate ();
825
826                                 CanvasNote* cn;
827                                 CanvasHit* ch;
828
829                                 if ((cn = dynamic_cast<CanvasNote*>(cne)) != 0) {
830                                         update_note (cn);
831                                 } else if ((ch = dynamic_cast<CanvasHit*>(cne)) != 0) {
832                                         update_hit (ch);
833                                 }
834
835                                 if (visible) {
836                                         cne->show ();
837                                 } else {
838                                         cne->hide ();
839                                 }
840
841                         } else {
842
843                                 add_note (note, visible);
844                         }
845
846                 } else {
847
848                         if ((cne = find_canvas_note (note)) != 0) {
849                                 cne->validate ();
850                                 cne->hide ();
851                         }
852                 }
853         }
854
855
856         /* remove note items that are no longer valid */
857
858         for (Events::iterator i = _events.begin(); i != _events.end(); ) {
859                 if (!(*i)->valid ()) {
860                         delete *i;
861                         i = _events.erase (i);
862                 } else {
863                         ++i;
864                 }
865         }
866
867         display_sysexes();
868         display_program_changes();
869
870         _marked_for_selection.clear ();
871         _marked_for_velocity.clear ();
872
873         /* we may have caused _events to contain things out of order (e.g. if a note
874            moved earlier or later). we don't generally need them in time order, but
875            make a note that a sort is required for those cases that require it.
876         */
877
878         _sort_needed = true;
879 }
880
881 void
882 MidiRegionView::display_program_changes()
883 {
884         boost::shared_ptr<Evoral::Control> control = _model->control(MidiPgmChangeAutomation);
885         if (!control) {
886                 return;
887         }
888
889         Glib::Mutex::Lock lock (control->list()->lock());
890
891         uint8_t channel = control->parameter().channel();
892
893         for (AutomationList::const_iterator event = control->list()->begin();
894                         event != control->list()->end(); ++event) {
895                 double event_time     = (*event)->when;
896                 double program_number = floor((*event)->value + 0.5);
897
898                 // Get current value of bank select MSB at time of the program change
899                 Evoral::Parameter bank_select_msb(MidiCCAutomation, channel, MIDI_CTL_MSB_BANK);
900                 boost::shared_ptr<Evoral::Control> msb_control = _model->control(bank_select_msb);
901                 uint8_t msb = 0;
902                 if (msb_control != 0) {
903                         msb = uint8_t(floor(msb_control->get_float(true, event_time) + 0.5));
904                 }
905
906                 // Get current value of bank select LSB at time of the program change
907                 Evoral::Parameter bank_select_lsb(MidiCCAutomation, channel, MIDI_CTL_LSB_BANK);
908                 boost::shared_ptr<Evoral::Control> lsb_control = _model->control(bank_select_lsb);
909                 uint8_t lsb = 0;
910                 if (lsb_control != 0) {
911                         lsb = uint8_t(floor(lsb_control->get_float(true, event_time) + 0.5));
912                 }
913
914                 MIDI::Name::PatchPrimaryKey patch_key(msb, lsb, program_number);
915
916                 boost::shared_ptr<MIDI::Name::Patch> patch =
917                         MIDI::Name::MidiPatchManager::instance().find_patch(
918                                         _model_name, _custom_device_mode, channel, patch_key);
919
920                 PCEvent program_change(event_time, uint8_t(program_number), channel);
921
922                 if (patch != 0) {
923                         add_pgm_change(program_change, patch->name());
924                 } else {
925                         char buf[4];
926                         snprintf(buf, 4, "%d", int(program_number));
927                         add_pgm_change(program_change, buf);
928                 }
929         }
930 }
931
932 void
933 MidiRegionView::display_sysexes()
934 {
935         for (MidiModel::SysExes::const_iterator i = _model->sysexes().begin(); i != _model->sysexes().end(); ++i) {
936                 Evoral::MusicalTime time = (*i)->time();
937                 assert(time >= 0);
938
939                 ostringstream str;
940                 str << hex;
941                 for (uint32_t b = 0; b < (*i)->size(); ++b) {
942                         str << int((*i)->buffer()[b]);
943                         if (b != (*i)->size() -1) {
944                                 str << " ";
945                         }
946                 }
947                 string text = str.str();
948
949                 ArdourCanvas::Group* const group = (ArdourCanvas::Group*)get_canvas_group();
950
951                 const double x = trackview.editor().frame_to_pixel(beats_to_frames(time));
952
953                 double height = midi_stream_view()->contents_height();
954
955                 boost::shared_ptr<CanvasSysEx> sysex = boost::shared_ptr<CanvasSysEx>(
956                                 new CanvasSysEx(*this, *group, text, height, x, 1.0));
957
958                 // Show unless program change is beyond the region bounds
959                 if (time - _region->start() >= _region->length() || time < _region->start()) {
960                         sysex->hide();
961                 } else {
962                         sysex->show();
963                 }
964
965                 _sys_exes.push_back(sysex);
966         }
967 }
968
969
970 MidiRegionView::~MidiRegionView ()
971 {
972         in_destructor = true;
973
974         note_delete_connection.disconnect ();
975
976         delete _list_editor;
977
978         RegionViewGoingAway (this); /* EMIT_SIGNAL */
979
980         if (_active_notes) {
981                 end_write();
982         }
983
984         _selection.clear();
985         clear_events();
986         delete _note_group;
987         delete _delta_command;
988 }
989
990 void
991 MidiRegionView::region_resized (const PropertyChange& what_changed)
992 {
993         RegionView::region_resized(what_changed);
994
995         if (what_changed.contains (ARDOUR::Properties::position)) {
996                 set_duration(_region->length(), 0);
997                 if (_enable_display) {
998                         redisplay_model();
999                 }
1000         }
1001 }
1002
1003 void
1004 MidiRegionView::reset_width_dependent_items (double pixel_width)
1005 {
1006         RegionView::reset_width_dependent_items(pixel_width);
1007         assert(_pixel_width == pixel_width);
1008
1009         if (_enable_display) {
1010                 redisplay_model();
1011         }
1012 }
1013
1014 void
1015 MidiRegionView::set_height (double height)
1016 {
1017         static const double FUDGE = 2.0;
1018         const double old_height = _height;
1019         RegionView::set_height(height);
1020         _height = height - FUDGE;
1021
1022         apply_note_range(midi_stream_view()->lowest_note(),
1023                          midi_stream_view()->highest_note(),
1024                          height != old_height + FUDGE);
1025
1026         if (name_pixbuf) {
1027                 name_pixbuf->raise_to_top();
1028         }
1029 }
1030
1031
1032 /** Apply the current note range from the stream view
1033  * by repositioning/hiding notes as necessary
1034  */
1035 void
1036 MidiRegionView::apply_note_range (uint8_t min, uint8_t max, bool force)
1037 {
1038         if (!_enable_display) {
1039                 return;
1040         }
1041
1042         if (!force && _current_range_min == min && _current_range_max == max) {
1043                 return;
1044         }
1045
1046         _current_range_min = min;
1047         _current_range_max = max;
1048
1049         for (Events::const_iterator i = _events.begin(); i != _events.end(); ++i) {
1050                 CanvasNoteEvent* event = *i;
1051                 boost::shared_ptr<NoteType> note (event->note());
1052
1053                 if (note->note() < _current_range_min ||
1054                     note->note() > _current_range_max) {
1055                         event->hide();
1056                 } else {
1057                         event->show();
1058                 }
1059
1060                 if (CanvasNote* cnote = dynamic_cast<CanvasNote*>(event)) {
1061
1062                         const double y1 = midi_stream_view()->note_to_y(note->note());
1063                         const double y2 = y1 + floor(midi_stream_view()->note_height());
1064
1065                         cnote->property_y1() = y1;
1066                         cnote->property_y2() = y2;
1067
1068                 } else if (CanvasHit* chit = dynamic_cast<CanvasHit*>(event)) {
1069
1070                         double x = trackview.editor().frame_to_pixel(
1071                                 beats_to_frames(note->time()) - _region->start());
1072                         const double diamond_size = midi_stream_view()->note_height() / 2.0;
1073                         double y = midi_stream_view()->note_to_y(event->note()->note())
1074                                 + ((diamond_size-2.0) / 4.0);
1075
1076                         chit->set_height (diamond_size);
1077                         chit->move (x - chit->x1(), y - chit->y1());
1078                         chit->show ();
1079                 }
1080         }
1081 }
1082
1083 GhostRegion*
1084 MidiRegionView::add_ghost (TimeAxisView& tv)
1085 {
1086         CanvasNote* note;
1087
1088         double unit_position = _region->position () / samples_per_unit;
1089         MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*>(&tv);
1090         MidiGhostRegion* ghost;
1091
1092         if (mtv && mtv->midi_view()) {
1093                 /* if ghost is inserted into midi track, use a dedicated midi ghost canvas group
1094                    to allow having midi notes on top of note lines and waveforms.
1095                  */
1096                 ghost = new MidiGhostRegion (*mtv->midi_view(), trackview, unit_position);
1097         } else {
1098                 ghost = new MidiGhostRegion (tv, trackview, unit_position);
1099         }
1100
1101         ghost->set_height ();
1102         ghost->set_duration (_region->length() / samples_per_unit);
1103         ghosts.push_back (ghost);
1104
1105         for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
1106                 if ((note = dynamic_cast<CanvasNote*>(*i)) != 0) {
1107                         ghost->add_note(note);
1108                 }
1109         }
1110
1111         GhostRegion::CatchDeletion.connect (*this, invalidator (*this), ui_bind (&RegionView::remove_ghost, this, _1), gui_context());
1112
1113         return ghost;
1114 }
1115
1116
1117 /** Begin tracking note state for successive calls to add_event
1118  */
1119 void
1120 MidiRegionView::begin_write()
1121 {
1122         assert(!_active_notes);
1123         _active_notes = new CanvasNote*[128];
1124         for (unsigned i=0; i < 128; ++i) {
1125                 _active_notes[i] = 0;
1126         }
1127 }
1128
1129
1130 /** Destroy note state for add_event
1131  */
1132 void
1133 MidiRegionView::end_write()
1134 {
1135         delete[] _active_notes;
1136         _active_notes = 0;
1137         _marked_for_selection.clear();
1138         _marked_for_velocity.clear();
1139 }
1140
1141
1142 /** Resolve an active MIDI note (while recording).
1143  */
1144 void
1145 MidiRegionView::resolve_note(uint8_t note, double end_time)
1146 {
1147         if (midi_view()->note_mode() != Sustained) {
1148                 return;
1149         }
1150
1151         if (_active_notes && _active_notes[note]) {
1152                 const nframes64_t end_time_frames = beats_to_frames(end_time);
1153                 _active_notes[note]->property_x2() = trackview.editor().frame_to_pixel(end_time_frames);
1154                 _active_notes[note]->property_outline_what() = (guint32) 0xF; // all edges
1155                 _active_notes[note] = 0;
1156         }
1157 }
1158
1159
1160 /** Extend active notes to rightmost edge of region (if length is changed)
1161  */
1162 void
1163 MidiRegionView::extend_active_notes()
1164 {
1165         if (!_active_notes) {
1166                 return;
1167         }
1168
1169         for (unsigned i=0; i < 128; ++i) {
1170                 if (_active_notes[i]) {
1171                         _active_notes[i]->property_x2() = trackview.editor().frame_to_pixel(_region->length());
1172                 }
1173         }
1174 }
1175
1176 void
1177 MidiRegionView::play_midi_note(boost::shared_ptr<NoteType> note)
1178 {
1179         if (no_sound_notes || !trackview.editor().sound_notes()) {
1180                 return;
1181         }
1182
1183         RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
1184         assert(route_ui);
1185
1186         route_ui->midi_track()->write_immediate_event(
1187                         note->on_event().size(), note->on_event().buffer());
1188
1189         const double note_length_beats = (note->off_event().time() - note->on_event().time());
1190         nframes_t note_length_ms = beats_to_frames(note_length_beats)
1191                         * (1000 / (double)route_ui->session()->nominal_frame_rate());
1192         Glib::signal_timeout().connect(sigc::bind(sigc::mem_fun(this, &MidiRegionView::play_midi_note_off), note),
1193                         note_length_ms, G_PRIORITY_DEFAULT);
1194 }
1195
1196 bool
1197 MidiRegionView::play_midi_note_off(boost::shared_ptr<NoteType> note)
1198 {
1199         RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
1200         assert(route_ui);
1201
1202         route_ui->midi_track()->write_immediate_event(
1203                         note->off_event().size(), note->off_event().buffer());
1204
1205         return false;
1206 }
1207
1208 bool
1209 MidiRegionView::note_in_region_range(const boost::shared_ptr<NoteType> note, bool& visible) const
1210 {
1211         const nframes64_t note_start_frames = beats_to_frames(note->time());
1212
1213         bool outside = (note_start_frames - _region->start() >= _region->length()) ||
1214                 (note_start_frames < _region->start());
1215
1216         visible = (note->note() >= midi_stream_view()->lowest_note()) &&
1217                 (note->note() <= midi_stream_view()->highest_note());
1218
1219         return !outside;
1220 }
1221
1222 void
1223 MidiRegionView::update_note (CanvasNote* ev)
1224 {
1225         boost::shared_ptr<NoteType> note = ev->note();
1226
1227         const nframes64_t note_start_frames = beats_to_frames(note->time());
1228         const nframes64_t note_end_frames   = beats_to_frames(note->end_time());
1229
1230         const double x = trackview.editor().frame_to_pixel(note_start_frames - _region->start());
1231         const double y1 = midi_stream_view()->note_to_y(note->note());
1232         const double note_endpixel =
1233                 trackview.editor().frame_to_pixel(note_end_frames - _region->start());
1234
1235         ev->property_x1() = x;
1236         ev->property_y1() = y1;
1237         if (note->length() > 0) {
1238                 ev->property_x2() = note_endpixel;
1239         } else {
1240                 ev->property_x2() = trackview.editor().frame_to_pixel(_region->length());
1241         }
1242         ev->property_y2() = y1 + floor(midi_stream_view()->note_height());
1243
1244         if (note->length() == 0) {
1245                 if (_active_notes) {
1246                         assert(note->note() < 128);
1247                         // If this note is already active there's a stuck note,
1248                         // finish the old note rectangle
1249                         if (_active_notes[note->note()]) {
1250                                 CanvasNote* const old_rect = _active_notes[note->note()];
1251                                 boost::shared_ptr<NoteType> old_note = old_rect->note();
1252                                 old_rect->property_x2() = x;
1253                                 old_rect->property_outline_what() = (guint32) 0xF;
1254                         }
1255                         _active_notes[note->note()] = ev;
1256                 }
1257                 /* outline all but right edge */
1258                 ev->property_outline_what() = (guint32) (0x1 & 0x4 & 0x8);
1259         } else {
1260                 /* outline all edges */
1261                 ev->property_outline_what() = (guint32) 0xF;
1262         }
1263 }
1264
1265 void
1266 MidiRegionView::update_hit (CanvasHit* ev)
1267 {
1268         boost::shared_ptr<NoteType> note = ev->note();
1269
1270         const nframes64_t note_start_frames = beats_to_frames(note->time());
1271         const double x = trackview.editor().frame_to_pixel(note_start_frames - _region->start());
1272         const double diamond_size = midi_stream_view()->note_height() / 2.0;
1273         const double y = midi_stream_view()->note_to_y(note->note()) + ((diamond_size-2) / 4.0);
1274
1275         ev->move_to (x, y);
1276 }
1277
1278 /** Add a MIDI note to the view (with length).
1279  *
1280  * If in sustained mode, notes with length 0 will be considered active
1281  * notes, and resolve_note should be called when the corresponding note off
1282  * event arrives, to properly display the note.
1283  */
1284 void
1285 MidiRegionView::add_note(const boost::shared_ptr<NoteType> note, bool visible)
1286 {
1287         CanvasNoteEvent* event = 0;
1288
1289         assert(note->time() >= 0);
1290         assert(midi_view()->note_mode() == Sustained || midi_view()->note_mode() == Percussive);
1291
1292         ArdourCanvas::Group* const group = (ArdourCanvas::Group*)get_canvas_group();
1293
1294         if (midi_view()->note_mode() == Sustained) {
1295
1296                 CanvasNote* ev_rect = new CanvasNote(*this, *group, note);
1297
1298                 update_note (ev_rect);
1299
1300                 event = ev_rect;
1301
1302                 MidiGhostRegion* gr;
1303
1304                 for (std::vector<GhostRegion*>::iterator g = ghosts.begin(); g != ghosts.end(); ++g) {
1305                         if ((gr = dynamic_cast<MidiGhostRegion*>(*g)) != 0) {
1306                                 gr->add_note(ev_rect);
1307                         }
1308                 }
1309
1310         } else if (midi_view()->note_mode() == Percussive) {
1311
1312                 const double diamond_size = midi_stream_view()->note_height() / 2.0;
1313
1314                 CanvasHit* ev_diamond = new CanvasHit(*this, *group, diamond_size, note);
1315
1316                 update_hit (ev_diamond);
1317
1318                 event = ev_diamond;
1319
1320         } else {
1321                 event = 0;
1322         }
1323
1324         if (event) {
1325                 if (_marked_for_selection.find(note) != _marked_for_selection.end()) {
1326                         note_selected(event, true);
1327                 }
1328
1329                 if (_marked_for_velocity.find(note) != _marked_for_velocity.end()) {
1330                         event->show_velocity();
1331                 }
1332                 event->on_channel_selection_change(_last_channel_selection);
1333                 _events.push_back(event);
1334
1335                 if (visible) {
1336                         event->show();
1337                 } else {
1338                         event->hide ();
1339                 }
1340         }
1341 }
1342
1343 void
1344 MidiRegionView::add_note (uint8_t channel, uint8_t number, uint8_t velocity,
1345                           Evoral::MusicalTime pos, Evoral::MusicalTime len)
1346 {
1347         boost::shared_ptr<NoteType> new_note (new NoteType (channel, pos, len, number, velocity));
1348
1349         start_delta_command (_("step add"));
1350         delta_add_note (new_note, true, false);
1351         apply_delta();
1352
1353         /* potentially extend region to hold new note */
1354
1355         nframes64_t end_frame = _region->position() + beats_to_frames (new_note->end_time());
1356         nframes64_t region_end = _region->position() + _region->length() - 1;
1357
1358         if (end_frame > region_end) {
1359                 _region->set_length (end_frame, this);
1360         } else {
1361                 redisplay_model ();
1362         }
1363 }
1364
1365 void
1366 MidiRegionView::add_pgm_change(PCEvent& program, const string& displaytext)
1367 {
1368         assert(program.time >= 0);
1369
1370         ArdourCanvas::Group* const group = (ArdourCanvas::Group*)get_canvas_group();
1371         const double x = trackview.editor().frame_to_pixel(beats_to_frames(program.time));
1372
1373         double height = midi_stream_view()->contents_height();
1374
1375         boost::shared_ptr<CanvasProgramChange> pgm_change = boost::shared_ptr<CanvasProgramChange>(
1376                         new CanvasProgramChange(*this, *group,
1377                                         displaytext,
1378                                         height,
1379                                         x, 1.0,
1380                                         _model_name,
1381                                         _custom_device_mode,
1382                                         program.time, program.channel, program.value));
1383
1384         // Show unless program change is beyond the region bounds
1385         if (program.time - _region->start() >= _region->length() || program.time < _region->start()) {
1386                 pgm_change->hide();
1387         } else {
1388                 pgm_change->show();
1389         }
1390
1391         _pgm_changes.push_back(pgm_change);
1392 }
1393
1394 void
1395 MidiRegionView::get_patch_key_at(double time, uint8_t channel, MIDI::Name::PatchPrimaryKey& key)
1396 {
1397         cerr << "getting patch key at " << time << " for channel " << channel << endl;
1398         Evoral::Parameter bank_select_msb(MidiCCAutomation, channel, MIDI_CTL_MSB_BANK);
1399         boost::shared_ptr<Evoral::Control> msb_control = _model->control(bank_select_msb);
1400         float msb = -1.0;
1401         if (msb_control != 0) {
1402                 msb = int(msb_control->get_float(true, time));
1403                 cerr << "got msb " << msb;
1404         }
1405
1406         Evoral::Parameter bank_select_lsb(MidiCCAutomation, channel, MIDI_CTL_LSB_BANK);
1407         boost::shared_ptr<Evoral::Control> lsb_control = _model->control(bank_select_lsb);
1408         float lsb = -1.0;
1409         if (lsb_control != 0) {
1410                 lsb = lsb_control->get_float(true, time);
1411                 cerr << " got lsb " << lsb;
1412         }
1413
1414         Evoral::Parameter program_change(MidiPgmChangeAutomation, channel, 0);
1415         boost::shared_ptr<Evoral::Control> program_control = _model->control(program_change);
1416         float program_number = -1.0;
1417         if (program_control != 0) {
1418                 program_number = program_control->get_float(true, time);
1419                 cerr << " got program " << program_number << endl;
1420         }
1421
1422         key.msb = (int) floor(msb + 0.5);
1423         key.lsb = (int) floor(lsb + 0.5);
1424         key.program_number = (int) floor(program_number + 0.5);
1425         assert(key.is_sane());
1426 }
1427
1428
1429 void
1430 MidiRegionView::alter_program_change(PCEvent& old_program, const MIDI::Name::PatchPrimaryKey& new_patch)
1431 {
1432         // TODO: Get the real event here and alter them at the original times
1433         Evoral::Parameter bank_select_msb(MidiCCAutomation, old_program.channel, MIDI_CTL_MSB_BANK);
1434         boost::shared_ptr<Evoral::Control> msb_control = _model->control(bank_select_msb);
1435         if (msb_control != 0) {
1436                 msb_control->set_float(float(new_patch.msb), true, old_program.time);
1437         }
1438
1439         // TODO: Get the real event here and alter them at the original times
1440         Evoral::Parameter bank_select_lsb(MidiCCAutomation, old_program.channel, MIDI_CTL_LSB_BANK);
1441         boost::shared_ptr<Evoral::Control> lsb_control = _model->control(bank_select_lsb);
1442         if (lsb_control != 0) {
1443                 lsb_control->set_float(float(new_patch.lsb), true, old_program.time);
1444         }
1445
1446         Evoral::Parameter program_change(MidiPgmChangeAutomation, old_program.channel, 0);
1447         boost::shared_ptr<Evoral::Control> program_control = _model->control(program_change);
1448
1449         assert(program_control != 0);
1450         program_control->set_float(float(new_patch.program_number), true, old_program.time);
1451
1452         redisplay_model();
1453 }
1454
1455 void
1456 MidiRegionView::program_selected(CanvasProgramChange& program, const MIDI::Name::PatchPrimaryKey& new_patch)
1457 {
1458         PCEvent program_change_event(program.event_time(), program.program(), program.channel());
1459         alter_program_change(program_change_event, new_patch);
1460 }
1461
1462 void
1463 MidiRegionView::previous_program(CanvasProgramChange& program)
1464 {
1465         MIDI::Name::PatchPrimaryKey key;
1466         get_patch_key_at(program.event_time(), program.channel(), key);
1467
1468         boost::shared_ptr<MIDI::Name::Patch> patch =
1469                 MIDI::Name::MidiPatchManager::instance().previous_patch(
1470                                 _model_name,
1471                                 _custom_device_mode,
1472                                 program.channel(),
1473                                 key);
1474
1475         PCEvent program_change_event(program.event_time(), program.program(), program.channel());
1476         if (patch) {
1477                 alter_program_change(program_change_event, patch->patch_primary_key());
1478         }
1479 }
1480
1481 void
1482 MidiRegionView::next_program(CanvasProgramChange& program)
1483 {
1484         MIDI::Name::PatchPrimaryKey key;
1485         get_patch_key_at(program.event_time(), program.channel(), key);
1486
1487         boost::shared_ptr<MIDI::Name::Patch> patch =
1488                 MIDI::Name::MidiPatchManager::instance().next_patch(
1489                                 _model_name,
1490                                 _custom_device_mode,
1491                                 program.channel(),
1492                                 key);
1493
1494         PCEvent program_change_event(program.event_time(), program.program(), program.channel());
1495         if (patch) {
1496                 alter_program_change(program_change_event, patch->patch_primary_key());
1497         }
1498 }
1499
1500 void
1501 MidiRegionView::maybe_remove_deleted_note_from_selection (CanvasNoteEvent* cne)
1502 {
1503         if (_selection.empty()) {
1504                 return;
1505         }
1506  
1507         if (_selection.erase (cne) > 0) {
1508                 cerr << "Erased a CNE from selection\n";
1509         }
1510 }
1511
1512 void
1513 MidiRegionView::delete_selection()
1514 {
1515         if (_selection.empty()) {
1516                 return;
1517         }
1518
1519         start_delta_command (_("delete selection"));
1520
1521         for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
1522                 if ((*i)->selected()) {
1523                         _delta_command->remove((*i)->note());
1524                 }
1525         }
1526
1527         _selection.clear();
1528
1529         apply_delta ();
1530 }
1531
1532 void
1533 MidiRegionView::clear_selection_except(ArdourCanvas::CanvasNoteEvent* ev)
1534 {
1535         for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
1536                 if ((*i)->selected() && (*i) != ev) {
1537                         (*i)->selected(false);
1538                         (*i)->hide_velocity();
1539                 }
1540         }
1541
1542         _selection.clear();
1543 }
1544
1545 void
1546 MidiRegionView::unique_select(ArdourCanvas::CanvasNoteEvent* ev)
1547 {
1548         for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
1549                 if ((*i) != ev) {
1550
1551                         Selection::iterator tmp = i;
1552                         ++tmp;
1553
1554                         (*i)->selected (false);
1555                         _selection.erase (i);
1556
1557                         i = tmp;
1558
1559                 } else {
1560                         ++i;
1561                 }
1562         }
1563
1564         /* don't bother with removing this regionview from the editor selection,
1565            since we're about to add another note, and thus put/keep this
1566            regionview in the editor selection.
1567         */
1568
1569         if (!ev->selected()) {
1570                 add_to_selection (ev);
1571         }
1572 }
1573
1574 void
1575 MidiRegionView::select_matching_notes (uint8_t notenum, uint16_t channel_mask, bool add, bool extend)
1576 {
1577         uint8_t low_note = 127;
1578         uint8_t high_note = 0;
1579         MidiModel::Notes& notes (_model->notes());
1580         _optimization_iterator = _events.begin();
1581
1582         if (extend && _selection.empty()) {
1583                 extend = false;
1584         }
1585
1586         if (extend) {
1587
1588                 /* scan existing selection to get note range */
1589
1590                 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
1591                         if ((*i)->note()->note() < low_note) {
1592                                 low_note = (*i)->note()->note();
1593                         }
1594                         if ((*i)->note()->note() > high_note) {
1595                                 high_note = (*i)->note()->note();
1596                         }
1597                 }
1598
1599                 low_note = min (low_note, notenum);
1600                 high_note = max (high_note, notenum);
1601         }
1602
1603         no_sound_notes = true;
1604
1605         for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
1606
1607                 boost::shared_ptr<NoteType> note (*n);
1608                 CanvasNoteEvent* cne;
1609                 bool select = false;
1610
1611                 if (((0x0001 << note->channel()) & channel_mask) != 0) {
1612                         if (extend) {
1613                                 if ((note->note() >= low_note && note->note() <= high_note)) {
1614                                         select = true;
1615                                 }
1616                         } else if (note->note() == notenum) {
1617                                 select = true;
1618                         }
1619                 }
1620
1621                 if (select) {
1622                         if ((cne = find_canvas_note (note)) != 0) {
1623                                 // extend is false because we've taken care of it, 
1624                                 // since it extends by time range, not pitch.
1625                                 note_selected (cne, add, false);
1626                         }
1627                 }
1628                 
1629                 add = true; // we need to add all remaining matching notes, even if the passed in value was false (for "set")
1630
1631         }
1632
1633         no_sound_notes = false;
1634 }
1635
1636 void
1637 MidiRegionView::toggle_matching_notes (uint8_t notenum, uint16_t channel_mask)
1638 {
1639         MidiModel::Notes& notes (_model->notes());
1640         _optimization_iterator = _events.begin();
1641
1642         for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
1643
1644                 boost::shared_ptr<NoteType> note (*n);
1645                 CanvasNoteEvent* cne;
1646
1647                 if (note->note() == notenum && (((0x0001 << note->channel()) & channel_mask) != 0)) {
1648                         if ((cne = find_canvas_note (note)) != 0) {
1649                                 if (cne->selected()) {
1650                                         note_deselected (cne);
1651                                 } else {
1652                                         note_selected (cne, true, false);
1653                                 }
1654                         }
1655                 }
1656         }
1657 }
1658
1659 void
1660 MidiRegionView::note_selected(ArdourCanvas::CanvasNoteEvent* ev, bool add, bool extend)
1661 {
1662         if (!add) {
1663                 clear_selection_except(ev);
1664         }
1665
1666         if (!extend) {
1667
1668                 if (!ev->selected()) {
1669                         add_to_selection (ev);
1670                 }
1671
1672         } else {
1673                 /* find end of latest note selected, select all between that and the start of "ev" */
1674
1675                 Evoral::MusicalTime earliest = DBL_MAX;
1676                 Evoral::MusicalTime latest = 0;
1677
1678                 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
1679                         if ((*i)->note()->end_time() > latest) {
1680                                 latest = (*i)->note()->end_time();
1681                         }
1682                         if ((*i)->note()->time() < earliest) {
1683                                 earliest = (*i)->note()->time();
1684                         }
1685                 }
1686
1687                 if (ev->note()->end_time() > latest) {
1688                         latest = ev->note()->end_time();
1689                 }
1690
1691                 if (ev->note()->time() < earliest) {
1692                         earliest = ev->note()->time();
1693                 }
1694
1695                 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
1696
1697                         /* find notes entirely within OR spanning the earliest..latest range */
1698
1699                         if (((*i)->note()->time() >= earliest && (*i)->note()->end_time() <= latest) ||
1700                             ((*i)->note()->time() <= earliest && (*i)->note()->end_time() >= latest)) {
1701                                 add_to_selection (*i);
1702                         }
1703
1704 #if 0
1705                         /* if events were guaranteed to be time sorted, we could do this.
1706                            but as of sept 10th 2009, they no longer are.
1707                         */
1708
1709                         if ((*i)->note()->time() > latest) {
1710                                 break;
1711                         }
1712 #endif
1713                 }
1714         }
1715 }
1716
1717 void
1718 MidiRegionView::note_deselected(ArdourCanvas::CanvasNoteEvent* ev)
1719 {
1720         remove_from_selection (ev);
1721 }
1722
1723 void
1724 MidiRegionView::update_drag_selection(double x1, double x2, double y1, double y2)
1725 {
1726         if (x1 > x2) {
1727                 swap (x1, x2);
1728         }
1729
1730         if (y1 > y2) {
1731                 swap (y1, y2);
1732         }
1733
1734         // TODO: Make this faster by storing the last updated selection rect, and only
1735         // adjusting things that are in the area that appears/disappeared.
1736         // We probably need a tree to be able to find events in O(log(n)) time.
1737
1738         for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
1739
1740                 /* check if any corner of the note is inside the rect
1741
1742                    Notes:
1743                      1) this is computing "touched by", not "contained by" the rect.
1744                      2) this does not require that events be sorted in time.
1745                  */
1746
1747                 const double ix1 = (*i)->x1();
1748                 const double ix2 = (*i)->x2();
1749                 const double iy1 = (*i)->y1();
1750                 const double iy2 = (*i)->y2();
1751
1752                 if ((ix1 >= x1 && ix1 <= x2 && iy1 >= y1 && iy1 <= y2) ||
1753                     (ix1 >= x1 && ix1 <= x2 && iy2 >= y1 && iy2 <= y2) ||
1754                     (ix2 >= x1 && ix2 <= x2 && iy1 >= y1 && iy1 <= y2) ||
1755                     (ix2 >= x1 && ix2 <= x2 && iy2 >= y1 && iy2 <= y2)) {
1756
1757                         // Inside rectangle
1758                         if (!(*i)->selected()) {
1759                                 add_to_selection (*i);
1760                         }
1761                 } else if ((*i)->selected()) {
1762                         // Not inside rectangle
1763                         remove_from_selection (*i);
1764                 }
1765         }
1766 }
1767
1768 void
1769 MidiRegionView::remove_from_selection (CanvasNoteEvent* ev)
1770 {
1771         Selection::iterator i = _selection.find (ev);
1772
1773         if (i != _selection.end()) {
1774                 _selection.erase (i);
1775         }
1776
1777         ev->selected (false);
1778         ev->hide_velocity ();
1779
1780         if (_selection.empty()) {
1781                 PublicEditor& editor (trackview.editor());
1782                 editor.get_selection().remove (this);
1783         }
1784 }
1785
1786 void
1787 MidiRegionView::add_to_selection (CanvasNoteEvent* ev)
1788 {
1789         bool add_mrv_selection = false;
1790
1791         if (_selection.empty()) {
1792                 add_mrv_selection = true;
1793         }
1794
1795         if (_selection.insert (ev).second) {
1796                 ev->selected (true);
1797                 play_midi_note ((ev)->note());
1798         }
1799
1800         if (add_mrv_selection) {
1801                 PublicEditor& editor (trackview.editor());
1802                 editor.get_selection().add (this);
1803         }
1804 }
1805
1806 void
1807 MidiRegionView::move_selection(double dx, double dy)
1808 {
1809         for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
1810                 (*i)->move_event(dx, dy);
1811         }
1812 }
1813
1814 void
1815 MidiRegionView::note_dropped(CanvasNoteEvent *, double dt, int8_t dnote)
1816 {
1817         assert (!_selection.empty());
1818
1819         uint8_t lowest_note_in_selection  = 127;
1820         uint8_t highest_note_in_selection = 0;
1821         uint8_t highest_note_difference = 0;
1822
1823         // find highest and lowest notes first
1824
1825         for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
1826                 uint8_t pitch = (*i)->note()->note();
1827                 lowest_note_in_selection  = std::min(lowest_note_in_selection,  pitch);
1828                 highest_note_in_selection = std::max(highest_note_in_selection, pitch);
1829         }
1830
1831         /*
1832         cerr << "dnote: " << (int) dnote << endl;
1833         cerr << "lowest note (streamview): " << int(midi_stream_view()->lowest_note())
1834              << " highest note (streamview): " << int(midi_stream_view()->highest_note()) << endl;
1835         cerr << "lowest note (selection): " << int(lowest_note_in_selection) << " highest note(selection): "
1836              << int(highest_note_in_selection) << endl;
1837         cerr << "selection size: " << _selection.size() << endl;
1838         cerr << "Highest note in selection: " << (int) highest_note_in_selection << endl;
1839         */
1840
1841         // Make sure the note pitch does not exceed the MIDI standard range
1842         if (highest_note_in_selection + dnote > 127) {
1843                 highest_note_difference = highest_note_in_selection - 127;
1844         }
1845
1846         start_diff_command(_("move notes"));
1847
1848         for (Selection::iterator i = _selection.begin(); i != _selection.end() ; ++i) {
1849
1850                 nframes64_t start_frames = beats_to_frames((*i)->note()->time());
1851
1852                 if (dt >= 0) {
1853                         start_frames += snap_frame_to_frame(trackview.editor().pixel_to_frame(dt));
1854                 } else {
1855                         start_frames -= snap_frame_to_frame(trackview.editor().pixel_to_frame(-dt));
1856                 }
1857
1858                 Evoral::MusicalTime new_time = frames_to_beats(start_frames);
1859
1860                 if (new_time < 0) {
1861                         continue;
1862                 }
1863
1864                 diff_add_change (*i, MidiModel::DiffCommand::StartTime, new_time);
1865
1866                 uint8_t original_pitch = (*i)->note()->note();
1867                 uint8_t new_pitch      = original_pitch + dnote - highest_note_difference;
1868
1869                 // keep notes in standard midi range
1870                 clamp_to_0_127(new_pitch);
1871
1872                 // keep original pitch if note is dragged outside valid midi range
1873                 if ((original_pitch != 0 && new_pitch == 0)
1874                                 || (original_pitch != 127 && new_pitch == 127)) {
1875                         new_pitch = original_pitch;
1876                 }
1877
1878                 lowest_note_in_selection  = std::min(lowest_note_in_selection,  new_pitch);
1879                 highest_note_in_selection = std::max(highest_note_in_selection, new_pitch);
1880
1881                 diff_add_change (*i, MidiModel::DiffCommand::NoteNumber, new_pitch);
1882         }
1883
1884         apply_diff();
1885
1886         // care about notes being moved beyond the upper/lower bounds on the canvas
1887         if (lowest_note_in_selection  < midi_stream_view()->lowest_note() ||
1888             highest_note_in_selection > midi_stream_view()->highest_note()) {
1889                 midi_stream_view()->set_note_range(MidiStreamView::ContentsRange);
1890         }
1891 }
1892
1893 nframes64_t
1894 MidiRegionView::snap_pixel_to_frame(double x)
1895 {
1896         PublicEditor& editor = trackview.editor();
1897         // x is region relative, convert it to global absolute frames
1898         nframes64_t frame = editor.pixel_to_frame(x) + _region->position();
1899         editor.snap_to(frame);
1900         return frame - _region->position(); // convert back to region relative
1901 }
1902
1903 nframes64_t
1904 MidiRegionView::snap_frame_to_frame(nframes64_t x)
1905 {
1906         PublicEditor& editor = trackview.editor();
1907         // x is region relative, convert it to global absolute frames
1908         nframes64_t frame = x + _region->position();
1909         editor.snap_to(frame);
1910         return frame - _region->position(); // convert back to region relative
1911 }
1912
1913 double
1914 MidiRegionView::snap_to_pixel(double x)
1915 {
1916         return (double) trackview.editor().frame_to_pixel(snap_pixel_to_frame(x));
1917 }
1918
1919 double
1920 MidiRegionView::get_position_pixels()
1921 {
1922         nframes64_t region_frame = get_position();
1923         return trackview.editor().frame_to_pixel(region_frame);
1924 }
1925
1926 double
1927 MidiRegionView::get_end_position_pixels()
1928 {
1929         nframes64_t frame = get_position() + get_duration ();
1930         return trackview.editor().frame_to_pixel(frame);
1931 }
1932
1933 nframes64_t
1934 MidiRegionView::beats_to_frames(double beats) const
1935 {
1936         return _time_converter.to(beats);
1937 }
1938
1939 double
1940 MidiRegionView::frames_to_beats(nframes64_t frames) const
1941 {
1942         return _time_converter.from(frames);
1943 }
1944
1945 void
1946 MidiRegionView::begin_resizing (bool /*at_front*/)
1947 {
1948         _resize_data.clear();
1949
1950         for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
1951                 CanvasNote *note = dynamic_cast<CanvasNote *> (*i);
1952
1953                 // only insert CanvasNotes into the map
1954                 if (note) {
1955                         NoteResizeData *resize_data = new NoteResizeData();
1956                         resize_data->canvas_note = note;
1957
1958                         // create a new SimpleRect from the note which will be the resize preview
1959                         SimpleRect *resize_rect = new SimpleRect(
1960                                         *group, note->x1(), note->y1(), note->x2(), note->y2());
1961
1962                         // calculate the colors: get the color settings
1963                         uint32_t fill_color = UINT_RGBA_CHANGE_A(
1964                                         ARDOUR_UI::config()->canvasvar_MidiNoteSelected.get(),
1965                                         128);
1966
1967                         // make the resize preview notes more transparent and bright
1968                         fill_color = UINT_INTERPOLATE(fill_color, 0xFFFFFF40, 0.5);
1969
1970                         // calculate color based on note velocity
1971                         resize_rect->property_fill_color_rgba() = UINT_INTERPOLATE(
1972                                         CanvasNoteEvent::meter_style_fill_color(note->note()->velocity()),
1973                                         fill_color,
1974                                         0.85);
1975
1976                         resize_rect->property_outline_color_rgba() = CanvasNoteEvent::calculate_outline(
1977                                         ARDOUR_UI::config()->canvasvar_MidiNoteSelected.get());
1978
1979                         resize_data->resize_rect = resize_rect;
1980                         _resize_data.push_back(resize_data);
1981                 }
1982         }
1983 }
1984
1985 void
1986 MidiRegionView::update_resizing (bool at_front, double delta_x, bool relative)
1987 {
1988         for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
1989                 SimpleRect* resize_rect = (*i)->resize_rect;
1990                 CanvasNote* canvas_note = (*i)->canvas_note;
1991                 double current_x;
1992
1993                 if (at_front) {
1994                         if (relative) {
1995                                 current_x = canvas_note->x1() + delta_x;
1996                         } else {
1997                                 // x is in track relative, transform it to region relative
1998                                 current_x = delta_x - get_position_pixels();
1999                         }
2000                 } else {
2001                         if (relative) {
2002                                 current_x = canvas_note->x2() + delta_x;
2003                         } else {
2004                                 // x is in track relative, transform it to region relative
2005                                 current_x = delta_x - get_end_position_pixels ();
2006                         }
2007                 }
2008
2009                 if (at_front) {
2010                         resize_rect->property_x1() = snap_to_pixel(current_x);
2011                         resize_rect->property_x2() = canvas_note->x2();
2012                 } else {
2013                         resize_rect->property_x2() = snap_to_pixel(current_x);
2014                         resize_rect->property_x1() = canvas_note->x1();
2015                 }
2016         }
2017 }
2018
2019 void
2020 MidiRegionView::commit_resizing (bool at_front, double delta_x, bool relative)
2021 {
2022         start_diff_command(_("resize notes"));
2023
2024         for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
2025                 CanvasNote*  canvas_note = (*i)->canvas_note;
2026                 SimpleRect*  resize_rect = (*i)->resize_rect;
2027                 const double region_start = get_position_pixels();
2028                 double current_x;
2029
2030                 if (at_front) {
2031                         if (relative) {
2032                                 current_x = canvas_note->x1() + delta_x;
2033                         } else {
2034                                 // x is in track relative, transform it to region relative
2035                                 current_x = region_start + delta_x;
2036                         }
2037                 } else {
2038                         if (relative) {
2039                                 current_x = canvas_note->x2() + delta_x;
2040                         } else {
2041                                 // x is in track relative, transform it to region relative
2042                                 current_x = region_start + delta_x;
2043                         }
2044                 }
2045
2046                 current_x = snap_pixel_to_frame (current_x);
2047                 current_x = frames_to_beats (current_x);
2048
2049                 if (at_front && current_x < canvas_note->note()->end_time()) {
2050                         diff_add_change (canvas_note, MidiModel::DiffCommand::StartTime, current_x);
2051
2052                         double len = canvas_note->note()->time() - current_x;
2053                         len += canvas_note->note()->length();
2054
2055                         if (len > 0) {
2056                                 /* XXX convert to beats */
2057                                 diff_add_change (canvas_note, MidiModel::DiffCommand::Length, len);
2058                         }
2059                 }
2060
2061                 if (!at_front) {
2062                         double len = current_x - canvas_note->note()->time();
2063
2064                         if (len > 0) {
2065                                 /* XXX convert to beats */
2066                                 diff_add_change (canvas_note, MidiModel::DiffCommand::Length, len);
2067                         }
2068                 }
2069
2070                 delete resize_rect;
2071                 delete (*i);
2072         }
2073
2074         _resize_data.clear();
2075         apply_diff();
2076 }
2077
2078 void
2079 MidiRegionView::change_note_velocity(CanvasNoteEvent* event, int8_t velocity, bool relative)
2080 {
2081         uint8_t new_velocity;
2082
2083         if (relative) {
2084                 new_velocity = event->note()->velocity() + velocity;
2085                 clamp_to_0_127(new_velocity);
2086         } else {
2087                 new_velocity = velocity;
2088         }
2089
2090         diff_add_change (event, MidiModel::DiffCommand::Velocity, new_velocity);
2091 }
2092
2093 void
2094 MidiRegionView::change_note_note (CanvasNoteEvent* event, int8_t note, bool relative)
2095 {
2096         uint8_t new_note;
2097
2098         if (relative) {
2099                 new_note = event->note()->note() + note;
2100         } else {
2101                 new_note = note;
2102         }
2103
2104         clamp_to_0_127 (new_note);
2105         diff_add_change (event, MidiModel::DiffCommand::NoteNumber, new_note);
2106 }
2107
2108 void
2109 MidiRegionView::trim_note (CanvasNoteEvent* event, Evoral::MusicalTime front_delta, Evoral::MusicalTime end_delta)
2110 {
2111         bool change_start = false;
2112         bool change_length = false;
2113         Evoral::MusicalTime new_start;
2114         Evoral::MusicalTime new_length;
2115
2116         /* NOTE: the semantics of the two delta arguments are slightly subtle:
2117
2118            front_delta: if positive - move the start of the note later in time (shortening it)
2119                         if negative - move the start of the note earlier in time (lengthening it)
2120
2121            end_delta:   if positive - move the end of the note later in time (lengthening it)
2122                         if negative - move the end of the note earlier in time (shortening it)
2123          */
2124
2125         if (front_delta) {
2126                 if (front_delta < 0) {
2127
2128                         if (event->note()->time() < -front_delta) {
2129                                 new_start = 0;
2130                         } else {
2131                                 new_start = event->note()->time() + front_delta; // moves earlier
2132                         }
2133
2134                         /* start moved toward zero, so move the end point out to where it used to be.
2135                            Note that front_delta is negative, so this increases the length.
2136                         */
2137
2138                         new_length = event->note()->length() - front_delta;
2139                         change_start = true;
2140                         change_length = true;
2141
2142                 } else {
2143
2144                         Evoral::MusicalTime new_pos = event->note()->time() + front_delta;
2145
2146                         if (new_pos < event->note()->end_time()) {
2147                                 new_start = event->note()->time() + front_delta;
2148                                 /* start moved toward the end, so move the end point back to where it used to be */
2149                                 new_length = event->note()->length() - front_delta;
2150                                 change_start = true;
2151                                 change_length = true;
2152                         }
2153                 }
2154
2155         }
2156
2157         if (end_delta) {
2158                 bool can_change = true;
2159                 if (end_delta < 0) {
2160                         if (event->note()->length() < -end_delta) {
2161                                 can_change = false;
2162                         }
2163                 }
2164
2165                 if (can_change) {
2166                         new_length = event->note()->length() + end_delta;
2167                         change_length = true;
2168                 }
2169         }
2170
2171         if (change_start) {
2172                 diff_add_change (event, MidiModel::DiffCommand::StartTime, new_start);
2173         }
2174
2175         if (change_length) {
2176                 diff_add_change (event, MidiModel::DiffCommand::Length, new_length);
2177         }
2178 }
2179
2180 void
2181 MidiRegionView::change_note_time (CanvasNoteEvent* event, Evoral::MusicalTime delta, bool relative)
2182 {
2183         Evoral::MusicalTime new_time;
2184
2185         if (relative) {
2186                 if (delta < 0.0) {
2187                         if (event->note()->time() < -delta) {
2188                                 new_time = 0;
2189                         } else {
2190                                 new_time = event->note()->time() + delta;
2191                         }
2192                 } else {
2193                         new_time = event->note()->time() + delta;
2194                 }
2195         } else {
2196                 new_time = delta;
2197         }
2198
2199         diff_add_change (event, MidiModel::DiffCommand::StartTime, new_time);
2200 }
2201
2202 void
2203 MidiRegionView::change_velocities (bool up, bool fine, bool allow_smush)
2204 {
2205         int8_t delta;
2206
2207         if (_selection.empty()) {
2208                 return;
2209         }
2210
2211         if (fine) {
2212                 delta = 1;
2213         } else {
2214                 delta = 10;
2215         }
2216
2217         if (!up) {
2218                 delta = -delta;
2219         }
2220
2221         if (!allow_smush) {
2222                 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2223                         if ((*i)->note()->velocity() + delta == 0 || (*i)->note()->velocity() + delta == 127) {
2224                                 return;
2225                         }
2226                 }
2227         }
2228
2229         start_diff_command(_("change velocities"));
2230
2231         for (Selection::iterator i = _selection.begin(); i != _selection.end();) {
2232                 Selection::iterator next = i;
2233                 ++next;
2234                 change_note_velocity (*i, delta, true);
2235                 i = next;
2236         }
2237
2238         apply_diff();
2239 }
2240
2241
2242 void
2243 MidiRegionView::transpose (bool up, bool fine, bool allow_smush)
2244 {
2245         if (_selection.empty()) {
2246                 return;
2247         }
2248
2249         int8_t delta;
2250
2251         if (fine) {
2252                 delta = 1;
2253         } else {
2254                 delta = 12;
2255         }
2256
2257         if (!up) {
2258                 delta = -delta;
2259         }
2260
2261         if (!allow_smush) {
2262                 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2263                         if (!up) {
2264                                 if ((int8_t) (*i)->note()->note() + delta <= 0) {
2265                                         return;
2266                                 }
2267                         } else {
2268                                 if ((int8_t) (*i)->note()->note() + delta > 127) {
2269                                         return;
2270                                 }
2271                         }
2272                 }
2273         }
2274
2275         start_diff_command (_("transpose"));
2276
2277         for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
2278                 Selection::iterator next = i;
2279                 ++next;
2280                 change_note_note (*i, delta, true);
2281                 i = next;
2282         }
2283
2284         apply_diff ();
2285 }
2286
2287 void
2288 MidiRegionView::change_note_lengths (bool fine, bool shorter, bool start, bool end)
2289 {
2290         Evoral::MusicalTime delta;
2291
2292         if (fine) {
2293                 delta = 1.0/128.0;
2294         } else {
2295                 /* grab the current grid distance */
2296                 bool success;
2297                 delta = trackview.editor().get_grid_type_as_beats (success, _region->position());
2298                 if (!success) {
2299                         /* XXX cannot get grid type as beats ... should always be possible ... FIX ME */
2300                         cerr << "Grid type not available as beats - TO BE FIXED\n";
2301                         return;
2302                 }
2303         }
2304
2305         if (shorter) {
2306                 delta = -delta;
2307         }
2308
2309         start_diff_command (_("change note lengths"));
2310
2311         for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
2312                 Selection::iterator next = i;
2313                 ++next;
2314
2315                 /* note the negation of the delta for start */
2316
2317                 trim_note (*i, (start ? -delta : 0), (end ? delta : 0));
2318                 i = next;
2319         }
2320
2321         apply_diff ();
2322
2323 }
2324
2325 void
2326 MidiRegionView::nudge_notes (bool forward)
2327 {
2328         if (_selection.empty()) {
2329                 return;
2330         }
2331
2332         /* pick a note as the point along the timeline to get the nudge distance.
2333            its not necessarily the earliest note, so we may want to pull the notes out
2334            into a vector and sort before using the first one.
2335         */
2336
2337         nframes64_t ref_point = _region->position() + beats_to_frames ((*(_selection.begin()))->note()->time());
2338         nframes64_t unused;
2339         nframes64_t distance;
2340
2341         if (trackview.editor().snap_mode() == Editing::SnapOff) {
2342                 
2343                 /* grid is off - use nudge distance */
2344
2345                 distance = trackview.editor().get_nudge_distance (ref_point, unused);
2346
2347         } else {
2348
2349                 /* use grid */
2350
2351                 nframes64_t next_pos = ref_point;
2352
2353                 if (forward) {
2354                         /* XXX need check on max_frames, but that needs max_frames64 or something */
2355                         next_pos += 1;
2356                 } else {
2357                         if (next_pos == 0) {
2358                                 return;
2359                         }
2360                         next_pos -= 1;
2361                 }
2362
2363                 cerr << "ref point was " << ref_point << " next was " << next_pos;
2364                 trackview.editor().snap_to (next_pos, (forward ? 1 : -1), false);
2365                 distance = ref_point - next_pos;
2366                 cerr << " final is " << next_pos << " distance = " << distance << endl;
2367         }
2368
2369         if (distance == 0) {
2370                 return;
2371         }
2372
2373         Evoral::MusicalTime delta = frames_to_beats (fabs (distance));
2374
2375         if (!forward) {
2376                 delta = -delta;
2377         }
2378
2379         start_diff_command (_("nudge"));
2380
2381         for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
2382                 Selection::iterator next = i;
2383                 ++next;
2384                 change_note_time (*i, delta, true);
2385                 i = next;
2386         }
2387
2388         apply_diff ();
2389 }
2390
2391 void
2392 MidiRegionView::change_channel(uint8_t channel)
2393 {
2394         start_diff_command(_("change channel"));
2395         for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2396                 diff_add_change (*i, MidiModel::DiffCommand::Channel, channel);
2397         }
2398         apply_diff();
2399 }
2400
2401
2402 void
2403 MidiRegionView::note_entered(ArdourCanvas::CanvasNoteEvent* ev)
2404 {
2405         if (_mouse_state == SelectTouchDragging) {
2406                 note_selected(ev, true);
2407         }
2408
2409         char buf[12];
2410         snprintf (buf, sizeof (buf), "%s (%d)", Evoral::midi_note_name (ev->note()->note()).c_str(), (int) ev->note()->note());
2411         PublicEditor& editor (trackview.editor());
2412         editor.show_verbose_canvas_cursor_with (buf);
2413 }
2414
2415 void
2416 MidiRegionView::note_left (ArdourCanvas::CanvasNoteEvent*)
2417 {
2418         PublicEditor& editor (trackview.editor());
2419         editor.hide_verbose_canvas_cursor ();
2420 }
2421
2422
2423 void
2424 MidiRegionView::switch_source(boost::shared_ptr<Source> src)
2425 {
2426         boost::shared_ptr<MidiSource> msrc = boost::dynamic_pointer_cast<MidiSource>(src);
2427         if (msrc)
2428                 display_model(msrc->model());
2429 }
2430
2431 void
2432 MidiRegionView::set_frame_color()
2433 {
2434         if (frame) {
2435                 if (_selected && should_show_selection) {
2436                         frame->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_SelectedFrameBase.get();
2437                 } else {
2438                         frame->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_MidiFrameBase.get();
2439                 }
2440         }
2441 }
2442
2443 void
2444 MidiRegionView::midi_channel_mode_changed(ChannelMode mode, uint16_t mask)
2445 {
2446         switch (mode) {
2447         case AllChannels:
2448         case FilterChannels:
2449                 _force_channel = -1;
2450                 break;
2451         case ForceChannel:
2452                 _force_channel = mask;
2453                 mask = 0xFFFF; // Show all notes as active (below)
2454         };
2455
2456         // Update notes for selection
2457         for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2458                 (*i)->on_channel_selection_change(mask);
2459         }
2460
2461         _last_channel_selection = mask;
2462 }
2463
2464 void
2465 MidiRegionView::midi_patch_settings_changed(std::string model, std::string custom_device_mode)
2466 {
2467         _model_name         = model;
2468         _custom_device_mode = custom_device_mode;
2469         redisplay_model();
2470 }
2471
2472 void
2473 MidiRegionView::cut_copy_clear (Editing::CutCopyOp op)
2474 {
2475         if (_selection.empty()) {
2476                 return;
2477         }
2478
2479         PublicEditor& editor (trackview.editor());
2480
2481         switch (op) {
2482         case Cut:
2483         case Copy:
2484                 editor.get_cut_buffer().add (selection_as_cut_buffer());
2485                 break;
2486         default:
2487                 break;
2488         }
2489
2490         if (op != Copy) {
2491
2492                 start_delta_command();
2493                 
2494                 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2495                         switch (op) {
2496                         case Copy:
2497                                 break;
2498                         case Cut:
2499                         case Clear:
2500                                 delta_remove_note (*i);
2501                                 break;
2502                         }
2503                 }
2504                 
2505                 apply_delta();
2506         }
2507 }
2508
2509 MidiCutBuffer*
2510 MidiRegionView::selection_as_cut_buffer () const
2511 {
2512         Notes notes;
2513
2514         for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2515                 NoteType* n = (*i)->note().get();
2516                 notes.insert (boost::shared_ptr<NoteType> (new NoteType (*n)));
2517         }
2518
2519         MidiCutBuffer* cb = new MidiCutBuffer (trackview.session());
2520         cb->set (notes);
2521
2522         return cb;
2523 }
2524
2525 void
2526 MidiRegionView::paste (nframes64_t pos, float times, const MidiCutBuffer& mcb)
2527 {
2528         if (mcb.empty()) {
2529                 return;
2530         }
2531
2532         start_delta_command (_("paste"));
2533
2534         Evoral::MusicalTime beat_delta;
2535         Evoral::MusicalTime paste_pos_beats;
2536         Evoral::MusicalTime duration;
2537         Evoral::MusicalTime end_point;
2538
2539         duration = (*mcb.notes().rbegin())->end_time() - (*mcb.notes().begin())->time();
2540         paste_pos_beats = frames_to_beats (pos - _region->position());
2541         beat_delta = (*mcb.notes().begin())->time() - paste_pos_beats;
2542         paste_pos_beats = 0;
2543
2544         _selection.clear ();
2545
2546         for (int n = 0; n < (int) times; ++n) {
2547
2548                 cerr << "Pasting " << mcb.notes().size() << " for the " << n+1 << "th time\n";
2549                 
2550                 for (Notes::const_iterator i = mcb.notes().begin(); i != mcb.notes().end(); ++i) {
2551
2552                         boost::shared_ptr<NoteType> copied_note (new NoteType (*((*i).get())));
2553                         copied_note->set_time (paste_pos_beats + copied_note->time() - beat_delta);
2554
2555                         /* make all newly added notes selected */
2556
2557                         delta_add_note (copied_note, true);
2558                         end_point = copied_note->end_time();
2559                 }
2560
2561                 paste_pos_beats += duration;
2562         }
2563
2564         /* if we pasted past the current end of the region, extend the region */
2565
2566         nframes64_t end_frame = _region->position() + beats_to_frames (end_point);
2567         nframes64_t region_end = _region->position() + _region->length() - 1;
2568
2569         if (end_frame > region_end) {
2570
2571                 cerr << "region end is now " << end_frame << " to extend from " << region_end << endl;
2572
2573                 trackview.session()->begin_reversible_command (_("paste"));
2574
2575                 _region->clear_history ();
2576                 _region->set_length (end_frame, this);
2577                 trackview.session()->add_command (new StatefulDiffCommand (_region));
2578         }
2579
2580         cerr << "region end finally at " << _region->position() + _region->length() - 1;
2581         apply_delta ();
2582 }
2583
2584 struct EventNoteTimeEarlyFirstComparator {
2585     bool operator() (CanvasNoteEvent* a, CanvasNoteEvent* b) {
2586             return a->note()->time() < b->note()->time();
2587     }
2588 };
2589
2590 void
2591 MidiRegionView::time_sort_events ()
2592 {
2593         if (!_sort_needed) {
2594                 return;
2595         }
2596
2597         EventNoteTimeEarlyFirstComparator cmp;
2598         _events.sort (cmp);
2599
2600         _sort_needed = false;
2601 }
2602
2603 void
2604 MidiRegionView::goto_next_note ()
2605 {
2606         // nframes64_t pos = -1;
2607         bool use_next = false;
2608
2609         if (_events.back()->selected()) {
2610                 return;
2611         }
2612
2613         time_sort_events ();
2614
2615         for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2616                 if ((*i)->selected()) {
2617                         use_next = true;
2618                         continue;
2619                 } else if (use_next) {
2620                         unique_select (*i);
2621                         // pos = _region->position() + beats_to_frames ((*i)->note()->time());
2622                         return;
2623                 }
2624         }
2625
2626         /* use the first one */
2627
2628         unique_select (_events.front());
2629
2630 }
2631
2632 void
2633 MidiRegionView::goto_previous_note ()
2634 {
2635         // nframes64_t pos = -1;
2636         bool use_next = false;
2637
2638         if (_events.front()->selected()) {
2639                 return;
2640         }
2641
2642         time_sort_events ();
2643
2644         for (Events::reverse_iterator i = _events.rbegin(); i != _events.rend(); ++i) {
2645                 if ((*i)->selected()) {
2646                         use_next = true;
2647                         continue;
2648                 } else if (use_next) {
2649                         unique_select (*i);
2650                         // pos = _region->position() + beats_to_frames ((*i)->note()->time());
2651                         return;
2652                 }
2653         }
2654
2655         /* use the last one */
2656
2657         unique_select (*(_events.rbegin()));
2658 }
2659
2660 void
2661 MidiRegionView::selection_as_notelist (Notes& selected, bool allow_all_if_none_selected)
2662 {
2663         bool had_selected = false;
2664
2665         time_sort_events ();
2666
2667         for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2668                 if ((*i)->selected()) {
2669                         selected.insert ((*i)->note());
2670                         had_selected = true;
2671                 }
2672         }
2673         
2674         if (allow_all_if_none_selected && !had_selected) {
2675                 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2676                         selected.insert ((*i)->note());
2677                 }
2678         }
2679 }