Prevent region drags with the middle button in internal edit mode (#3869).
[ardour.git] / gtk2_ardour / editor_drag.cc
index d069e2e984f107e56b18ac70cdaa59e34d433f80..00313d06720b41d00cba917179c48c96f185fa18 100644 (file)
@@ -32,6 +32,8 @@
 #include "ardour/session.h"
 #include "ardour/dB.h"
 #include "ardour/region_factory.h"
+#include "ardour/operations.h"
+
 #include "editor.h"
 #include "i18n.h"
 #include "keyboard.h"
@@ -91,6 +93,8 @@ DragManager::abort ()
 
        _drags.clear ();
 
+       _editor->set_follow_playhead (_old_follow_playhead, false);
+       
        _ending = false;
 }
 
@@ -112,6 +116,10 @@ DragManager::set (Drag* d, GdkEvent* e, Gdk::Cursor* c)
 void
 DragManager::start_grab (GdkEvent* e, Gdk::Cursor* c)
 {
+       /* Prevent follow playhead during the drag to be nice to the user */
+       _old_follow_playhead = _editor->follow_playhead ();
+       _editor->set_follow_playhead (false);
+               
        _current_pointer_frame = _editor->event_frame (e, &_current_pointer_x, &_current_pointer_y);
        
        for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
@@ -139,6 +147,8 @@ DragManager::end_grab (GdkEvent* e)
        _drags.clear ();
 
        _ending = false;
+
+       _editor->set_follow_playhead (_old_follow_playhead, false);
        
        return r;
 }
@@ -306,7 +316,7 @@ Drag::motion_handler (GdkEvent* event, bool from_autoscroll)
 
        if (!from_autoscroll && !_move_threshold_passed) {
 
-               bool const xp = (::llabs (_drags->current_pointer_frame () - _grab_frame) >= threshold.first);
+               bool const xp = (::llabs (_drags->current_pointer_frame () - _raw_grab_frame) >= threshold.first);
                bool const yp = (::fabs ((_drags->current_pointer_y () - _grab_y)) >= threshold.second);
 
                _move_threshold_passed = ((xp && x_movement_matters()) || (yp && y_movement_matters()));
@@ -339,7 +349,7 @@ Drag::abort ()
                _item->ungrab (0);
        }
 
-       aborted ();
+       aborted (_move_threshold_passed);
 
        _editor->stop_canvas_autoscroll ();
        _editor->hide_verbose_canvas_cursor ();
