Make smf_track_get_next_event gracefully handle empty tracks.
[ardour.git] / gtk2_ardour / editor_mouse.cc
index 652ab49343522c12fd8d105f4b54ad0d34a887b2..157bda3f79a8190c74213e9fa0d25f4d62fe190c 100644 (file)
@@ -1,4 +1,3 @@
-
 /*
     Copyright (C) 2000-2001 Paul Davis 
 
@@ -49,6 +48,7 @@
 #include "keyboard.h"
 #include "editing.h"
 #include "rgb_macros.h"
+#include "control_point_dialog.h"
 
 #include <ardour/types.h>
 #include <ardour/profile.h>
@@ -367,8 +367,15 @@ Editor::step_mouse_mode (bool next)
 {
        switch (current_mouse_mode()) {
        case MouseObject:
-               if (next) set_mouse_mode (MouseRange);
-               else set_mouse_mode (MouseTimeFX);
+               if (next) {
+                       if (Profile->get_sae()) {
+                               set_mouse_mode (MouseZoom);
+                       } else {
+                               set_mouse_mode (MouseRange);
+                       }
+               } else {
+                       set_mouse_mode (MouseTimeFX);
+               }
                break;
 
        case MouseRange:
@@ -377,8 +384,19 @@ Editor::step_mouse_mode (bool next)
                break;
 
        case MouseZoom:
-               if (next) set_mouse_mode (MouseGain);
-               else set_mouse_mode (MouseRange);
+               if (next) {
+                       if (Profile->get_sae()) {
+                               set_mouse_mode (MouseTimeFX);
+                       } else {
+                               set_mouse_mode (MouseGain);
+                       }
+               } else {
+                       if (Profile->get_sae()) {
+                               set_mouse_mode (MouseObject);
+                       } else {
+                               set_mouse_mode (MouseRange);
+                       }
+               }
                break;
        
        case MouseGain:
@@ -387,8 +405,15 @@ Editor::step_mouse_mode (bool next)
                break;
        
        case MouseTimeFX:
-               if (next) set_mouse_mode (MouseAudition);
-               else set_mouse_mode (MouseGain);
+               if (next) {
+                       set_mouse_mode (MouseAudition);
+               } else {
+                       if (Profile->get_sae()) {
+                               set_mouse_mode (MouseZoom);
+                       } else {
+                               set_mouse_mode (MouseGain);
+                       }
+               }
                break;
 
        case MouseAudition:
@@ -508,10 +533,11 @@ Editor::set_midi_edit_cursor (MidiEditMode m)
 void
 Editor::button_selection (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
 {
-       /* in object/audition/timefx mode, any button press sets
-          the selection if the object can be selected. this is a
-          bit of hack, because we want to avoid this if the
-          mouse operation is a region alignment.
+       /* in object/audition/timefx/gain-automation mode,
+          any button press sets the selection if the object
+          can be selected. this is a bit of hack, because
+          we want to avoid this if the mouse operation is a
+          region alignment.
 
           note: not dbl-click or triple-click
        */
@@ -519,6 +545,7 @@ Editor::button_selection (ArdourCanvas::Item* item, GdkEvent* event, ItemType it
        if (((mouse_mode != MouseObject) &&
             (mouse_mode != MouseAudition || item_type != RegionItem) &&
             (mouse_mode != MouseTimeFX || item_type != RegionItem) &&
+            (mouse_mode != MouseGain) &&
             (mouse_mode != MouseRange)) ||
 
            ((event->type != GDK_BUTTON_PRESS && event->type != GDK_BUTTON_RELEASE) || event->button.button > 3)) {
@@ -537,7 +564,7 @@ Editor::button_selection (ArdourCanvas::Item* item, GdkEvent* event, ItemType it
                        }
                }
        }
-           
+
        Selection::Operation op = Keyboard::selection_type (event->button.state);
        bool press = (event->type == GDK_BUTTON_PRESS);
 
@@ -602,7 +629,7 @@ bool
 Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
 {
        Glib::RefPtr<Gdk::Window> canvas_window = const_cast<Editor*>(this)->track_canvas->get_window();
-       
+
        if (canvas_window) {
                Glib::RefPtr<const Gdk::Window> pointer_window;
                int x, y;
@@ -626,7 +653,7 @@ Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemTyp
        }
 
        button_selection (item, event, item_type);
-       
+
        if (drag_info.item == 0 &&
            (Keyboard::is_delete_event (&event->button) ||
             Keyboard::is_context_menu_event (&event->button) ||
@@ -680,27 +707,43 @@ Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemTyp
                                }
                                return true;
 
+                       case MarkerBarItem:
                        case TempoBarItem:
-                               return true;
-
                        case MeterBarItem:
+                               if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
+                                       start_cursor_grab_no_stop(&playhead_cursor->canvas_item, event);
+                               }
                                return true;
+                               break;
+
                                
                        case RangeMarkerBarItem:
-                               start_range_markerbar_op (item, event, CreateRangeMarker); 
+                               if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {                
+                                       start_cursor_grab_no_stop(&playhead_cursor->canvas_item, event);
+                               } else {
+                                       start_range_markerbar_op (item, event, CreateRangeMarker); 
+                               }       
                                return true;
                                break;
 
                        case CdMarkerBarItem:
-                               start_range_markerbar_op (item, event, CreateCDMarker); 
+                               if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
+                                       start_cursor_grab_no_stop(&playhead_cursor->canvas_item, event);
+                               } else {
+                                       start_range_markerbar_op (item, event, CreateCDMarker); 
+                               }
                                return true;
                                break;
 
                        case TransportMarkerBarItem:
-                               start_range_markerbar_op (item, event, CreateTransportMarker); 
+                               if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
+                                       start_cursor_grab_no_stop(&playhead_cursor->canvas_item, event);
+                               } else {
+                                       start_range_markerbar_op (item, event, CreateTransportMarker);
+                               }
                                return true;
                                break;
