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