@@ -897,7 +907,7 @@ RegionMoveDrag::finished_no_copy (
        if (_x_constrained) {
                _editor->begin_reversible_command (_("fixed time region drag"));
        } else {
-               _editor->begin_reversible_command (_("region drag"));
+               _editor->begin_reversible_command (Operations::region_drag);
        }
 
        for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ) {
@@ -1124,7 +1134,7 @@ RegionMoveDrag::add_stateful_diff_commands_for_playlists (PlaylistSet const & pl
        for (PlaylistSet::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
                StatefulDiffCommand* c = new StatefulDiffCommand (*i);
                if (!c->empty()) {
-                       _editor->session()->add_command (new StatefulDiffCommand (*i));
+                       _editor->session()->add_command (c);
                } else {
                        delete c;
                }
@@ -1133,7 +1143,7 @@ RegionMoveDrag::add_stateful_diff_commands_for_playlists (PlaylistSet const & pl
 
 
 void
-RegionMoveDrag::aborted ()
+RegionMoveDrag::aborted (bool movement_occurred)
 {
        if (_copy) {
 
@@ -1144,12 +1154,12 @@ RegionMoveDrag::aborted ()
                _views.clear ();
 
        } else {
-               RegionMotionDrag::aborted ();
+               RegionMotionDrag::aborted (movement_occurred);
        }
 }
 
 void
-RegionMotionDrag::aborted ()
+RegionMotionDrag::aborted (bool)
 {
        for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
                RegionView* rv = i->view;
@@ -1166,7 +1176,10 @@ RegionMotionDrag::aborted ()
 
        _editor->update_canvas_now ();
 }
-                                     
+
+/** @param b true to brush, otherwise false.
+ *  @param c true to make copies of the regions being moved, otherwise false.
+ */
 RegionMoveDrag::RegionMoveDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b, bool c)
        : RegionMotionDrag (e, i, p, v, b),
          _copy (c)
@@ -1219,7 +1232,7 @@ RegionInsertDrag::finished (GdkEvent *, bool)
 
        boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
 
-       _editor->begin_reversible_command (_("insert region"));
+       _editor->begin_reversible_command (Operations::insert_region);
        playlist->clear_changes ();
        playlist->add_region (_primary->region (), _last_frame_position);
        _editor->session()->add_command (new StatefulDiffCommand (playlist));
@@ -1231,7 +1244,7 @@ RegionInsertDrag::finished (GdkEvent *, bool)
 }
 
 void
-RegionInsertDrag::aborted ()
+RegionInsertDrag::aborted (bool)
 {
        delete _primary;
        _primary = 0;
@@ -1330,7 +1343,7 @@ RegionSpliceDrag::finished (GdkEvent* event, bool movement_occurred)
 }
 
 void
-RegionSpliceDrag::aborted ()
+RegionSpliceDrag::aborted (bool)
 {
        /* XXX: TODO */
 }
@@ -1349,6 +1362,7 @@ RegionCreateDrag::motion (GdkEvent* event, bool first_move)
 {
        if (first_move) {
                add_region();
+               _view->playlist()->freeze ();
        } else {
                if (_region) {
                        framepos_t const f = adjusted_current_frame (event);
@@ -1368,6 +1382,8 @@ RegionCreateDrag::finished (GdkEvent*, bool movement_occurred)
 {
        if (!movement_occurred) {
                add_region ();
+       } else {
+               _view->playlist()->thaw ();
        }
 
        if (_region) {
@@ -1391,8 +1407,12 @@ RegionCreateDrag::add_region ()
 }
 
 void
-RegionCreateDrag::aborted ()
+RegionCreateDrag::aborted (bool)
 {
+       if (_region) {
+               _view->playlist()->thaw ();
+       }
+
        /* XXX */
 }
 
@@ -1479,7 +1499,7 @@ NoteResizeDrag::finished (GdkEvent*, bool /*movement_occurred*/)
 }
 
 void
-NoteResizeDrag::aborted ()
+NoteResizeDrag::aborted (bool)
 {
        /* XXX: TODO */
 }
@@ -1503,14 +1523,13 @@ RegionGainDrag::finished (GdkEvent *, bool)
 }
 
 void
-RegionGainDrag::aborted ()
+RegionGainDrag::aborted (bool)
 {
        /* XXX: TODO */
 }
 
 TrimDrag::TrimDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
        : RegionDrag (e, i, p, v)
-       , _have_transaction (false)
 {
        DEBUG_TRACE (DEBUG::Drags, "New TrimDrag\n");
 }
@@ -1602,13 +1621,12 @@ TrimDrag::motion (GdkEvent* event, bool first_move)
                }
 
                _editor->begin_reversible_command (trim_type);
-               _have_transaction = true;
 
                for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
                        RegionView* rv = i->view;
                        rv->fake_set_opaque (false);
                        rv->enable_display (false);
-                       rv->region()->clear_changes ();
+                       rv->region()->playlist()->clear_owned_changes ();
 
                        AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (rv);
 
@@ -1706,12 +1724,24 @@ TrimDrag::finished (GdkEvent* event, bool movement_occurred)
                        _primary->thaw_after_trim ();
                } else {
 
+                       set<boost::shared_ptr<Playlist> > diffed_playlists;
+
                        for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
-                               i->view->thaw_after_trim ();
+                                i->view->thaw_after_trim ();
                                i->view->enable_display (true);
                                i->view->fake_set_opaque (true);
-                               if (_have_transaction) {
-                                       _editor->session()->add_command (new StatefulDiffCommand (i->view->region()));
+
+                               /* Trimming one region may affect others on the playlist, so we need
+                                  to get undo Commands from the whole playlist rather than just the
+                                  region.  Use diffed_playlists to make sure we don't diff a given
+                                  playlist more than once.
+                               */
+                               boost::shared_ptr<Playlist> p = i->view->region()->playlist ();
+                               if (diffed_playlists.find (p) == diffed_playlists.end()) {
+                                       vector<Command*> cmds;
+                                       p->rdiff (cmds);
+                                       _editor->session()->add_commands (cmds);
+                                       diffed_playlists.insert (p);
                                }
                        }
                }
@@ -1720,9 +1750,7 @@ TrimDrag::finished (GdkEvent* event, bool movement_occurred)
                }
 
                _editor->motion_frozen_playlists.clear ();
-               if (_have_transaction) {
-                       _editor->commit_reversible_command();
-               }
+               _editor->commit_reversible_command();
 
        } else {
                /* no mouse movement */
@@ -1739,7 +1767,7 @@ TrimDrag::finished (GdkEvent* event, bool movement_occurred)
 }
 
 void