-
+                               
                        default:
                                break;
                        }
@@ -830,7 +873,13 @@ Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemTyp
                case MouseGain:
                        switch (item_type) {
                        case RegionItem:
-                               // start_line_grab_from_regionview (item, event);
+                               /* start a grab so that if we finish after moving
+                                  we can tell what happened.
+                               */
+                               drag_info.item = item;
+                               drag_info.motion_callback = &Editor::region_gain_motion_callback;
+                               drag_info.finished_callback = 0;
+                               start_grab (event, current_canvas_cursor);
                                break;
 
                        case GainLineItem:
@@ -982,7 +1031,7 @@ Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemT
 {
        nframes64_t where = event_frame (event, 0, 0);
        AutomationTimeAxisView* atv = 0;
-
+       
        /* no action if we're recording */
                                                
        if (session && session->actively_recording()) {
@@ -1001,7 +1050,7 @@ Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemT
        button_selection (item, event, item_type);
 
        /* edit events get handled here */
-       
+
        if (drag_info.item == 0 && Keyboard::is_edit_event (&event->button)) {
                switch (item_type) {
                case RegionItem:
@@ -1022,6 +1071,10 @@ Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemT
                        }
                        break;
 
+               case ControlPointItem:
+                       edit_control_point (item);
+                       break;
+
                default:
                        break;
                }
@@ -1163,36 +1216,44 @@ Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemT
                        return true;
 
                case MarkerBarItem:
-                       if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
-                               snap_to (where, 0, true);
+                       if (!_dragging_playhead) {
+                               if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
+                                       snap_to (where, 0, true);
+                               }
+                               mouse_add_new_marker (where);
                        }
-                       mouse_add_new_marker (where);
                        return true;
 
                case CdMarkerBarItem:
-                       // if we get here then a dragged range wasn't done
-                       if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
-                               snap_to (where, 0, true);
+                       if (!_dragging_playhead) {
+                               // if we get here then a dragged range wasn't done
+                               if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
+                                       snap_to (where, 0, true);
+                               }
+                               mouse_add_new_marker (where, true);
                        }
-                       mouse_add_new_marker (where, true);
                        return true;
 
                case TempoBarItem:
-                       if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
-                               snap_to (where);
+                       if (!_dragging_playhead) {
+                               if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
+                                       snap_to (where);
+                               }
+                               mouse_add_new_tempo_event (where);
                        }
-                       mouse_add_new_tempo_event (where);
                        return true;
                        
                case MeterBarItem:
-                       mouse_add_new_meter_event (pixel_to_frame (event->button.x));
+                       if (!_dragging_playhead) {
+                               mouse_add_new_meter_event (pixel_to_frame (event->button.x));
+                       } 
                        return true;
                        break;
 
                default:
                        break;
                }