-TrimDrag::aborted ()
+TrimDrag::aborted (bool movement_occurred)
 {
        /* Our motion method is changing model state, so use the Undo system
           to cancel.  Perhaps not ideal, as this will leave an Undo point
@@ -1748,7 +1776,7 @@ TrimDrag::aborted ()
 
        finished (0, true);
        
-       if (_have_transaction) {
+       if (movement_occurred) {
                _editor->undo ();
        }
 
@@ -1776,6 +1804,8 @@ TrimDrag::setup_pointer_frame_offset ()
        case EndTrim:
                _pointer_frame_offset = raw_grab_frame() - i->initial_end;
                break;
+       case ContentsTrim:
+               break;
        }
 }
 
@@ -1801,7 +1831,6 @@ MeterMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
                MeterMarker* new_marker = new MeterMarker (
                        *_editor,
                        *_editor->meter_group,
-                       *_editor->cursor_group,
                        ARDOUR_UI::config()->canvasvar_MeterMarker.get(),
                        name,
                        *new MeterSection (_marker->meter())
@@ -1877,7 +1906,7 @@ MeterMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
 }
 
 void
-MeterMarkerDrag::aborted ()
+MeterMarkerDrag::aborted (bool)
 {
        _marker->set_position (_marker->meter().frame ());
 }
@@ -1905,7 +1934,6 @@ TempoMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
                TempoMarker* new_marker = new TempoMarker (
                        *_editor,
                        *_editor->tempo_group,
-                       *_editor->cursor_group,
                        ARDOUR_UI::config()->canvasvar_TempoMarker.get(),
                        name,
                        *new TempoSection (_marker->tempo())
@@ -1971,7 +1999,7 @@ TempoMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
 }
 
 void
-TempoMarkerDrag::aborted ()
+TempoMarkerDrag::aborted (bool)
 {
        _marker->set_position (_marker->tempo().frame());
 }
@@ -1983,17 +2011,33 @@ CursorDrag::CursorDrag (Editor* e, ArdourCanvas::Item* i, bool s)
        DEBUG_TRACE (DEBUG::Drags, "New CursorDrag\n");
 }
 
+/** Do all the things we do when dragging the playhead to make it look as though
+ *  we have located, without actually doing the locate (because that would cause
+ *  the diskstream buffers to be refilled, which is too slow).
+ */
+void
+CursorDrag::fake_locate (framepos_t t)
+{
+       _editor->playhead_cursor->set_position (t);
+       
+       Session* s = _editor->session ();
+       if (s->timecode_transmission_suspended ()) {
+               framepos_t const f = _editor->playhead_cursor->current_frame;
+               s->send_mmc_locate (f);
+               s->send_full_time_code (f);
+       }
+
+       _editor->show_verbose_time_cursor (t, 10);
+       _editor->UpdateAllTransportClocks (t);
+}
+
 void
 CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
 {
        Drag::start_grab (event, c);
 
-       if (!_stop) {
-               framepos_t where = _editor->event_frame (event, 0, 0);
-
-               _editor->snap_to_with_modifier (where, event);
-               _editor->playhead_cursor->set_position (where);
-       }
+       framepos_t where = _editor->event_frame (event, 0, 0);
+       _editor->snap_to_with_modifier (where, event);
 
        _editor->_dragging_playhead = true;
        
@@ -2009,16 +2053,12 @@ CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
                }
                
                s->request_suspend_timecode_transmission ();
-
-               if (s->timecode_transmission_suspended ()) {
-                       framepos_t const f = _editor->playhead_cursor->current_frame;
-                       s->send_mmc_locate (f);
-                       s->send_full_time_code (f);
+               while (!s->timecode_transmission_suspended ()) {
+                       /* twiddle our thumbs */
                }
        }
-
-       _editor->show_verbose_time_cursor (_editor->playhead_cursor->current_frame, 10);
-       _editor->UpdateAllTransportClocks (_editor->playhead_cursor->current_frame);
+       
+       fake_locate (where);
 }
 
 void
@@ -2030,22 +2070,11 @@ CursorDrag::motion (GdkEvent* event, bool)
                return;
        }
 
-       _editor->playhead_cursor->set_position (adjusted_frame);
-
-       _editor->show_verbose_time_cursor (_editor->playhead_cursor->current_frame, 10);
-
-       Session* s = _editor->session ();
-       if (s && s->timecode_transmission_suspended ()) {
-               framepos_t const f = _editor->playhead_cursor->current_frame;
-               s->send_mmc_locate (f);
-               s->send_full_time_code (f);
-       }
+       fake_locate (adjusted_frame);
        
-
 #ifdef GTKOSX
        _editor->update_canvas_now ();
 #endif
-       _editor->UpdateAllTransportClocks (_editor->playhead_cursor->current_frame);
 }
 
 void
@@ -2068,7 +2097,7 @@ CursorDrag::finished (GdkEvent* event, bool movement_occurred)
 }
 
 void
-CursorDrag::aborted ()
+CursorDrag::aborted (bool)
 {
        if (_editor->_dragging_playhead) {
                _editor->session()->request_resume_timecode_transmission ();
@@ -2183,7 +2212,7 @@ FadeInDrag::finished (GdkEvent* event, bool movement_occurred)
 }
 
 void
-FadeInDrag::aborted ()
+FadeInDrag::aborted (bool)
 {
        for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
                AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
@@ -2306,7 +2335,7 @@ FadeOutDrag::finished (GdkEvent* event, bool movement_occurred)
 }
 
 void
-FadeOutDrag::aborted ()
+FadeOutDrag::aborted (bool)
 {
        for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
                AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
@@ -2330,13 +2359,6 @@ MarkerDrag::MarkerDrag (Editor* e, ArdourCanvas::Item* i)
 
        _points.push_back (Gnome::Art::Point (0, 0));
        _points.push_back (Gnome::Art::Point (0, physical_screen_height (_editor->get_window())));
-
-       _line = new ArdourCanvas::Line (*_editor->timebar_group);
-       _line->property_width_pixels() = 1;
-       _line->property_points () = _points;
-       _line->hide ();
-
-       _line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_MarkerDragLine.get();
 }
 
 MarkerDrag::~MarkerDrag ()
@@ -2443,10 +2465,6 @@ MarkerDrag::motion (GdkEvent* event, bool)
 
        framepos_t next = newframe;
 
-       if (newframe == last_pointer_frame()) {
-               return;
-       }
-
        if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
                move_both = true;
        }
@@ -2540,7 +2558,7 @@ MarkerDrag::motion (GdkEvent* event, bool)
                                        copy_location->set_end (new_end);
                                } else  if (new_start < copy_location->end()) {
                                        copy_location->set_start (new_start);
-                               } else {
+                               } else if (newframe > 0) {
                                        _editor->snap_to (next, 1, true);
                                        copy_location->set_end (next);
                                        copy_location->set_start (newframe);
@@ -2638,12 +2656,10 @@ MarkerDrag::finished (GdkEvent* event, bool movement_occurred)
        XMLNode &after = _editor->session()->locations()->get_state();
        _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
        _editor->commit_reversible_command ();
-
-       _line->hide();
 }
 
 void
-MarkerDrag::aborted ()
+MarkerDrag::aborted (bool)
 {
        /* XXX: TODO */
 }
@@ -2651,11 +2667,7 @@ MarkerDrag::aborted ()
 void
 MarkerDrag::update_item (Location* location)
 {
-       double const x1 = _editor->frame_to_pixel (location->start());
-
-       _points.front().set_x(x1);
-       _points.back().set_x(x1);
-       _line->property_points() = _points;
+        /* noop */
 }
 
 ControlPointDrag::ControlPointDrag (Editor* e, ArdourCanvas::Item* i)
@@ -2766,7 +2778,7 @@ ControlPointDrag::finished (GdkEvent* event, bool movement_occurred)
 }
 
 void
-ControlPointDrag::aborted ()
+ControlPointDrag::aborted (bool)
 {
        _point->line().reset ();
 }