-
+               
                switch (mouse_mode) {
                case MouseObject:
                        switch (item_type) {
@@ -1219,7 +1280,13 @@ Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemT
 
                        switch (item_type) {
                        case RegionItem:
-                               dynamic_cast<AudioRegionView*>(clicked_regionview)->add_gain_point_event (item, event);
+                               /* check that we didn't drag before releasing, since
+                                  its really annoying to create new control
+                                  points when doing this.
+                               */
+                               if (drag_info.first_move) { 
+                                       dynamic_cast<AudioRegionView*>(clicked_regionview)->add_gain_point_event (item, event);
+                               }
                                return true;
                                break;
                                
@@ -1571,7 +1638,7 @@ Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_
        case TempoBarItem:
        case MarkerBarItem:
                if (is_drawable()) {
-                       track_canvas->get_window()->set_cursor (*timebar_cursor);
+                       track_canvas->get_window()->set_cursor (*current_canvas_cursor);
                }
                break;
                
@@ -1783,6 +1850,9 @@ Editor::motion_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item
        case PlayheadCursorItem:
        case MarkerItem:
        case ControlPointItem:
+       case MarkerBarItem:
+       case TempoBarItem:
+       case MeterBarItem:
        case RangeMarkerBarItem:
        case TransportMarkerBarItem:
        case CdMarkerBarItem:
@@ -1809,7 +1879,9 @@ Editor::motion_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item
                  if (!from_autoscroll) {
                          maybe_autoscroll_horizontally (&event->motion);
                  }
-                 (this->*(drag_info.motion_callback)) (item, event);
+                 if (drag_info.motion_callback) {
+                         (this->*(drag_info.motion_callback)) (item, event);
+                 }
                  goto handled;
          }
          goto not_handled;
@@ -1819,6 +1891,15 @@ Editor::motion_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item
        }
 
        switch (mouse_mode) {
+       case MouseGain:
+               if (item_type == RegionItem) {
+                       if (drag_info.item && drag_info.motion_callback) {
+                               (this->*(drag_info.motion_callback)) (item, event);
+                       }
+                       goto handled;
+               }
+               break;
+
        case MouseObject:
        case MouseRange:
        case MouseZoom:
@@ -1829,7 +1910,9 @@ Editor::motion_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item
                        if (!from_autoscroll) {
                                maybe_autoscroll (&event->motion);
                        }
-                       (this->*(drag_info.motion_callback)) (item, event);
+                       if (drag_info.motion_callback) {
+                               (this->*(drag_info.motion_callback)) (item, event);
+                       }
                        goto handled;
                }
                goto not_handled;
@@ -1902,7 +1985,7 @@ Editor::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
 
        // if dragging with button2, the motion is x constrained, with Alt-button2 it is y constrained
 
-       if (event->button.button == 2) {
+       if (Keyboard::is_button2_event (&event->button)) {
                if (Keyboard::modifier_state_equals (event->button.state, Keyboard::SecondaryModifier)) {
                        drag_info.y_constrained = true;
                        drag_info.x_constrained = false;
@@ -1998,6 +2081,14 @@ Editor::end_grab (ArdourCanvas::Item* item, GdkEvent* event)
        return did_drag;
 }
 
+void
+Editor::region_gain_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
+{
+       if (drag_info.first_move && drag_info.move_threshold_passed) {
+               drag_info.first_move = false;
+       }
+}
+
 void
 Editor::start_fade_in_grab (ArdourCanvas::Item* item, GdkEvent* event)
 {
@@ -2257,6 +2348,39 @@ Editor::start_cursor_grab (ArdourCanvas::Item* item, GdkEvent* event)
        show_verbose_time_cursor (cursor->current_frame, 10);
 }
 
+void
+Editor::start_cursor_grab_no_stop (ArdourCanvas::Item* item, GdkEvent* event)
+{
+       drag_info.item = item;
+       drag_info.motion_callback = &Editor::cursor_drag_motion_callback;
+       drag_info.finished_callback = &Editor::cursor_drag_finished_ensure_locate_callback;
+
+       start_grab (event);
+
+       if ((drag_info.data = (item->get_data ("cursor"))) == 0) {
+               fatal << _("programming error: cursor canvas item has no cursor data pointer!") << endmsg;
+               /*NOTREACHED*/
+       }
+
+       Cursor* cursor = (Cursor *) drag_info.data;
+       nframes64_t where = event_frame (event, 0, 0);
+
+       snap_to(where);
+       playhead_cursor->set_position (where);
+
+       if (cursor == playhead_cursor) {
+               _dragging_playhead = true;
+
+               if (session && session->is_auditioning()) {
+                       session->cancel_audition ();
+               }
+       }
+
+       drag_info.pointer_frame_offset = drag_info.grab_frame - cursor->current_frame;  
+       
+       show_verbose_time_cursor (cursor->current_frame, 10);
+}
+
 void
 Editor::cursor_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
 {
@@ -2294,12 +2418,28 @@ Editor::cursor_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
 void
 Editor::cursor_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
 {
-       if (drag_info.first_move) return;
+       _dragging_playhead = false;
+
+       if (drag_info.first_move) {
+               return;
+       }
        
        cursor_drag_motion_callback (item, event);
+       
+       if (item == &playhead_cursor->canvas_item) {
+               if (session) {
+                       session->request_locate (playhead_cursor->current_frame, drag_info.was_rolling);
+               }
+       } 
+}
 
+void
+Editor::cursor_drag_finished_ensure_locate_callback (ArdourCanvas::Item* item, GdkEvent* event)
+{
        _dragging_playhead = false;
        
+       cursor_drag_motion_callback (item, event);
+       
        if (item == &playhead_cursor->canvas_item) {
                if (session) {
                        session->request_locate (playhead_cursor->current_frame, drag_info.was_rolling);
@@ -2431,7 +2571,7 @@ Editor::marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
        Marker* dragged_marker = (Marker*) drag_info.data;
        Marker* marker;
        Location  *real_location;
-       Location  *copy_location;
+       Location *copy_location = 0;
 
        if (drag_info.pointer_frame_offset <= drag_info.current_pointer_frame) {
                newframe = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
@@ -3066,6 +3206,28 @@ Editor::control_point_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent
        cp->line().end_drag (cp);
 }
 
+void
+Editor::edit_control_point (ArdourCanvas::Item* item)
+{
+       ControlPoint* p = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"));
+
+       if (p == 0) {
+               fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
+               /*NOTREACHED*/
+       }
+
+       ControlPointDialog d (p);
+       d.set_position (Gtk::WIN_POS_MOUSE);
+       ensure_float (d);
+
+       if (d.run () != RESPONSE_ACCEPT) {
+               return;
+       }
+
+       p->line().modify_point_y (*p, d.get_y_fraction ());
+}
+
+
 void
 Editor::start_line_grab_from_regionview (ArdourCanvas::Item* item, GdkEvent* event)
 {
@@ -3100,12 +3262,14 @@ Editor::start_line_grab (AutomationLine* line, GdkEvent* event)
        nframes64_t frame_within_region;
 
        /* need to get x coordinate in terms of parent (TimeAxisItemView)
-          origin.
+          origin, and ditto for y.
        */
 
        cx = event->button.x;
        cy = event->button.y;
+
        line->parent_group().w2i (cx, cy);
+
        frame_within_region = (nframes64_t) floor (cx * frames_per_unit);
 
        if (!line->control_points_adjacent (frame_within_region, current_line_drag_info.before, 
@@ -3121,6 +3285,11 @@ Editor::start_line_grab (AutomationLine* line, GdkEvent* event)
 
        start_grab (event, fader_cursor);
 
+       /* store grab start in parent frame */
+
+       drag_info.grab_x = cx;
+       drag_info.grab_y = cy;
+
        double fraction = 1.0 - (cy / line->height());
 
        line->start_drag (0, drag_info.grab_frame, fraction);
@@ -3136,7 +3305,7 @@ Editor::line_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
        AutomationLine* line = reinterpret_cast<AutomationLine *> (drag_info.data);
 
        double dy = drag_info.current_pointer_y - drag_info.last_pointer_y;
-
+       
        if (event->button.state & Keyboard::SecondaryModifier) {
                dy *= 0.1;
        }
@@ -3145,9 +3314,9 @@ Editor::line_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
 
        // calculate zero crossing point. back off by .01 to stay on the
        // positive side of zero
-       double _unused = 0;
        double zero_gain_y = (1.0 - ZERO_GAIN_FRACTION) * line->height() - .01;
-       line->parent_group().i2w(_unused, zero_gain_y);
+
+       // line->parent_group().i2w(_unused, zero_gain_y);
 
        // make sure we hit zero when passing through
        if ((cy < zero_gain_y and (cy - dy) > zero_gain_y)
@@ -3160,6 +3329,7 @@ Editor::line_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
        cy = max (0.0, cy);
        cy = min ((double) line->height(), cy);
 
+
        double fraction = 1.0 - (cy / line->height());
 
        bool push;
@@ -3215,7 +3385,9 @@ Editor::start_region_grab (ArdourCanvas::Item* item, GdkEvent* event)
        drag_info.last_frame_position = (nframes64_t) (clicked_regionview->region()->position() / speed);
        drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
        drag_info.source_trackview = &clicked_regionview->get_time_axis_view();
+       drag_info.source_layer = clicked_regionview->region()->layer();
        drag_info.dest_trackview = drag_info.source_trackview;
+       drag_info.dest_layer = drag_info.source_layer;
        // we want a move threshold
        drag_info.want_move_threshold = true;
        show_verbose_time_cursor (drag_info.last_frame_position, 10);
@@ -3234,6 +3406,7 @@ Editor::start_create_region_grab (ArdourCanvas::Item* item, GdkEvent* event)
        drag_info.data = clicked_axisview;
        drag_info.source_trackview = clicked_axisview;
        drag_info.dest_trackview = drag_info.source_trackview;
+       drag_info.dest_layer = drag_info.source_layer;
        drag_info.motion_callback = &Editor::create_region_drag_motion_callback;
        drag_info.finished_callback = &Editor::create_region_drag_finished_callback;
 
@@ -3263,6 +3436,7 @@ Editor::start_region_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
        
        drag_info.source_trackview = &clicked_regionview->get_time_axis_view();
        drag_info.dest_trackview = drag_info.source_trackview;
+       drag_info.dest_layer = drag_info.source_layer;
        drag_info.last_frame_position = (nframes64_t) (clicked_regionview->region()->position() / speed);
        drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
        // we want a move threshold
@@ -3299,6 +3473,7 @@ Editor::start_region_brush_grab (ArdourCanvas::Item* item, GdkEvent* event)
        drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
        drag_info.source_trackview = &clicked_regionview->get_time_axis_view();
        drag_info.dest_trackview = drag_info.source_trackview;
+       drag_info.dest_layer = drag_info.source_layer;
        // we want a move threshold
        drag_info.want_move_threshold = true;
        drag_info.brushing = true;
@@ -3375,12 +3550,13 @@ Editor::possibly_copy_regions_during_grab (GdkEvent* event)
 }
 
 bool
-Editor::check_region_drag_possible (RouteTimeAxisView** tv)
+Editor::check_region_drag_possible (RouteTimeAxisView** tv, layer_t* layer)
 {
        /* Which trackview is this ? */
 
-       TimeAxisView* tvp = trackview_by_y_position (drag_info.current_pointer_y);
-       (*tv) = dynamic_cast<RouteTimeAxisView*>(tvp);
+       std::pair<TimeAxisView*, int> const tvp = trackview_by_y_position (drag_info.current_pointer_y);
+       (*tv) = dynamic_cast<RouteTimeAxisView*> (tvp.first);
+       (*layer) = tvp.second;
 
        /* The region motion is only processed if the pointer is over
           an audio track.
@@ -3407,8 +3583,9 @@ void
 Editor::region_drag_splice_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
 {
        RouteTimeAxisView* tv;
+       layer_t layer;
        
-       if (!check_region_drag_possible (&tv)) {
+       if (!check_region_drag_possible (&tv, &layer)) {
                return;
        }
 
@@ -3469,187 +3646,278 @@ Editor::region_drag_splice_finished_callback (ArdourCanvas::Item* item, GdkEvent
 {
 }
 
+void
+Editor::visible_order_range (int* low, int* high) const
+{
+       *low = TimeAxisView::max_order ();
+       *high = 0;
+       
+       for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
+
+               RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
+               
+               if (!rtv->hidden()) {
+                       
+                       if (*high < rtv->order()) {
+                               *high = rtv->order ();
+                       }
+                       
+                       if (*low > rtv->order()) {
+                               *low = rtv->order ();
+                       }
+               }
+       }
+}
+
+/** @param new_order New track order.
+ *  @param old_order Old track order.
+ *  @param visible_y_low Lowest visible order.
+ *  @param visible_y_high Highest visible order.
+ *  @param tracks Bitset of tracks indexed by order; 0 means a audio/MIDI track, 1 means something else.
+ *  @param heigh_list Heights of tracks indexed by order.
+ *  @return true if y movement should not happen, otherwise false.
+ */
+bool
+Editor::y_movement_disallowed (
+       int new_order, int old_order, int y_span, int visible_y_low, int visible_y_high,
+       bitset<512> const & tracks, vector<int32_t> const & height_list
+       ) const
+{
+       if (new_order != old_order) {
+
+               /* this isn't the pointer track */      
+
+               if (y_span > 0) {
+
+                       /* moving up the canvas */
+                       if ( (new_order - y_span) >= visible_y_low) {
+
+                               int32_t n = 0;
+
+                               /* work out where we'll end up with this y span, taking hidden TimeAxisViews into account */
+                               int32_t visible_tracks = 0;
+                               while (visible_tracks < y_span ) {
+                                       visible_tracks++;
+                                       while (height_list[new_order - (visible_tracks - n)] == 0) {
+                                               /* passing through a hidden track */
+                                               n--;
+                                       }                 
+                               }
+                
+                               if (tracks[new_order - (y_span - n)] != 0x00) {
+                                       /* moving to a non-track; disallow */
+                                       return true;
+                               }
+                               
+
+                       } else {
+                               /* moving beyond the lowest visible track; disallow */
+                               return true;
+                       }                 
+                 
+               } else if (y_span < 0) {
+
+                       /* moving down the canvas */
+                       if ((new_order - y_span) <= visible_y_high) {
+
+                               int32_t visible_tracks = 0;
+                               int32_t n = 0;
+                               while (visible_tracks > y_span ) {
+                                       visible_tracks--;
+                     
+                                       while (height_list[new_order - (visible_tracks - n)] == 0) {
+                                               /* passing through a hidden track */
+                                               n++;
+                                       }                
+                               }
+                                               
+                               if (tracks[new_order - (y_span - n)] != 0x00) {
+                                       /* moving to a non-track; disallow */
+                                       return true;
+                               }
+
+                               
+                       } else {
+
+                               /* moving beyond the highest visible track; disallow */
+                               return true;
+                       }
+               }               
+               
+       } else {
+               
+               /* this is the pointer's track */
+               
+               if ((new_order - y_span) > visible_y_high) {
+                       /* we will overflow */
+                       return true;
+               } else if ((new_order - y_span) < visible_y_low) {
+                       /* we will overflow */
+                       return true;
+               }
+       }
+
+       return false;
+}
+
 void
 Editor::region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
 {
        double x_delta;
        double y_delta = 0;
        nframes64_t pending_region_position = 0;
-       int32_t pointer_y_span = 0, canvas_pointer_y_span = 0, original_pointer_order;
-       int32_t visible_y_high = 0, visible_y_low = 512;  //high meaning higher numbered.. not the height on the screen
+       int32_t pointer_order_span = 0, canvas_pointer_order_span = 0;
+       int32_t pointer_layer_span = 0;
+       
        bool clamp_y_axis = false;
-       vector<int32_t>  height_list(512) ;
        vector<int32_t>::iterator j;
-       RouteTimeAxisView* tv;
 
        possibly_copy_regions_during_grab (event);
 
-       if (!check_region_drag_possible (&tv)) {
+       /* *pointer* variables reflect things about the pointer; as we may be moving
+          multiple regions, much detail must be computed per-region */
+
+       /* current_pointer_view will become the TimeAxisView that we're currently pointing at, and
+          current_pointer_layer the current layer on that TimeAxisView */
+       RouteTimeAxisView* current_pointer_view;
+       layer_t current_pointer_layer;
+       if (!check_region_drag_possible (&current_pointer_view, &current_pointer_layer)) {
                return;
        }
 
-       original_pointer_order = drag_info.dest_trackview->order;
-       
+       /* TimeAxisView that we were pointing at last time we entered this method */
+       TimeAxisView const * const last_pointer_view = drag_info.dest_trackview;
+       /* the order of the track that we were pointing at last time we entered this method */
+       int32_t const last_pointer_order = last_pointer_view->order ();
+       /* the layer that we were pointing at last time we entered this method */
+       layer_t const last_pointer_layer = drag_info.dest_layer;
+
        /************************************************************
-            Y-Delta Computation
+            Y DELTA COMPUTATION
        ************************************************************/   
 
+       /* Height of TimeAxisViews, indexed by order */
+       /* XXX: hard-coded limit of TimeAxisViews */
+       vector<int32_t> height_list (512);
+       
        if (drag_info.brushing) {
                clamp_y_axis = true;
-               pointer_y_span = 0;
+               pointer_order_span = 0;
                goto y_axis_done;
        }
 
-       if ((pointer_y_span = (drag_info.dest_trackview->order - tv->order)) != 0) {
+       /* the change in track order between this callback and the last */
+       pointer_order_span = last_pointer_view->order() - current_pointer_view->order();
+       /* the change in layer between this callback and the last;
+          only meaningful if pointer_order_span == 0 (ie we've not moved tracks) */
+       pointer_layer_span = last_pointer_layer - current_pointer_layer;
+
+       if (pointer_order_span != 0) {
 
-               int32_t children = 0, numtracks = 0;
-               // XXX hard coding track limit, oh my, so very very bad
-               bitset <1024> tracks (0x00);
+               int32_t children = 0;
+               /* XXX: hard-coded limit of tracks */
+               bitset <512> tracks (0x00);
+
+               int visible_y_high;
+               int visible_y_low;
+               visible_order_range (&visible_y_low, &visible_y_high);
+               
                /* get a bitmask representing the visible tracks */
 
                for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
-                       TimeAxisView *tracklist_timeview;
-                       tracklist_timeview = (*i);
-                       RouteTimeAxisView* rtv2 = dynamic_cast<RouteTimeAxisView*>(tracklist_timeview);
+                       RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
                        TimeAxisView::Children children_list;
              
-                       /* zeroes are audio tracks. ones are other types. */
+                       /* zeroes are audio/MIDI tracks. ones are other types. */
              
-                       if (!rtv2->hidden()) {
+                       if (!rtv->hidden()) {
                                
-                               if (visible_y_high < rtv2->order) {
-                                       visible_y_high = rtv2->order;
-                               }
-                               if (visible_y_low > rtv2->order) {
-                                       visible_y_low = rtv2->order;
-                               }
-               
-                               if (!rtv2->is_track()) {                                  
-                                       tracks = tracks |= (0x01 << rtv2->order);
+                               if (!rtv->is_track()) {
+                                       /* not an audio nor MIDI track */
+                                       tracks = tracks |= (0x01 << rtv->order());
                                }
        
-                               height_list[rtv2->order] = (*i)->current_height();
+                               height_list[rtv->order()] = (*i)->current_height();
                                children = 1;
 
-                               if ((children_list = rtv2->get_child_list()).size() > 0) {
+                               if ((children_list = rtv->get_child_list()).size() > 0) {
                                        for (TimeAxisView::Children::iterator j = children_list.begin(); j != children_list.end(); ++j) { 
-                                               tracks = tracks |= (0x01 << (rtv2->order + children));
-                                               height_list[rtv2->order + children] =  (*j)->current_height();
-                                               numtracks++;
+                                               tracks = tracks |= (0x01 << (rtv->order() + children));
+                                               height_list[rtv->order() + children] = (*j)->current_height();
                                                children++;     
                                        }
                                }
-                               numtracks++;        
                        }
                }
-               /* find the actual span according to the canvas */
-
-               canvas_pointer_y_span = pointer_y_span;
-               if (drag_info.dest_trackview->order >= tv->order) {
-                       int32_t y;
-                       for (y = tv->order; y < drag_info.dest_trackview->order; y++) {
-                               if (height_list[y] == 0 ) {
-                                       canvas_pointer_y_span--;
+               
+               /* find the actual pointer span, in terms of the number of visible tracks;
+                  to do this, we reduce |pointer_order_span| by the number of hidden tracks
+                  over the span */
+
+               canvas_pointer_order_span = pointer_order_span;
+               if (last_pointer_view->order() >= current_pointer_view->order()) {
+                       for (int32_t y = current_pointer_view->order(); y < last_pointer_view->order(); y++) {
+                               if (height_list[y] == 0) {
+                                       canvas_pointer_order_span--;
                                }
                        }
                } else {
-                       int32_t y;
-                       for (y = drag_info.dest_trackview->order;y <= tv->order; y++) {
-                               if (    height_list[y] == 0 ) {
-                                       canvas_pointer_y_span++;
+                       for (int32_t y = last_pointer_view->order(); y <= current_pointer_view->order(); y++) {
+                               if (height_list[y] == 0) {
+                                       canvas_pointer_order_span++;
                                }
                        }
                }
 
                for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
-                       RegionView* rv2 = (*i);
-                       double ix1, ix2, iy1, iy2;
-                       int32_t n = 0;
+                       
+                       RegionView* rv = (*i);
 
-                       if (rv2->region()->locked()) {
+                       if (rv->region()->locked()) {
                                continue;
                        }
 
-                       rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
-                       rv2->get_canvas_frame()->i2w (ix1, iy1);
+                       double ix1, ix2, iy1, iy2;
+                       rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
+                       rv->get_canvas_frame()->i2w (ix1, iy1);
                        iy1 += vertical_adjustment.get_value() - canvas_timebars_vsize;
 
-                       TimeAxisView* tvp2 = trackview_by_y_position (iy1);
-                       RouteTimeAxisView* rtv2 = dynamic_cast<RouteTimeAxisView*>(tvp2);
-
-                       if (rtv2->order != original_pointer_order) {    
-                               /* this isn't the pointer track */      
+                       /* get the new trackview for this particular region */
+                       std::pair<TimeAxisView*, int> const tvp = trackview_by_y_position (iy1);
+                       assert (tvp.first);
+                       RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
 
-                               if (canvas_pointer_y_span > 0) {
-
-                                       /* moving up the canvas */
-                                       if ((rtv2->order - canvas_pointer_y_span) >= visible_y_low) {
-       
-                                               int32_t visible_tracks = 0;
-                                               while (visible_tracks < canvas_pointer_y_span ) {
-                                                       visible_tracks++;
-                 
-                                                       while (height_list[rtv2->order - (visible_tracks - n)] == 0) {
-                                                               /* we're passing through a hidden track */
-                                                               n--;
-                                                       }                 
-                                               }
-                
-                                               if (tracks[rtv2->order - (canvas_pointer_y_span - n)] != 0x00) {                  
-                                                       clamp_y_axis = true;
-                                               }
-                   
-                                       } else {
-                                               clamp_y_axis = true;
-                                       }                 
-                 
-                               } else if (canvas_pointer_y_span < 0) {
+                       /* I know this method has a slightly excessive argument list, but I think
+                          it's nice to separate the code out all the same, since it has such a
+                          simple result, and it makes it clear that there are no other
+                          side-effects.
+                       */
 
-                                       /*moving down the canvas*/
+                       /* XXX: not sure that we should be passing canvas_pointer_order_span in here,
+                          as surely this is a per-region thing... */
+                       
+                       clamp_y_axis = y_movement_disallowed (
+                               rtv->order(), last_pointer_order, canvas_pointer_order_span, visible_y_low, visible_y_high,
+                               tracks, height_list
+                               );
 
-                                       if ((rtv2->order - (canvas_pointer_y_span - n)) <= visible_y_high) { // we will overflow
-                   
-                   
-                                               int32_t visible_tracks = 0;
-                   
-                                               while (visible_tracks > canvas_pointer_y_span ) {
-                                                       visible_tracks--;
-                     
-                                                       while (height_list[rtv2->order - (visible_tracks - n)] == 0) {             
-                                                               n++;
-                                                       }                
-                                               }
-                                               if (  tracks[rtv2->order - ( canvas_pointer_y_span - n)] != 0x00) {
-                                                       clamp_y_axis = true;
-                           
-                                               }
-                                       } else {
-                         
-                                               clamp_y_axis = true;
-                                       }
-                               }               
-                 
-                       } else {
-                     
-                               /* this is the pointer's track */
-                               if ((rtv2->order - pointer_y_span) > visible_y_high) { // we will overflow 
-                                       clamp_y_axis = true;
-                               } else if ((rtv2->order - pointer_y_span) < visible_y_low) { // we will underflow
-                                       clamp_y_axis = true;
-                               }
-                       }             
                        if (clamp_y_axis) {
                                break;
                        }
                }
 
-       } else  if (drag_info.dest_trackview == tv) {
-               clamp_y_axis = true;
-       }         
+       } else if (drag_info.dest_trackview == current_pointer_view) {
+
+               if (current_pointer_layer == last_pointer_layer) {
+                       /* No movement; clamp */
+                       clamp_y_axis = true;
+               } 
+       }
 
   y_axis_done:
        if (!clamp_y_axis) {
-               drag_info.dest_trackview = tv;        
+               drag_info.dest_trackview = current_pointer_view;
+               drag_info.dest_layer = current_pointer_layer;
        }
          
        /************************************************************
@@ -3697,8 +3965,6 @@ Editor::region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
                        pending_region_position = drag_info.last_frame_position;
                }
 
-               // printf ("3: pending_region_position= %lu    %lu\n", pending_region_position, drag_info.last_frame_position );
-
                bool x_move_allowed;
                
                if (Config->get_edit_mode() == Lock) {
@@ -3724,16 +3990,15 @@ Editor::region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
                                x_delta = -((double) (drag_info.last_frame_position - pending_region_position) / frames_per_unit);
                                for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
 
-                                       RegionView* rv2 = (*i);
+                                       RegionView* rv = (*i);
 
                                        // If any regionview is at zero, we need to know so we can stop further leftward motion.
        
                                        double ix1, ix2, iy1, iy2;
-                                       rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
-                                       rv2->get_canvas_frame()->i2w (ix1, iy1);
-                       
+                                       rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
+                                       rv->get_canvas_frame()->i2w (ix1, iy1);
+
                                        if (-x_delta > ix1 + horizontal_adjustment.get_value()) {
-                                               //      do_move = false;
                                                x_delta = 0;
                                                pending_region_position = drag_info.last_frame_position;
                                                break;
@@ -3758,9 +4023,9 @@ Editor::region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
            PREPARE TO MOVE
        ************************************************************/
 
-       if (x_delta == 0 && (pointer_y_span == 0)) {
+       if (x_delta == 0 && pointer_order_span == 0 && pointer_layer_span == 0) {
                /* haven't reached next snap point, and we're not switching
-                  trackviews. nothing to do.
+                  trackviews nor layers. nothing to do.
                */
                return;
        }
@@ -3781,29 +4046,34 @@ Editor::region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
                const list<RegionView*>& layered_regions = selection->regions.by_layer();
                
                for (list<RegionView*>::const_iterator i = layered_regions.begin(); i != layered_regions.end(); ++i) {
-           
+
                        RegionView* rv = (*i);
-                       double ix1, ix2, iy1, iy2;
-                       int32_t temp_pointer_y_span = pointer_y_span;
 
                        if (rv->region()->locked()) {
                                continue;
                        }
 
-                       /* get item BBox, which will be relative to parent. so we have
-                          to query on a child, then convert to world coordinates using
-                          the parent.
-                       */
+                       /* here we are calculating the y distance from the
+                          top of the first track view to the top of the region
+                          area of the track view that we're working on */
 
-                       rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
-                       rv->get_canvas_frame()->i2w (ix1, iy1);
+                       /* this x value is just a dummy value so that we have something
+                          to pass to i2w () */
+
+                       double ix1 = 0;
+
+                       /* distance from the top of this track view to the region area
+                          of our track view is always 1 */
                        
-                       cerr << "adjust y from " << iy1 << " using "
-                            << vertical_adjustment.get_value() << " - "
-                            << canvas_timebars_vsize
-                            << endl;
+                       double iy1 = 1;
 
-                       iy1 += get_trackview_group_vertical_offset ();;
+                       /* convert to world coordinates, ie distance from the top of
+                          the ruler section */
+                       
+                       rv->get_canvas_frame()->i2w (ix1, iy1);
+
+                       /* compensate for the ruler section and the vertical scrollbar position */
+                       iy1 += get_trackview_group_vertical_offset ();
 
                        if (drag_info.first_move) {
 
@@ -3818,74 +4088,116 @@ Editor::region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
                                   parent groups have different coordinates.
                                */
 
-                               rv->get_canvas_group()->property_y() =  iy1 - 1;
+                               rv->get_canvas_group()->property_y() = iy1 - 1;
                                rv->get_canvas_group()->reparent(*_region_motion_group);
 
                                rv->fake_set_opaque (true);
                        }
 
-                       TimeAxisView* tvp2 = trackview_by_y_position (iy1);
-                       RouteTimeAxisView* canvas_rtv = dynamic_cast<RouteTimeAxisView*>(tvp2);
-                       RouteTimeAxisView* temp_rtv;
+                       /* current view for this particular region */
+                       std::pair<TimeAxisView*, int> pos = trackview_by_y_position (iy1);
+                       RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (pos.first);
 
-                       if ((pointer_y_span != 0) && !clamp_y_axis) {
-                               y_delta = 0;
+                       if (pointer_order_span != 0 && !clamp_y_axis) {
+
+                               /* INTER-TRACK MOVEMENT */
+
+                               /* move through the height list to the track that the region is currently on */
+                               vector<int32_t>::iterator j = height_list.begin ();
                                int32_t x = 0;
-                               for (j = height_list.begin(); j!= height_list.end(); j++) {     
-                                       if (x == canvas_rtv->order) {
-                                               /* we found the track the region is on */
-                                               if (x != original_pointer_order) {
-                                                       /*this isn't from the same track we're dragging from */
-                                                       temp_pointer_y_span = canvas_pointer_y_span;
-                                               }                 
-                                               while (temp_pointer_y_span > 0) {
-                                                       /* we're moving up canvas-wise,
-                                                          so  we need to find the next track height
-                                                       */
-                                                       if (j != height_list.begin()) {           
-                                                               j--;
+                               while (j != height_list.end () && x != rtv->order ()) {
+                                       ++x;
+                                       ++j;
+                               }
+
+                               y_delta = 0;
+                               int32_t temp_pointer_order_span = canvas_pointer_order_span;
+
+                               if (j != height_list.end ()) {
+
+                                       /* Account for layers in the original and
+                                          destination tracks.  If we're moving around in layers we assume
+                                          that only one track is involved, so it's ok to use *pointer*
+                                          variables here. */
+
+                                       StreamView* lv = last_pointer_view->view ();
+                                       assert (lv);
+
+                                       /* move to the top of the last trackview */
+                                       if (lv->layer_display () == Stacked) {
+                                               y_delta -= (lv->layers() - last_pointer_layer - 1) * lv->child_height ();
+                                       }
+                                       
+                                       StreamView* cv = current_pointer_view->view ();
+                                       assert (cv);
+
+                                       /* move to the right layer on the current trackview */
+                                       if (cv->layer_display () == Stacked) {
+                                               y_delta += (cv->layers() - current_pointer_layer - 1) * cv->child_height ();
+                                       }
+
+                                       /* And for being on a non-topmost layer on the new
+                                          track */
+
+                                       while (temp_pointer_order_span > 0) {
+                                               /* we're moving up canvas-wise,
+                                                  so we need to find the next track height
+                                               */
+                                               if (j != height_list.begin()) {           
+                                                       j--;
+                                               }
+
+                                               if (x != last_pointer_order) {
+                                                       if ((*j) == 0) {
+                                                               ++temp_pointer_order_span;
                                                        }
-                                                       if (x != original_pointer_order) { 
-                                                               /* we're not from the dragged track, so ignore hidden tracks. */              
-                                                               if ((*j) == 0) {
-                                                                       temp_pointer_y_span++;
-                                                               }
-                                                       }          
-                                                       y_delta -= (*j);        
-                                                       temp_pointer_y_span--;  
                                                }
 
-                                               while (temp_pointer_y_span < 0) {                 
-                                                       y_delta += (*j);
-                                                       if (x != original_pointer_order) { 
-                                                               if ((*j) == 0) {
-                                                                       temp_pointer_y_span--;
-                                                               }
-                                                       }          
-                   
-                                                       if (j != height_list.end()) {                 
-                                                               j++;
+                                               y_delta -= (*j);
+                                               temp_pointer_order_span--;
+                                       }
+
+                                       while (temp_pointer_order_span < 0) {
+
+                                               y_delta += (*j);
+
+                                               if (x != last_pointer_order) {
+                                                       if ((*j) == 0) {
+                                                               --temp_pointer_order_span;
                                                        }
-                                                       temp_pointer_y_span++;
                                                }
-                                               /* find out where we'll be when we move and set height accordingly */
-                 
-                                               tvp2 = trackview_by_y_position (iy1 + y_delta);
-                                               temp_rtv = dynamic_cast<RouteTimeAxisView*>(tvp2);
-                                               rv->set_height (temp_rtv->current_height());
+                                               
+                                               if (j != height_list.end()) {                 
+                                                       j++;
+                                               }
 
-                                               /*   if you un-comment the following, the region colours will follow the track colours whilst dragging,
-                                                    personally, i think this can confuse things, but never mind.
-                                               */
-                                 
-                                               //const GdkColor& col (temp_rtv->view->get_region_color());
-                                               //rv->set_color (const_cast<GdkColor&>(col));
-                                               break;          
+                                               temp_pointer_order_span++;
                                        }
-                                       x++;
+
+                                       
+                                       /* find out where we'll be when we move and set height accordingly */
+
+                                       std::pair<TimeAxisView*, int> const pos = trackview_by_y_position (iy1 + y_delta);
+                                       RouteTimeAxisView const * temp_rtv = dynamic_cast<RouteTimeAxisView*> (pos.first);
+                                       rv->set_height (temp_rtv->view()->child_height());
+                               
+                                       /* if you un-comment the following, the region colours will follow
+                                          the track colours whilst dragging; personally
+                                          i think this can confuse things, but never mind.
+                                       */
+                                       
+                                       //const GdkColor& col (temp_rtv->view->get_region_color());
+                                       //rv->set_color (const_cast<GdkColor&>(col));
                                }
                        }
 
+                       if (pointer_order_span == 0 && pointer_layer_span != 0 && !clamp_y_axis) {
+
+                               /* INTER-LAYER MOVEMENT in the same track */
+                               y_delta = rtv->view()->child_height () * pointer_layer_span;
+                       }
+
+
                        if (drag_info.brushing) {
                                mouse_brush_insert_region (rv, pending_region_position);
                        } else {
@@ -3922,6 +4234,8 @@ Editor::region_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event
        pair<PlaylistSet::iterator,bool> insert_result, frozen_insert_result;
        nframes64_t drag_delta;
        bool changed_tracks, changed_position;
+       std::pair<TimeAxisView*, int> tvp;
+       std::map<RegionView*, RouteTimeAxisView*> final;
 
        /* first_move is set to false if the regionview has been moved in the 
           motion handler. 
@@ -3976,33 +4290,40 @@ Editor::region_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event
        }
 
        begin_reversible_command (op_string);
-
        changed_position = (drag_info.last_frame_position != (nframes64_t) (clicked_regionview->region()->position()));
-       changed_tracks = (trackview_by_y_position (drag_info.current_pointer_y) != &clicked_regionview->get_time_axis_view());
+       tvp = trackview_by_y_position (drag_info.current_pointer_y);
+       changed_tracks = (tvp.first != &clicked_regionview->get_time_axis_view());
 
        drag_delta = clicked_regionview->region()->position() - drag_info.last_frame_position;
 
        track_canvas->update_now ();
 
-       for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ) {
-                       
-               RegionView* rv = (*i);              
+       /* make a list of where each region ended up */
+       for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
+
                double ix1, ix2, iy1, iy2;
-               rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
-               rv->get_canvas_frame()->i2w (ix1, iy1);
+               (*i)->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
+               (*i)->get_canvas_frame()->i2w (ix1, iy1);
                iy1 += vertical_adjustment.get_value() - canvas_timebars_vsize;
 
-               TimeAxisView* dest_tv = trackview_by_y_position (iy1);
-               RouteTimeAxisView* dest_rtv = dynamic_cast<RouteTimeAxisView*>(dest_tv);
+               std::pair<TimeAxisView*, int> tv = trackview_by_y_position (iy1);
+               final[*i] = dynamic_cast<RouteTimeAxisView*> (tv.first);
+       }
+
+       for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ) {
+
+               RegionView* rv = (*i);
+               RouteTimeAxisView* dest_rtv = final[*i];
+
                nframes64_t where;
 
                if (rv->region()->locked()) {
                        ++i;
                        continue;
                }
-               
+
                if (changed_position && !drag_info.x_constrained) {
--                      where = rv->region()->position() - drag_delta;
+                       where = rv->region()->position() - drag_delta;
                } else {
                        where = rv->region()->position();
                }
@@ -5538,7 +5859,9 @@ Editor::end_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
                }
                
        } else {
-               selection->clear_tracks();
+               if (!getenv("ARDOUR_SAE")) {
+                       selection->clear_tracks();
+               }
                selection->clear_regions();
                selection->clear_points ();
                selection->clear_lines ();