@@ -2876,7 +2888,7 @@ LineDrag::finished (GdkEvent* event, bool)
 }
 
 void
-LineDrag::aborted ()
+LineDrag::aborted (bool)
 {
        _line->reset ();
 }
@@ -2894,7 +2906,7 @@ FeatureLineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
 {
        Drag::start_grab (event);
        
-       _line = reinterpret_cast<SimpleLine*> (_item);
+       _line = reinterpret_cast<Line*> (_item);
        assert (_line);
 
        /* need to get x coordinate in terms of parent (AudioRegionView) origin. */
@@ -2907,10 +2919,10 @@ FeatureLineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
        /* store grab start in parent frame */
        _region_view_grab_x = cx;
        
-       _before = _line->property_x1();
+       _before = *(float*) _item->get_data ("position");
        
        _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
-               
+
        _max_x = _editor->frame_to_pixel(_arv->get_duration());
 }
 
@@ -2932,21 +2944,34 @@ FeatureLineDrag::motion (GdkEvent*, bool)
                cx = 0;
        }
        
-       _line->property_x1() = cx; 
-       _line->property_x2() = cx;
+       ArdourCanvas::Points points;
+       
+       double x1 = 0, x2 = 0, y1 = 0, y2 = 0;
+
+       _line->get_bounds(x1, y2, x2, y2);
+       
+       points.push_back(Gnome::Art::Point(cx, 2.0)); // first x-coord needs to be a non-normal value
+       points.push_back(Gnome::Art::Point(cx, y2 - y1));
 
-       _before = _line->property_x1();
+       _line->property_points() = points;
+       
+       float *pos = new float;
+       *pos = cx;
+       
+       _line->set_data ("position", pos);
+
+       _before = cx;
 }
 
 void
 FeatureLineDrag::finished (GdkEvent*, bool)
 {
        _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
-       _arv->update_transient(_before, _line->property_x1());
+       _arv->update_transient(_before, _before);
 }
 
 void
-FeatureLineDrag::aborted ()
+FeatureLineDrag::aborted (bool)
 {
        //_line->reset ();
 }
@@ -3033,19 +3058,16 @@ RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred)
 
 
                Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
-               bool committed;
 
                _editor->begin_reversible_command (_("rubberband selection"));
 
                if (grab_frame() < last_pointer_frame()) {
-                       committed = _editor->select_all_within (grab_frame(), last_pointer_frame() - 1, y1, y2, _editor->track_views, op, false);
+                       _editor->select_all_within (grab_frame(), last_pointer_frame() - 1, y1, y2, _editor->track_views, op, false);
                } else {
-                       committed = _editor->select_all_within (last_pointer_frame(), grab_frame() - 1, y1, y2, _editor->track_views, op, false);
+                       _editor->select_all_within (last_pointer_frame(), grab_frame() - 1, y1, y2, _editor->track_views, op, false);
                }
 
-               if (!committed) {
-                       _editor->commit_reversible_command ();
-               }
+               _editor->commit_reversible_command ();
 
        } else {
                if (!getenv("ARDOUR_SAE")) {
@@ -3060,7 +3082,7 @@ RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred)
 }
 
 void
-RubberbandSelectDrag::aborted ()
+RubberbandSelectDrag::aborted (bool)
 {
        _editor->rubberband_rect->hide ();
 }
@@ -3131,7 +3153,7 @@ TimeFXDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
 }
 
 void
-TimeFXDrag::aborted ()
+TimeFXDrag::aborted (bool)
 {
        _primary->get_time_axis_view().hide_timestretch ();
 }
@@ -3164,7 +3186,7 @@ ScrubDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
 }
 
 void
-ScrubDrag::aborted ()
+ScrubDrag::aborted (bool)
 {
        /* XXX: TODO */
 }
@@ -3432,7 +3454,7 @@ SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
 }
 
 void
-SelectionDrag::aborted ()
+SelectionDrag::aborted (bool)
 {
        /* XXX: TODO */
 }
@@ -3638,7 +3660,7 @@ RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
 }
 
 void
-RangeMarkerBarDrag::aborted ()
+RangeMarkerBarDrag::aborted (bool)
 {
        /* XXX: TODO */
 }
@@ -3719,14 +3741,18 @@ MouseZoomDrag::finished (GdkEvent* event, bool movement_occurred)
                        _editor->temporal_zoom_by_frame (last_pointer_frame(), grab_frame(), "mouse zoom");
                }
        } else {
-               _editor->temporal_zoom_to_frame (_zoom_out, grab_frame());
+               if (Keyboard::the_keyboard().key_is_down (GDK_Shift_L)) {
+                       _editor->tav_zoom_step (_zoom_out);
+               } else {
+                       _editor->temporal_zoom_to_frame (_zoom_out, grab_frame());
+               }
        }
 
        _editor->zoom_rect->hide();
 }
 
 void
-MouseZoomDrag::aborted ()
+MouseZoomDrag::aborted (bool)
 {
        _editor->zoom_rect->hide ();
 }
@@ -3868,7 +3894,7 @@ NoteDrag::finished (GdkEvent* ev, bool moved)
 }
 
 void
-NoteDrag::aborted ()
+NoteDrag::aborted (bool)
 {
        /* XXX: TODO */
 }
@@ -4071,7 +4097,7 @@ AutomationRangeDrag::finished (GdkEvent* event, bool)
 }
 
 void
-AutomationRangeDrag::aborted ()
+AutomationRangeDrag::aborted (bool)
 {
        for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
                i->line->clear_always_in_view ();
@@ -4090,17 +4116,17 @@ DraggingView::DraggingView (RegionView* v, RegionDrag* parent)
        initial_end = v->region()->position () + v->region()->length ();
 }
 
-ProgramChangeDrag::ProgramChangeDrag (Editor* e, CanvasProgramChange* i, MidiRegionView* r)
+PatchChangeDrag::PatchChangeDrag (Editor* e, CanvasPatchChange* i, MidiRegionView* r)
        : Drag (e, i)
        , _region_view (r)
-       , _program_change (i)
+       , _patch_change (i)
        , _cumulative_dx (0)
 {
-       DEBUG_TRACE (DEBUG::Drags, "New ProgramChangeDrag\n");
+       DEBUG_TRACE (DEBUG::Drags, "New PatchChangeDrag\n");
 }
 
 void
-ProgramChangeDrag::motion (GdkEvent* ev, bool)
+PatchChangeDrag::motion (GdkEvent* ev, bool)
 {
        framepos_t f = adjusted_current_frame (ev);
        boost::shared_ptr<Region> r = _region_view->region ();
@@ -4109,12 +4135,12 @@ ProgramChangeDrag::motion (GdkEvent* ev, bool)
        
        framecnt_t const dxf = f - grab_frame();
        double const dxu = _editor->frame_to_unit (dxf);
-       _program_change->move (dxu - _cumulative_dx, 0);
+       _patch_change->move (dxu - _cumulative_dx, 0);
        _cumulative_dx = dxu;
 }
 
 void
-ProgramChangeDrag::finished (GdkEvent* ev, bool movement_occurred)
+PatchChangeDrag::finished (GdkEvent* ev, bool movement_occurred)
 {
        if (!movement_occurred) {
                return;
@@ -4126,22 +4152,22 @@ ProgramChangeDrag::finished (GdkEvent* ev, bool movement_occurred)
        f = max (f, r->position ());
        f = min (f, r->last_frame ());
        
-       _region_view->move_program_change (
-               MidiRegionView::PCEvent (_program_change->event_time(), _program_change->program(), _program_change->channel()),
+       _region_view->move_patch_change (
+               *_patch_change,
                _region_view->frames_to_beats (f - r->position() - r->start())
                );
 }
 
 void
-ProgramChangeDrag::aborted ()
+PatchChangeDrag::aborted (bool)
 {
-       _program_change->move (-_cumulative_dx, 0);
+       _patch_change->move (-_cumulative_dx, 0);
 }
 
 void
-ProgramChangeDrag::setup_pointer_frame_offset ()
+PatchChangeDrag::setup_pointer_frame_offset ()
 {
        boost::shared_ptr<Region> region = _region_view->region ();
-       _pointer_frame_offset = raw_grab_frame() - _region_view->beats_to_frames (_program_change->event_time()) - region->position() + region->start();
+       _pointer_frame_offset = raw_grab_frame() - _region_view->beats_to_frames (_patch_change->patch()->time()) - region->position() + region->start();
 }