Handle changed session duration & resizing canvas better wrt end marker, remove unuse...
[ardour.git] / gtk2_ardour / editor_mouse.cc
index 6065c1bf1df4458f141963113fd75e378fce8220..1a03f66f2427935d32794bd4281fd5d4f0f7731e 100644 (file)
@@ -1,3 +1,4 @@
+
 /*
     Copyright (C) 2000-2001 Paul Davis 
 
 
 #include <pbd/error.h>
 #include <gtkmm2ext/utils.h>
+#include <gtkmm2ext/tearoff.h>
 #include <pbd/memento_command.h>
+#include <pbd/basename.h>
 
 #include "ardour_ui.h"
 #include "editor.h"
 #include "time_axis_view.h"
 #include "audio_time_axis.h"
 #include "audio_region_view.h"
+#include "midi_region_view.h"
 #include "marker.h"
 #include "streamview.h"
 #include "region_gain_line.h"
 #include "automation_time_axis.h"
+#include "control_point.h"
 #include "prompter.h"
 #include "utils.h"
 #include "selection.h"
 #include <ardour/route.h>
 #include <ardour/audio_track.h>
 #include <ardour/audio_diskstream.h>
+#include <ardour/midi_diskstream.h>
 #include <ardour/playlist.h>
 #include <ardour/audioplaylist.h>
 #include <ardour/audioregion.h>
+#include <ardour/midi_region.h>
 #include <ardour/dB.h>
 #include <ardour/utils.h>
 #include <ardour/region_factory.h>
+#include <ardour/source_factory.h>
 
 #include <bitset>
 
@@ -68,8 +76,44 @@ using namespace sigc;
 using namespace Gtk;
 using namespace Editing;
 
-nframes_t
-Editor::event_frame (GdkEvent* event, double* pcx, double* pcy)
+const static double ZERO_GAIN_FRACTION = gain_to_slider_position(dB_to_coefficient(0.0));
+
+bool
+Editor::mouse_frame (nframes64_t& where, bool& in_track_canvas) const
+{
+       int x, y;
+       double wx, wy;
+       Gdk::ModifierType mask;
+       Glib::RefPtr<Gdk::Window> canvas_window = const_cast<Editor*>(this)->track_canvas->get_window();
+       Glib::RefPtr<const Gdk::Window> pointer_window;
+
+       if (!canvas_window) {
+               return false;
+       }
+       
+       pointer_window = canvas_window->get_pointer (x, y, mask);
+
+       if (pointer_window == track_canvas->get_bin_window()) {
+               wx = x;
+               wy = y;
+               in_track_canvas = true;
+
+       } else {
+               in_track_canvas = false;
+                       return false;
+       }
+
+       GdkEvent event;
+       event.type = GDK_BUTTON_RELEASE;
+       event.button.x = wx;
+       event.button.y = wy;
+       
+       where = event_frame (&event, 0, 0);
+       return true;
+}
+
+nframes64_t
+Editor::event_frame (GdkEvent* event, double* pcx, double* pcy) const
 {
        double cx, cy;
 
@@ -88,18 +132,24 @@ Editor::event_frame (GdkEvent* event, double* pcx, double* pcy)
        case GDK_BUTTON_PRESS:
        case GDK_2BUTTON_PRESS:
        case GDK_3BUTTON_PRESS:
-               track_canvas.w2c(event->button.x, event->button.y, *pcx, *pcy);
+
+               *pcx = event->button.x;
+               *pcy = event->button.y;
+               _trackview_group->w2i(*pcx, *pcy);
                break;
        case GDK_MOTION_NOTIFY:
-               track_canvas.w2c(event->motion.x, event->motion.y, *pcx, *pcy);
+       
+               *pcx = event->motion.x;
+               *pcy = event->motion.y;
+               _trackview_group->w2i(*pcx, *pcy);
                break;
        case GDK_ENTER_NOTIFY:
        case GDK_LEAVE_NOTIFY:
-               track_canvas.w2c(event->crossing.x, event->crossing.y, *pcx, *pcy);
+               track_canvas->w2c(event->crossing.x, event->crossing.y, *pcx, *pcy);
                break;
        case GDK_KEY_PRESS:
        case GDK_KEY_RELEASE:
-               // track_canvas.w2c(event->key.x, event->key.y, *pcx, *pcy);
+               // track_canvas->w2c(event->key.x, event->key.y, *pcx, *pcy);
                break;
        default:
                warning << string_compose (_("Editor::event_frame() used on unhandled event type %1"), event->type) << endmsg;
@@ -157,12 +207,69 @@ Editor::mouse_mode_toggled (MouseMode m)
                        set_mouse_mode (m);
                }
                break;
+       
+       case MouseNote:
+               if (mouse_note_button.get_active()) {
+                       set_mouse_mode (m);
+               }
+               break;
 
        default:
                break;
        }
 }      
 
+Gdk::Cursor*
+Editor::which_grabber_cursor ()
+{
+       switch (_edit_point) {
+       case EditAtMouse:
+               return grabber_edit_point_cursor;
+               break;
+       default:
+               break;
+       }
+       return grabber_cursor;
+}
+
+void
+Editor::set_canvas_cursor ()
+{
+       switch (mouse_mode) {
+       case MouseRange:
+               current_canvas_cursor = selector_cursor;
+               break;
+
+       case MouseObject:
+               current_canvas_cursor = which_grabber_cursor();
+               break;
+
+       case MouseGain:
+               current_canvas_cursor = cross_hair_cursor;
+               break;
+
+       case MouseZoom:
+               current_canvas_cursor = zoom_cursor;
+               break;
+
+       case MouseTimeFX:
+               current_canvas_cursor = time_fx_cursor; // just use playhead
+               break;
+
+       case MouseAudition:
+               current_canvas_cursor = speaker_cursor;
+               break;
+       
+       case MouseNote:
+               set_midi_edit_cursor (current_midi_edit_mode());
+               break;
+       }
+
+       if (is_drawable()) {
+               track_canvas->get_window()->set_cursor(*current_canvas_cursor);
+       }
+}
+
 void
 Editor::set_mouse_mode (MouseMode m, bool force)
 {
@@ -204,7 +311,7 @@ Editor::set_mouse_mode (MouseMode m, bool force)
                }
        }
 
-       /* XXX the hack of unsetting all other buttongs should go 
+       /* XXX the hack of unsetting all other buttons should go 
           away once GTK2 allows us to use regular radio buttons drawn like
           normal buttons, rather than my silly GroupedButton hack.
        */
@@ -214,40 +321,45 @@ Editor::set_mouse_mode (MouseMode m, bool force)
        switch (mouse_mode) {
        case MouseRange:
                mouse_select_button.set_active (true);
-               current_canvas_cursor = selector_cursor;
                break;
 
        case MouseObject:
                mouse_move_button.set_active (true);
-               current_canvas_cursor = grabber_cursor;
                break;
 
        case MouseGain:
                mouse_gain_button.set_active (true);
-               current_canvas_cursor = cross_hair_cursor;
                break;
 
        case MouseZoom:
                mouse_zoom_button.set_active (true);
-               current_canvas_cursor = zoom_cursor;
                break;
 
        case MouseTimeFX:
                mouse_timefx_button.set_active (true);
-               current_canvas_cursor = time_fx_cursor; // just use playhead
                break;
 
        case MouseAudition:
                mouse_audition_button.set_active (true);
-               current_canvas_cursor = speaker_cursor;
+               break;
+       
+       case MouseNote:
+               mouse_note_button.set_active (true);
+               set_midi_edit_cursor (current_midi_edit_mode());
                break;
        }
 
-       ignore_mouse_mode_toggle = false;
-
-       if (is_drawable()) {
-               track_canvas.get_window()->set_cursor(*current_canvas_cursor);
+       if (midi_tools_tearoff) {
+               if (mouse_mode == MouseNote) {
+                       midi_tools_tearoff->show();
+               } else {
+                       midi_tools_tearoff->hide();
+               }
        }
+       
+       ignore_mouse_mode_toggle = false;
+       
+       set_canvas_cursor ();
 }
 
 void
@@ -283,14 +395,119 @@ Editor::step_mouse_mode (bool next)
                if (next) set_mouse_mode (MouseObject);
                else set_mouse_mode (MouseTimeFX);
                break;
+       
+       case MouseNote:
+               if (next) set_mouse_mode (MouseObject);
+               else set_mouse_mode (MouseAudition);
+               break;
        }
 }
 
 void
-Editor::button_selection (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
+Editor::midi_edit_mode_toggled (MidiEditMode m)
+{
+       if (ignore_midi_edit_mode_toggle) {
+               return;
+       }
+
+       switch (m) {
+       case MidiEditPencil:
+               if (midi_tool_pencil_button.get_active())
+                       set_midi_edit_mode (m);
+               break;
+
+       case MidiEditSelect:
+               if (midi_tool_select_button.get_active())
+                       set_midi_edit_mode (m);
+               break;
+
+       case MidiEditResize:
+               if (midi_tool_resize_button.get_active())
+                       set_midi_edit_mode (m);
+               break;
+
+       case MidiEditErase:
+               if (midi_tool_erase_button.get_active())
+                       set_midi_edit_mode (m);
+               break;
+
+       default:
+               break;
+       }
+
+       set_midi_edit_cursor(m);
+}      
+
+
+void
+Editor::set_midi_edit_mode (MidiEditMode m, bool force)
+{
+       if (drag_info.item) {
+               return;
+       }
+
+       if (!force && m == midi_edit_mode) {
+               return;
+       }
+       
+       midi_edit_mode = m;
+
+       instant_save ();
+       
+       ignore_midi_edit_mode_toggle = true;
+
+       switch (midi_edit_mode) {
+       case MidiEditPencil:
+               midi_tool_pencil_button.set_active (true);
+               break;
+
+       case MidiEditSelect:
+               midi_tool_select_button.set_active (true);
+               break;
+
+       case MidiEditResize:
+               midi_tool_resize_button.set_active (true);
+               break;
+
+       case MidiEditErase:
+               midi_tool_erase_button.set_active (true);
+               break;
+       }
+
+       ignore_midi_edit_mode_toggle = false;
+
+       set_midi_edit_cursor (current_midi_edit_mode());
+
+       if (is_drawable()) {
+               track_canvas->get_window()->set_cursor(*current_canvas_cursor);
+       }
+}
+
+void
+Editor::set_midi_edit_cursor (MidiEditMode m)
 {
-       bool commit = false;
+       switch (midi_edit_mode) {
+       case MidiEditPencil:
+               current_canvas_cursor = midi_pencil_cursor;
+               break;
+
+       case MidiEditSelect:
+               current_canvas_cursor = midi_select_cursor;
+               break;
+
+       case MidiEditResize:
+               current_canvas_cursor = midi_resize_cursor;
+               break;
 
+       case MidiEditErase:
+               current_canvas_cursor = midi_erase_cursor;
+               break;
+       }
+}
+
+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
@@ -304,7 +521,7 @@ Editor::button_selection (ArdourCanvas::Item* item, GdkEvent* event, ItemType it
             (mouse_mode != MouseTimeFX || item_type != RegionItem) &&
             (mouse_mode != MouseRange)) ||
 
-           (event->type != GDK_BUTTON_PRESS && event->type != GDK_BUTTON_RELEASE || event->button.button > 3)) {
+           ((event->type != GDK_BUTTON_PRESS && event->type != GDK_BUTTON_RELEASE) || event->button.button > 3)) {
                
                return;
        }
@@ -329,66 +546,80 @@ Editor::button_selection (ArdourCanvas::Item* item, GdkEvent* event, ItemType it
        switch (item_type) {
        case RegionItem:
                if (mouse_mode != MouseRange) {
-                       commit = set_selected_regionview_from_click (press, op, true);
+                       set_selected_regionview_from_click (press, op, true);
                } else if (event->type == GDK_BUTTON_PRESS) {
-                       commit = set_selected_track_from_click (press, op, false);
+                       set_selected_track_as_side_effect ();
                }
                break;
                
-       case RegionViewNameHighlight:
-       case RegionViewName:
+       case RegionViewNameHighlight:
+       case RegionViewName:
                if (mouse_mode != MouseRange) {
-                       commit = set_selected_regionview_from_click (press, op, true);
+                       set_selected_regionview_from_click (press, op, true);
                } else if (event->type == GDK_BUTTON_PRESS) {
-                       commit = set_selected_track_from_click (press, op, false);
+                       set_selected_track_as_side_effect ();
                }
                break;
 
+
        case FadeInHandleItem:
        case FadeInItem:
        case FadeOutHandleItem:
        case FadeOutItem:
                if (mouse_mode != MouseRange) {
-                       commit = set_selected_regionview_from_click (press, op, true);
+                       set_selected_regionview_from_click (press, op, true);
                } else if (event->type == GDK_BUTTON_PRESS) {
-                       commit = set_selected_track_from_click (press, op, false);
+                       set_selected_track_as_side_effect ();
                }
                break;
-               
-       case GainAutomationControlPointItem:
-       case PanAutomationControlPointItem:
-       case RedirectAutomationControlPointItem:
+
+       case ControlPointItem:
+               set_selected_track_as_side_effect ();
                if (mouse_mode != MouseRange) {
-                       commit = set_selected_control_point_from_click (op, false);
+                       set_selected_control_point_from_click (op, false);
                }
                break;
                
        case StreamItem:
                /* for context click or range selection, select track */
                if (event->button.button == 3) {
-                       commit = set_selected_track_from_click (press, op, true);
+                       set_selected_track_as_side_effect ();
                } else if (event->type == GDK_BUTTON_PRESS && mouse_mode == MouseRange) {
-                       commit = set_selected_track_from_click (press, op, false);
+                       set_selected_track_as_side_effect ();
                }
                break;
                    
        case AutomationTrackItem:
-               commit = set_selected_track_from_click (press, op, true);
+               set_selected_track_as_side_effect (true);
                break;
                
        default:
                break;
        }
-       
-//     if (commit) {
-//             commit_reversible_command ();
-//     }
 }
 
 bool
 Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
 {
-       track_canvas.grab_focus();
+       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;
+               double wx, wy;
+               Gdk::ModifierType mask;
+
+               pointer_window = canvas_window->get_pointer (x, y, mask);
+               
+               if (pointer_window == track_canvas->get_bin_window()) {
+                       track_canvas->window_to_world (x, y, wx, wy);
+                       allow_vertical_scroll = true;
+               } else {
+                       allow_vertical_scroll = false;
+               }
+       }
+
+       track_canvas->grab_focus();
 
        if (session && session->actively_recording()) {
                return true;
@@ -421,14 +652,12 @@ Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemTyp
                        */
                        
                        switch (item_type) {
-                       case EditCursorItem:
                        case PlayheadCursorItem:
                                start_cursor_grab (item, event);
                                return true;
 
                        case MarkerItem:
-                               if (Keyboard::modifier_state_equals (event->button.state, 
-                                                                    Keyboard::ModifierMask(Keyboard::Control|Keyboard::Shift))) {
+                               if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
                                        hide_marker (item, event);
                                } else {
                                        start_marker_grab (item, event);
@@ -436,7 +665,7 @@ Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemTyp
                                return true;
 
                        case TempoMarkerItem:
-                               if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
+                               if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
                                        start_tempo_marker_copy_grab (item, event);
                                } else {
                                        start_tempo_marker_grab (item, event);
@@ -444,7 +673,7 @@ Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemTyp
                                return true;
 
                        case MeterMarkerItem:
-                               if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
+                               if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
                                        start_meter_marker_copy_grab (item, event);
                                } else {
                                        start_meter_marker_grab (item, event);
@@ -462,6 +691,11 @@ Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemTyp
                                return true;
                                break;
 
+                       case CdMarkerBarItem:
+                               start_range_markerbar_op (item, event, CreateCDMarker); 
+                               return true;
+                               break;
+
                        case TransportMarkerBarItem:
                                start_range_markerbar_op (item, event, CreateTransportMarker); 
                                return true;
@@ -485,14 +719,13 @@ Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemTyp
 
                        case SelectionItem:
                                if (Keyboard::modifier_state_contains 
-                                   (event->button.state, Keyboard::ModifierMask(Keyboard::Alt))) {
+                                   (event->button.state, Keyboard::ModifierMask(Keyboard::SecondaryModifier))) {
                                        // contains and not equals because I can't use alt as a modifier alone.
                                        start_selection_grab (item, event);
-                               } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
+                               } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
                                        /* grab selection for moving */
                                        start_selection_op (item, event, SelectionMove);
-                               }
-                               else {
+                               } else {
                                        /* this was debated, but decided the more common action was to
                                           make a new selection */
                                        start_selection_op (item, event, CreateSelection);
@@ -506,7 +739,7 @@ Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemTyp
                        break;
                        
                case MouseObject:
-                       if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::Control|Keyboard::Alt)) &&
+                       if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::SecondaryModifier)) &&
                            event->type == GDK_BUTTON_PRESS) {
                                
                                start_rubberband_select (item, event);
@@ -523,7 +756,7 @@ Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemTyp
                                        return true;
 
                                case RegionItem:
-                                       if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
+                                       if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
                                                start_region_copy_grab (item, event);
                                        } else if (Keyboard::the_keyboard().key_is_down (GDK_b)) {
                                                start_region_brush_grab (item, event);
@@ -539,20 +772,16 @@ Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemTyp
                                        
                                case RegionViewName:
                                        /* rename happens on edit clicks */
-                                               start_trim (clicked_regionview->get_name_highlight(), event);
-                                               return true;
+                                       start_trim (clicked_regionview->get_name_highlight(), event);
+                                       return true;
                                        break;
 
-                               case GainAutomationControlPointItem:
-                               case PanAutomationControlPointItem:
-                               case RedirectAutomationControlPointItem:
+                               case ControlPointItem:
                                        start_control_point_grab (item, event);
                                        return true;
                                        break;
                                        
-                               case GainAutomationLineItem:
-                               case PanAutomationLineItem:
-                               case RedirectAutomationLineItem:
+                               case AutomationLineItem:
                                        start_line_grab_from_line (item, event);
                                        return true;
                                        break;
@@ -562,7 +791,7 @@ Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemTyp
                                        start_rubberband_select (item, event);
                                        break;
                                        
-                               /* <CMT Additions> */
+#ifdef WITH_CMT
                                case ImageFrameHandleStartItem:
                                        imageframe_start_handle_op(item, event) ;
                                        return(true) ;
@@ -579,16 +808,13 @@ Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemTyp
                                        markerview_item_end_handle_op(item, event) ;
                                        return(true) ;
                                        break ;
-                               /* </CMT Additions> */
-                               
-                               /* <CMT Additions> */
                                case MarkerViewItem:
                                        start_markerview_grab(item, event) ;
                                        break ;
                                case ImageFrameItem:
                                        start_imageframe_grab(item, event) ;
                                        break ;
-                               /* </CMT Additions> */
+#endif
 
                                case MarkerBarItem:
                                        
@@ -607,17 +833,11 @@ Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemTyp
                                // start_line_grab_from_regionview (item, event);
                                break;
 
-                       case GainControlPointItem:
-                               start_control_point_grab (item, event);
-                               return true;
-                               
                        case GainLineItem:
                                start_line_grab_from_line (item, event);
                                return true;
 
-                       case GainAutomationControlPointItem:
-                       case PanAutomationControlPointItem:
-                       case RedirectAutomationControlPointItem:
+                       case ControlPointItem:
                                start_control_point_grab (item, event);
                                return true;
                                break;
@@ -629,15 +849,11 @@ Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemTyp
                        break;
 
                        switch (item_type) {
-                       case GainAutomationControlPointItem:
-                       case PanAutomationControlPointItem:
-                       case RedirectAutomationControlPointItem:
+                       case ControlPointItem:
                                start_control_point_grab (item, event);
                                break;
 
-                       case GainAutomationLineItem:
-                       case PanAutomationLineItem:
-                       case RedirectAutomationLineItem:
+                       case AutomationLineItem:
                                start_line_grab_from_line (item, event);
                                break;
 
@@ -668,9 +884,19 @@ Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemTyp
                        break;
 
                case MouseAudition:
-                       /* handled in release */
+                       _scrubbing = true;
+                       scrub_reversals = 0;
+                       scrub_reverse_distance = 0;
+                       last_scrub_x = event->button.x;
+                       scrubbing_direction = 0;
+                       track_canvas->get_window()->set_cursor (*transparent_cursor);
+                       /* rest handled in motion & release */
                        break;
 
+               case MouseNote:
+                       start_create_region_grab (item, event);
+                       break;
+               
                default:
                        break;
                }
@@ -682,16 +908,14 @@ Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemTyp
                        if (event->type == GDK_BUTTON_PRESS) {
                                switch (item_type) {
                                case RegionItem:
-                                       if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
+                                       if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
                                                start_region_copy_grab (item, event);
                                        } else {
                                                start_region_grab (item, event);
                                        }
-                                       
+                                       return true;
                                        break;
-                               case GainAutomationControlPointItem:
-                               case PanAutomationControlPointItem:
-                               case RedirectAutomationControlPointItem:
+                               case ControlPointItem:
                                        start_control_point_grab (item, event);
                                        return true;
                                        break;
@@ -728,7 +952,7 @@ Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemTyp
                                        
                                
                case MouseZoom:
-                       if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
+                       if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
                                temporal_zoom_session();
                        } else {
                                temporal_zoom_to_frame (true, event_frame(event));
@@ -756,7 +980,8 @@ Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemTyp
 bool
 Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
 {
-       nframes_t where = event_frame (event, 0, 0);
+       nframes64_t where = event_frame (event, 0, 0);
+       AutomationTimeAxisView* atv = 0;
 
        /* no action if we're recording */
                                                
@@ -820,7 +1045,7 @@ Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemT
                        case FadeOutHandleItem:
                                popup_fade_context_menu (1, event->button.time, item, item_type);
                                break;
-
+                       
                        case StreamItem:
                                popup_track_context_menu (1, event->button.time, item_type, false, where);
                                break;
@@ -841,7 +1066,8 @@ Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemT
 
                        case MarkerBarItem: 
                        case RangeMarkerBarItem: 
-                       case TransportMarkerBarItem: 
+                       case TransportMarkerBarItem:
+                       case CdMarkerBarItem:
                        case TempoBarItem:
                        case MeterBarItem:
                                popup_ruler_menu (pixel_to_frame(event->button.x), item_type);
@@ -858,12 +1084,12 @@ Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemT
                        case MeterMarkerItem:
                                tm_marker_context_menu (&event->button, item);
                                break;
-
+                       
                        case CrossfadeViewItem:
                                popup_track_context_menu (1, event->button.time, item_type, false, where);
                                break;
 
-                       /* <CMT Additions> */
+#ifdef WITH_CMT
                        case ImageFrameItem:
                                popup_imageframe_edit_menu(1, event->button.time, item, true) ;
                                break ;
@@ -876,8 +1102,7 @@ Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemT
                        case MarkerTimeAxisItem:
                                popup_marker_time_axis_edit_menu(1, event->button.time, item, false) ;
                                break ;
-                       /* <CMT Additions> */
-
+#endif
                                
                        default:
                                break;
@@ -910,18 +1135,14 @@ Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemT
                        }
                        break;
                        
-               case GainControlPointItem:
+               case ControlPointItem:
                        if (mouse_mode == MouseGain) {
                                remove_gain_control_point (item, event);
+                       } else {
+                               remove_control_point (item, event);
                        }
                        break;
 
-               case GainAutomationControlPointItem:
-               case PanAutomationControlPointItem:
-               case RedirectAutomationControlPointItem:
-                       remove_control_point (item, event);
-                       break;
-
                default:
                        break;
                }
@@ -933,13 +1154,10 @@ Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemT
 
                switch (item_type) {
                /* see comments in button_press_handler */
-               case EditCursorItem:
                case PlayheadCursorItem:
                case MarkerItem:
                case GainLineItem:
-               case GainAutomationLineItem:
-               case PanAutomationLineItem:
-               case RedirectAutomationLineItem:
+               case AutomationLineItem:
                case StartSelectionTrimItem:
                case EndSelectionTrimItem:
                        return true;
@@ -951,6 +1169,14 @@ Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemT
                        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);
+                       }
+                       mouse_add_new_marker (where, true);
+                       return true;
+
                case TempoBarItem:
                        if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
                                snap_to (where);
@@ -971,14 +1197,14 @@ Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemT
                case MouseObject:
                        switch (item_type) {
                        case AutomationTrackItem:
-                               dynamic_cast<AutomationTimeAxisView*>(clicked_trackview)->add_automation_event 
-                                       (item,
-                                        event,
-                                        where,
-                                        event->button.y);
+                               atv = dynamic_cast<AutomationTimeAxisView*>(clicked_routeview);
+                               if (atv) {
+                                       atv->add_automation_event (item, event, where, event->button.y);
+                               }
                                return true;
+                               
                                break;
-
+                               
                        default:
                                break;
                        }
@@ -986,8 +1212,10 @@ Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemT
 
                case MouseGain:
                        // Gain only makes sense for audio regions
-                       if ( ! dynamic_cast<AudioRegionView*>(clicked_regionview))
+
+                       if (!dynamic_cast<AudioRegionView*>(clicked_regionview)) {
                                break;
+                       }
 
                        switch (item_type) {
                        case RegionItem:
@@ -996,7 +1224,7 @@ Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemT
                                break;
                                
                        case AutomationTrackItem:
-                               dynamic_cast<AutomationTimeAxisView*>(clicked_trackview)->
+                               dynamic_cast<AutomationTimeAxisView*>(clicked_axisview)->
                                        add_automation_event (item, event, where, event->button.y);
                                return true;
                                break;
@@ -1006,15 +1234,23 @@ Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemT
                        break;
                        
                case MouseAudition:
-                       switch (item_type) {
-                       case RegionItem:
-                               audition_selected_region ();
-                               break;
-                       default:
-                               break;
-                       }
+                       _scrubbing = false;
+                       track_canvas->get_window()->set_cursor (*current_canvas_cursor);
+                       if (scrubbing_direction == 0) {
+                               /* no drag, just a click */
+                               switch (item_type) {
+                               case RegionItem:
+                                       play_selected_region ();
+                                       break;
+                               default:
+                                       break;
+                               }
+                       } else {
+                               /* make sure we stop */
+                               session->request_transport_speed (0.0);
+                       }
                        break;
-
+                       
                default:
                        break;
 
@@ -1030,9 +1266,9 @@ Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemT
                case MouseObject:
                        switch (item_type) {
                        case RegionItem:
-                               if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
+                               if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
                                        raise_region ();
-                               } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::Shift|Keyboard::Alt))) {
+                               } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier|Keyboard::SecondaryModifier))) {
                                        lower_region ();
                                } else {
                                        // Button2 click is unused
@@ -1074,33 +1310,13 @@ Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_
        Marker * marker;
        double fraction;
        
-       switch (item_type) {
-       case GainControlPointItem:
-               if (mouse_mode == MouseGain) {
-                       cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
-                       cp->set_visible (true);
-
-                       double at_x, at_y;
-                       at_x = cp->get_x();
-                       at_y = cp->get_y ();
-                       cp->item->i2w (at_x, at_y);
-                       at_x += 20.0;
-                       at_y += 20.0;
-
-                       fraction = 1.0 - (cp->get_y() / cp->line.height());
-
-                       set_verbose_canvas_cursor (cp->line.get_verbose_cursor_string (fraction), at_x, at_y);
-                       show_verbose_canvas_cursor ();
-
-                       if (is_drawable()) {
-                               track_canvas.get_window()->set_cursor (*fader_cursor);
-                       }
-               }
-               break;
+       if (last_item_entered != item) {
+               last_item_entered = item;
+               last_item_entered_n = 0;
+       }
 
-       case GainAutomationControlPointItem:
-       case PanAutomationControlPointItem:
-       case RedirectAutomationControlPointItem:
+       switch (item_type) {
+       case ControlPointItem:
                if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
                        cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
                        cp->set_visible (true);
@@ -1108,71 +1324,79 @@ Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_
                        double at_x, at_y;
                        at_x = cp->get_x();
                        at_y = cp->get_y ();
-                       cp->item->i2w (at_x, at_y);
-                       at_x += 20.0;
-                       at_y += 20.0;
+                       cp->item()->i2w (at_x, at_y);
+                       at_x += 10.0;
+                       at_y += 10.0;
 
-                       fraction = 1.0 - (cp->get_y() / cp->line.height());
+                       fraction = 1.0 - (cp->get_y() / cp->line().height());
 
-                       set_verbose_canvas_cursor (cp->line.get_verbose_cursor_string (fraction), at_x, at_y);
-                       show_verbose_canvas_cursor ();
+                       if (is_drawable() && !_scrubbing) {
+                               track_canvas->get_window()->set_cursor (*fader_cursor);
+                       }
 
-                       if (is_drawable()) {
-                               track_canvas.get_window()->set_cursor (*fader_cursor);
+                       last_item_entered_n++;
+                       set_verbose_canvas_cursor (cp->line().get_verbose_cursor_string (fraction), at_x, at_y);
+                       if (last_item_entered_n < 10) {
+                               show_verbose_canvas_cursor ();
                        }
                }
                break;
-               
+
        case GainLineItem:
                if (mouse_mode == MouseGain) {
                        ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
                        if (line)
-                               line->property_fill_color_rgba() = color_map[cEnteredGainLine];
+                               line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredGainLine.get();
                        if (is_drawable()) {
-                               track_canvas.get_window()->set_cursor (*fader_cursor);
+                               track_canvas->get_window()->set_cursor (*fader_cursor);
                        }
                }
                break;
                        
-       case GainAutomationLineItem:
-       case RedirectAutomationLineItem:
-       case PanAutomationLineItem:
+       case AutomationLineItem:
                if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
                        {
                                ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
                                if (line)
-                                       line->property_fill_color_rgba() = color_map[cEnteredAutomationLine];
+                                       line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredAutomationLine.get();
                        }
                        if (is_drawable()) {
-                               track_canvas.get_window()->set_cursor (*fader_cursor);
+                               track_canvas->get_window()->set_cursor (*fader_cursor);
                        }
                }
                break;
                
        case RegionViewNameHighlight:
                if (is_drawable() && mouse_mode == MouseObject) {
-                       track_canvas.get_window()->set_cursor (*trimmer_cursor);
+                       track_canvas->get_window()->set_cursor (*trimmer_cursor);
                }
                break;
 
        case StartSelectionTrimItem:
        case EndSelectionTrimItem:
-       /* <CMT Additions> */
+
+#ifdef WITH_CMT
        case ImageFrameHandleStartItem:
        case ImageFrameHandleEndItem:
        case MarkerViewHandleStartItem:
        case MarkerViewHandleEndItem:
-       /* </CMT Additions> */
+#endif
 
                if (is_drawable()) {
-                       track_canvas.get_window()->set_cursor (*trimmer_cursor);
+                       track_canvas->get_window()->set_cursor (*trimmer_cursor);
                }
                break;
 
-       case EditCursorItem:
        case PlayheadCursorItem:
                if (is_drawable()) {
-                       track_canvas.get_window()->set_cursor (*grabber_cursor);
+                       switch (_edit_point) {
+                       case EditAtMouse:
+                               track_canvas->get_window()->set_cursor (*grabber_edit_point_cursor);
+                               break;
+                       default:
+                               track_canvas->get_window()->set_cursor (*grabber_cursor);
+                               break;
+                       }
                }
                break;
 
@@ -1182,7 +1406,7 @@ Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_
 
                if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
                        if (mouse_mode == MouseObject && is_drawable()) {
-                               track_canvas.get_window()->set_cursor (*trimmer_cursor);
+                               track_canvas->get_window()->set_cursor (*trimmer_cursor);
                        }
                } 
                break;
@@ -1190,7 +1414,7 @@ Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_
 
        case AutomationTrackItem:
                if (is_drawable()) {
-                       Gdk::Cursor *cursor;
+                       Gdk::Cursor *cursor;
                        switch (mouse_mode) {
                        case MouseRange:
                                cursor = selector_cursor;
@@ -1203,7 +1427,7 @@ Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_
                                break;
                        }
 
-                       track_canvas.get_window()->set_cursor (*cursor);
+                       track_canvas->get_window()->set_cursor (*cursor);
 
                        AutomationTimeAxisView* atv;
                        if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
@@ -1216,10 +1440,11 @@ Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_
        case MarkerBarItem:
        case RangeMarkerBarItem:
        case TransportMarkerBarItem:
+       case CdMarkerBarItem:
        case MeterBarItem:
        case TempoBarItem:
                if (is_drawable()) {
-                       time_canvas.get_window()->set_cursor (*timebar_cursor);
+                       track_canvas->get_window()->set_cursor (*timebar_cursor);
                }
                break;
 
@@ -1227,12 +1452,13 @@ Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_
                if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
                        break;
                }
-               marker->set_color_rgba (color_map[cEnteredMarker]);
+               entered_marker = marker;
+               marker->set_color_rgba (ARDOUR_UI::config()->canvasvar_EnteredMarker.get());
                // fall through
        case MeterMarkerItem:
        case TempoMarkerItem:
                if (is_drawable()) {
-                       time_canvas.get_window()->set_cursor (*timebar_cursor);
+                       track_canvas->get_window()->set_cursor (*timebar_cursor);
                }
                break;
        case FadeInHandleItem:
@@ -1255,13 +1481,8 @@ Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_
 
        switch (item_type) {
        case GainLineItem:
-       case GainAutomationLineItem:
-       case RedirectAutomationLineItem:
-       case PanAutomationLineItem:
-       case GainControlPointItem:
-       case GainAutomationControlPointItem:
-       case PanAutomationControlPointItem:
-       case RedirectAutomationControlPointItem:
+       case AutomationLineItem:
+       case ControlPointItem:
                /* these do not affect the current entered track state */
                clear_entered_track = false;
                break;
@@ -1289,19 +1510,16 @@ Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_
        bool is_start;
 
        switch (item_type) {
-       case GainControlPointItem:
-       case GainAutomationControlPointItem:
-       case PanAutomationControlPointItem:
-       case RedirectAutomationControlPointItem:
+       case ControlPointItem:
                cp = reinterpret_cast<ControlPoint*>(item->get_data ("control_point"));
-               if (cp->line.npoints() > 1) {
-                       if (!cp->selected) {
+               if (cp->line().the_list()->interpolation() != AutomationList::Discrete) {
+                       if (cp->line().npoints() > 1 && !cp->selected()) {
                                cp->set_visible (false);
                        }
                }
                
                if (is_drawable()) {
-                       track_canvas.get_window()->set_cursor (*current_canvas_cursor);
+                       track_canvas->get_window()->set_cursor (*current_canvas_cursor);
                }
 
                hide_verbose_canvas_cursor ();
@@ -1310,23 +1528,22 @@ Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_
        case RegionViewNameHighlight:
        case StartSelectionTrimItem:
        case EndSelectionTrimItem:
-       case EditCursorItem:
        case PlayheadCursorItem:
-       /* <CMT Additions> */
+
+#ifdef WITH_CMT
        case ImageFrameHandleStartItem:
        case ImageFrameHandleEndItem:
        case MarkerViewHandleStartItem:
        case MarkerViewHandleEndItem:
-       /* </CMT Additions> */
+#endif
+
                if (is_drawable()) {
-                       track_canvas.get_window()->set_cursor (*current_canvas_cursor);
+                       track_canvas->get_window()->set_cursor (*current_canvas_cursor);
                }
                break;
 
        case GainLineItem:
-       case GainAutomationLineItem:
-       case RedirectAutomationLineItem:
-       case PanAutomationLineItem:
+       case AutomationLineItem:
                al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
                {
                        ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
@@ -1334,7 +1551,7 @@ Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_
                                line->property_fill_color_rgba() = al->get_line_color();
                }
                if (is_drawable()) {
-                       track_canvas.get_window()->set_cursor (*current_canvas_cursor);
+                       track_canvas->get_window()->set_cursor (*current_canvas_cursor);
                }
                break;
 
@@ -1342,18 +1559,19 @@ Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_
                /* see enter_handler() for notes */
                if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
                        if (is_drawable() && mouse_mode == MouseObject) {
-                               track_canvas.get_window()->set_cursor (*current_canvas_cursor);
+                               track_canvas->get_window()->set_cursor (*current_canvas_cursor);
                        }
                }
                break;
 
        case RangeMarkerBarItem:
        case TransportMarkerBarItem:
+       case CdMarkerBarItem:
        case MeterBarItem:
        case TempoBarItem:
        case MarkerBarItem:
                if (is_drawable()) {
-                       time_canvas.get_window()->set_cursor (*timebar_cursor);
+                       track_canvas->get_window()->set_cursor (*timebar_cursor);
                }
                break;
                
@@ -1361,14 +1579,16 @@ Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_
                if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
                        break;
                }
-               loc = find_location_from_marker (marker, is_start);
-               if (loc) location_flags_changed (loc, this);
+               entered_marker = 0;
+               if ((loc = find_location_from_marker (marker, is_start)) != 0) {
+                       location_flags_changed (loc, this);
+               }
                // fall through
        case MeterMarkerItem:
        case TempoMarkerItem:
                
                if (is_drawable()) {
-                       time_canvas.get_window()->set_cursor (*timebar_cursor);
+                       track_canvas->get_window()->set_cursor (*timebar_cursor);
                }
 
                break;
@@ -1387,7 +1607,7 @@ Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_
 
        case AutomationTrackItem:
                if (is_drawable()) {
-                       track_canvas.get_window()->set_cursor (*current_canvas_cursor);
+                       track_canvas->get_window()->set_cursor (*current_canvas_cursor);
                        clear_entered_track = true;
                        Glib::signal_idle().connect (mem_fun(*this, &Editor::left_automation_track));
                }
@@ -1410,45 +1630,141 @@ Editor::left_automation_track ()
        return false;
 }
 
-bool
-Editor::motion_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type, bool from_autoscroll)
+void
+Editor::scrub ()
 {
-       gint x, y;
+       double delta;
        
-       /* We call this so that MOTION_NOTIFY events continue to be
-          delivered to the canvas. We need to do this because we set
-          Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
-          the density of the events, at the expense of a round-trip
-          to the server. Given that this will mostly occur on cases
-          where DISPLAY = :0.0, and given the cost of what the motion
-          event might do, its a good tradeoff.  
-       */
-
-       track_canvas.get_pointer (x, y);
-
-       if (current_stepping_trackview) {
-               /* don't keep the persistent stepped trackview if the mouse moves */
-               current_stepping_trackview = 0;
-               step_timeout.disconnect ();
-       }
-
-       if (session && session->actively_recording()) {
-               /* Sorry. no dragging stuff around while we record */
-               return true;
-       }
-
-       drag_info.item_type = item_type;
-       drag_info.current_pointer_frame = event_frame (event, &drag_info.current_pointer_x,
-                                                      &drag_info.current_pointer_y);
-
-       if (!from_autoscroll && drag_info.item) {
+       if (scrubbing_direction == 0) {
+               /* first move */
+               session->request_locate (drag_info.current_pointer_frame, false);
+               session->request_transport_speed (0.1);
+               scrubbing_direction = 1;
+               
+       } else {
+               
+               if (last_scrub_x > drag_info.current_pointer_x) {
+                       
+                       /* pointer moved to the left */
+                       
+                       if (scrubbing_direction > 0) {
+                               
+                               /* we reversed direction to go backwards */
+                               
+                               scrub_reversals++;
+                               scrub_reverse_distance += (int) (last_scrub_x - drag_info.current_pointer_x);
+                               
+                       } else {
+                               
+                               /* still moving to the left (backwards) */
+                               
+                               scrub_reversals = 0;
+                               scrub_reverse_distance = 0;
+                               
+                               delta = 0.01 * (last_scrub_x - drag_info.current_pointer_x);
+                               session->request_transport_speed (session->transport_speed() - delta);
+                       }
+                       
+               } else {
+                       /* pointer moved to the right */
+                       
+                       if (scrubbing_direction < 0) {
+                               /* we reversed direction to go forward */
+                               
+                               scrub_reversals++;
+                               scrub_reverse_distance += (int) (drag_info.current_pointer_x - last_scrub_x);
+                               
+                       } else {
+                               /* still moving to the right */
+                               
+                               scrub_reversals = 0;
+                               scrub_reverse_distance = 0;
+                               
+                               delta = 0.01 * (drag_info.current_pointer_x - last_scrub_x);
+                               session->request_transport_speed (session->transport_speed() + delta);
+                       }
+               }
+               
+               /* if there have been more than 2 opposite motion moves detected, or one that moves
+                  back more than 10 pixels, reverse direction
+               */
+               
+               if (scrub_reversals >= 2 || scrub_reverse_distance > 10) {
+                       
+                       if (scrubbing_direction > 0) {
+                               /* was forwards, go backwards */
+                               session->request_transport_speed (-0.1);
+                               scrubbing_direction = -1;
+                       } else {
+                               /* was backwards, go forwards */
+                               session->request_transport_speed (0.1);
+                               scrubbing_direction = 1;
+                       }
+                       
+                       scrub_reverse_distance = 0;
+                       scrub_reversals = 0;
+               }
+       }
+       
+       last_scrub_x = drag_info.current_pointer_x;
+}
+
+bool
+Editor::motion_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type, bool from_autoscroll)
+{
+       if (event->motion.is_hint) {
+               gint x, y;
+               
+               /* We call this so that MOTION_NOTIFY events continue to be
+                  delivered to the canvas. We need to do this because we set
+                  Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
+                  the density of the events, at the expense of a round-trip
+                  to the server. Given that this will mostly occur on cases
+                  where DISPLAY = :0.0, and given the cost of what the motion
+                  event might do, its a good tradeoff.  
+               */
+
+               track_canvas->get_pointer (x, y);
+       } 
+
+       if (current_stepping_trackview) {
+               /* don't keep the persistent stepped trackview if the mouse moves */
+               current_stepping_trackview = 0;
+               step_timeout.disconnect ();
+       }
+
+       if (session && session->actively_recording()) {
+               /* Sorry. no dragging stuff around while we record */
+               return true;
+       }
+
+       drag_info.item_type = item_type;
+       drag_info.last_pointer_x = drag_info.current_pointer_x;
+       drag_info.last_pointer_y = drag_info.current_pointer_y;
+       drag_info.current_pointer_frame = event_frame (event, &drag_info.current_pointer_x,
+                                                      &drag_info.current_pointer_y);
+
+       
+       switch (mouse_mode) {
+       case MouseAudition:
+               if (_scrubbing) {
+                       scrub ();
+               }
+               break;
+
+       default:
+               break;
+       }
+
+
+       if (!from_autoscroll && drag_info.item) {
                /* item != 0 is the best test i can think of for dragging.
                */
                if (!drag_info.move_threshold_passed) {
 
-                       bool x_threshold_passed =  (abs ((int) (drag_info.current_pointer_x - drag_info.grab_x)) > 4);
-                       bool y_threshold_passed =  (abs ((int) (drag_info.current_pointer_y - drag_info.grab_y)) > 4);
-
+                       bool x_threshold_passed =  (::llabs ((nframes64_t) (drag_info.current_pointer_x - drag_info.grab_x)) > 4LL);
+                       bool y_threshold_passed =  (::llabs ((nframes64_t) (drag_info.current_pointer_y - drag_info.grab_y)) > 4LL);
+                       
                        drag_info.move_threshold_passed = (x_threshold_passed || y_threshold_passed);
                        
                        // and change the initial grab loc/frame if this drag info wants us to
@@ -1465,12 +1781,11 @@ Editor::motion_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item
 
        switch (item_type) {
        case PlayheadCursorItem:
-       case EditCursorItem:
        case MarkerItem:
-       case GainControlPointItem:
-       case RedirectAutomationControlPointItem:
-       case GainAutomationControlPointItem:
-       case PanAutomationControlPointItem:
+       case ControlPointItem:
+       case RangeMarkerBarItem:
+       case TransportMarkerBarItem:
+       case CdMarkerBarItem:
        case TempoMarkerItem:
        case MeterMarkerItem:
        case RegionViewNameHighlight:
@@ -1478,27 +1793,27 @@ Editor::motion_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item
        case EndSelectionTrimItem:
        case SelectionItem:
        case GainLineItem:
-       case RedirectAutomationLineItem:
-       case GainAutomationLineItem:
-       case PanAutomationLineItem:
+       case AutomationLineItem:
        case FadeInHandleItem:
        case FadeOutHandleItem:
-       /* <CMT Additions> */
+
+#ifdef WITH_CMT
        case ImageFrameHandleStartItem:
        case ImageFrameHandleEndItem:
        case MarkerViewHandleStartItem:
        case MarkerViewHandleEndItem:
-       /* </CMT Additions> */
+#endif
+
          if (drag_info.item && (event->motion.state & Gdk::BUTTON1_MASK ||
                                 (event->motion.state & Gdk::BUTTON2_MASK))) {
                  if (!from_autoscroll) {
-                         maybe_autoscroll (event);
+                         maybe_autoscroll_horizontally (&event->motion);
                  }
                  (this->*(drag_info.motion_callback)) (item, event);
                  goto handled;
          }
          goto not_handled;
-         
+         break;
        default:
                break;
        }
@@ -1508,10 +1823,11 @@ Editor::motion_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item
        case MouseRange:
        case MouseZoom:
        case MouseTimeFX:
+       case MouseNote:
                if (drag_info.item && (event->motion.state & GDK_BUTTON1_MASK ||
                                       (event->motion.state & GDK_BUTTON2_MASK))) {
                        if (!from_autoscroll) {
-                               maybe_autoscroll (event);
+                               maybe_autoscroll (&event->motion);
                        }
                        (this->*(drag_info.motion_callback)) (item, event);
                        goto handled;
@@ -1532,6 +1848,45 @@ Editor::motion_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item
        return false;
 }
 
+void
+Editor::break_drag ()
+{
+       stop_canvas_autoscroll ();
+       hide_verbose_canvas_cursor ();
+
+       if (drag_info.item) {
+               drag_info.item->ungrab (0);
+
+               /* put it back where it came from */
+
+               double cxw, cyw;
+               cxw = 0;
+               cyw = 0;
+               drag_info.item->i2w (cxw, cyw);
+               drag_info.item->move (drag_info.original_x - cxw, drag_info.original_y - cyw);
+       }
+
+       finalize_drag ();
+}
+
+void
+Editor::finalize_drag ()
+{
+       drag_info.item = 0;
+       drag_info.copy = false;
+       drag_info.motion_callback = 0;
+       drag_info.finished_callback = 0;
+       drag_info.dest_trackview = 0;
+       drag_info.source_trackview = 0;
+       drag_info.last_frame_position = 0;
+       drag_info.grab_frame = 0;
+       drag_info.last_pointer_frame = 0;
+       drag_info.current_pointer_frame = 0;
+       drag_info.brushing = false;
+       range_marker_drag_rect->hide();
+       drag_info.clear_copied_locations ();
+}
+
 void
 Editor::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
 {
@@ -1542,13 +1897,13 @@ Editor::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
        }
 
        if (cursor == 0) {
-               cursor = grabber_cursor;
+               cursor = which_grabber_cursor ();
        }
 
-        // if dragging with button2, the motion is x constrained, with Alt-button2 it is y constrained
+       // if dragging with button2, the motion is x constrained, with Alt-button2 it is y constrained
 
        if (event->button.button == 2) {
-               if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Alt)) {
+               if (Keyboard::modifier_state_equals (event->button.state, Keyboard::SecondaryModifier)) {
                        drag_info.y_constrained = true;
                        drag_info.x_constrained = false;
                } else {
@@ -1560,11 +1915,13 @@ Editor::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
                drag_info.y_constrained = false;
        }
 
-       drag_info.grab_frame = event_frame(event, &drag_info.grab_x, &drag_info.grab_y);
+       drag_info.grab_frame = event_frame (event, &drag_info.grab_x, &drag_info.grab_y);
        drag_info.last_pointer_frame = drag_info.grab_frame;
        drag_info.current_pointer_frame = drag_info.grab_frame;
        drag_info.current_pointer_x = drag_info.grab_x;
        drag_info.current_pointer_y = drag_info.grab_y;
+       drag_info.last_pointer_x = drag_info.current_pointer_x;
+       drag_info.last_pointer_y = drag_info.current_pointer_y;
        drag_info.cumulative_x_drag = 0;
        drag_info.cumulative_y_drag = 0;
        drag_info.first_move = true;
@@ -1572,7 +1929,11 @@ Editor::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
        drag_info.want_move_threshold = false;
        drag_info.pointer_frame_offset = 0;
        drag_info.brushing = false;
-       drag_info.copied_location = 0;
+       drag_info.clear_copied_locations ();
+
+       drag_info.original_x = 0;
+       drag_info.original_y = 0;
+       drag_info.item->i2w (drag_info.original_x, drag_info.original_y);
 
        drag_info.item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK,
                              *cursor,
@@ -1603,7 +1964,7 @@ Editor::swap_grab (ArdourCanvas::Item* new_item, Gdk::Cursor* cursor, uint32_t t
        drag_info.item = new_item;
 
        if (cursor == 0) {
-               cursor = grabber_cursor;
+               cursor = which_grabber_cursor ();
        }
 
        drag_info.item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK, *cursor, time);
@@ -1623,6 +1984,8 @@ Editor::end_grab (ArdourCanvas::Item* item, GdkEvent* event)
        drag_info.item->ungrab (event->button.time);
 
        if (drag_info.finished_callback) {
+               drag_info.last_pointer_x = drag_info.current_pointer_x;
+               drag_info.last_pointer_y = drag_info.current_pointer_y;
                (this->*(drag_info.finished_callback)) (item, event);
        }
 
@@ -1630,54 +1993,11 @@ Editor::end_grab (ArdourCanvas::Item* item, GdkEvent* event)
 
        hide_verbose_canvas_cursor();
 
-       drag_info.item = 0;
-       drag_info.copy = false;
-       drag_info.motion_callback = 0;
-       drag_info.finished_callback = 0;
-       drag_info.last_trackview = 0;
-       drag_info.last_frame_position = 0;
-       drag_info.grab_frame = 0;
-       drag_info.last_pointer_frame = 0;
-       drag_info.current_pointer_frame = 0;
-       drag_info.brushing = false;
-
-       if (drag_info.copied_location) {
-               delete drag_info.copied_location;
-               drag_info.copied_location = 0;
-       }
+       finalize_drag ();
 
        return did_drag;
 }
 
-void
-Editor::set_edit_cursor (GdkEvent* event)
-{
-       nframes_t pointer_frame = event_frame (event);
-
-       if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
-               if (snap_type != SnapToEditCursor) {
-                       snap_to (pointer_frame);
-               }
-       }
-
-       edit_cursor->set_position (pointer_frame);
-       edit_cursor_clock.set (pointer_frame);
-}
-
-void
-Editor::set_playhead_cursor (GdkEvent* event)
-{
-       nframes_t pointer_frame = event_frame (event);
-
-       if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
-               snap_to (pointer_frame);
-       }
-
-       if (session) {
-               session->request_locate (pointer_frame, session->transport_rolling());
-       }
-}
-
 void
 Editor::start_fade_in_grab (ArdourCanvas::Item* item, GdkEvent* event)
 {
@@ -1694,17 +2014,18 @@ Editor::start_fade_in_grab (ArdourCanvas::Item* item, GdkEvent* event)
 
        AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
 
-       drag_info.pointer_frame_offset = drag_info.grab_frame - ((nframes_t) arv->audio_region()->fade_in().back()->when + arv->region()->position());  
+
+       drag_info.pointer_frame_offset = drag_info.grab_frame - ((nframes64_t) arv->audio_region()->fade_in()->back()->when + arv->region()->position());       
 }
 
 void
 Editor::fade_in_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
 {
        AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
-       nframes_t pos;
-       nframes_t fade_length;
+       nframes64_t pos;
+       nframes64_t fade_length;
 
-       if ((int32_t)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
+       if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
                pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
        }
        else {
@@ -1744,12 +2065,12 @@ void
 Editor::fade_in_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
 {
        AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
-       nframes_t pos;
-       nframes_t fade_length;
+       nframes64_t pos;
+       nframes64_t fade_length;
 
        if (drag_info.first_move) return;
 
-       if ((int32_t)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
+       if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
                pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
        } else {
                pos = 0;
@@ -1773,13 +2094,14 @@ Editor::fade_in_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* even
                        continue;
                }
        
-               AutomationList& alist = tmp->audio_region()->fade_in();
-               XMLNode &before = alist.get_state();
+               boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
+               XMLNode &before = alist->get_state();
 
                tmp->audio_region()->set_fade_in_length (fade_length);
+               tmp->audio_region()->set_fade_in_active (true);
                
-               XMLNode &after = alist.get_state();
-               session->add_command(new MementoCommand<AutomationList>(alist, &before, &after));
+               XMLNode &after = alist->get_state();
+               session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
        }
 
        commit_reversible_command ();
@@ -1801,20 +2123,19 @@ Editor::start_fade_out_grab (ArdourCanvas::Item* item, GdkEvent* event)
 
        AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
 
-       drag_info.pointer_frame_offset = drag_info.grab_frame - (arv->region()->length() - (nframes_t) arv->audio_region()->fade_out().back()->when + arv->region()->position());       
+       drag_info.pointer_frame_offset = drag_info.grab_frame - (arv->region()->length() - (nframes64_t) arv->audio_region()->fade_out()->back()->when + arv->region()->position());    
 }
 
 void
 Editor::fade_out_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
 {
        AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
-       nframes_t pos;
-       nframes_t fade_length;
+       nframes64_t pos;
+       nframes64_t fade_length;
 
-       if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
+       if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
                pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
-       }
-       else {
+       } else {
                pos = 0;
        }
 
@@ -1856,10 +2177,10 @@ Editor::fade_out_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* eve
        if (drag_info.first_move) return;
 
        AudioRegionView* arv = static_cast<AudioRegionView*>(drag_info.data);
-       nframes_t pos;
-       nframes_t fade_length;
+       nframes64_t pos;
+       nframes64_t fade_length;
 
-       if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
+       if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
                pos = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
        }
        else {
@@ -1890,13 +2211,14 @@ Editor::fade_out_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* eve
                        continue;
                }
        
-               AutomationList& alist = tmp->audio_region()->fade_out();
-               XMLNode &before = alist.get_state();
+               boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
+               XMLNode &before = alist->get_state();
                
                tmp->audio_region()->set_fade_out_length (fade_length);
+               tmp->audio_region()->set_fade_out_active (true);
 
-               XMLNode &after = alist.get_state();
-               session->add_command(new MementoCommand<AutomationList>(alist, &before, &after));
+               XMLNode &after = alist->get_state();
+               session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
        }
 
        commit_reversible_command ();
@@ -1939,9 +2261,9 @@ void
 Editor::cursor_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
 {
        Cursor* cursor = (Cursor *) drag_info.data;
-       nframes_t adjusted_frame;
+       nframes64_t adjusted_frame;
        
-       if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
+       if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
                adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
        }
        else {
@@ -1949,7 +2271,7 @@ Editor::cursor_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
        }
        
        if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
-               if (cursor != edit_cursor || snap_type != SnapToEditCursor) {
+               if (cursor == playhead_cursor) {
                        snap_to (adjusted_frame);
                }
        }
@@ -1957,15 +2279,14 @@ Editor::cursor_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
        if (adjusted_frame == drag_info.last_pointer_frame) return;
 
        cursor->set_position (adjusted_frame);
-       
-       if (cursor == edit_cursor) {
-               edit_cursor_clock.set (cursor->current_frame);
-       } else {
-               UpdateAllTransportClocks (cursor->current_frame);
-       }
 
        show_verbose_time_cursor (cursor->current_frame, 10);
 
+#ifdef GTKOSX
+       track_canvas->update_now ();
+#endif
+       UpdateAllTransportClocks (cursor->current_frame);
+
        drag_info.last_pointer_frame = adjusted_frame;
        drag_info.first_move = false;
 }
@@ -1983,9 +2304,6 @@ Editor::cursor_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event
                if (session) {
                        session->request_locate (playhead_cursor->current_frame, drag_info.was_rolling);
                }
-       } else if (item == &edit_cursor->canvas_item) {
-               edit_cursor->set_position (edit_cursor->current_frame);
-               edit_cursor_clock.set (edit_cursor->current_frame);
        } 
 }
 
@@ -1996,16 +2314,16 @@ Editor::update_marker_drag_item (Location *location)
        double x2 = frame_to_pixel (location->end());
 
        if (location->is_mark()) {
-               marker_drag_line_points.front().set_x(x1);
+               marker_drag_line_points.front().set_x(x1);
                marker_drag_line_points.back().set_x(x1);
                marker_drag_line->property_points() = marker_drag_line_points;
-       }
-       else {
+       } else {
                range_marker_drag_rect->property_x1() = x1;
                range_marker_drag_rect->property_x2() = x2;
        }
 }
 
+
 void
 Editor::start_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
 {
@@ -2027,44 +2345,101 @@ Editor::start_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
 
        start_grab (event);
 
-       drag_info.copied_location = new Location (*location);
+       _dragging_edit_point = true;
+
        drag_info.pointer_frame_offset = drag_info.grab_frame - (is_start ? location->start() : location->end());       
 
        update_marker_drag_item (location);
 
        if (location->is_mark()) {
-               marker_drag_line->show();
-               marker_drag_line->raise_to_top();
-       }
-       else {
+               // marker_drag_line->show();
+               // marker_drag_line->raise_to_top();
+       } else {
                range_marker_drag_rect->show();
-               range_marker_drag_rect->raise_to_top();
+               //range_marker_drag_rect->raise_to_top();
+       }
+
+       if (is_start) {
+               show_verbose_time_cursor (location->start(), 10);
+       } else {
+               show_verbose_time_cursor (location->end(), 10);
+       }
+
+       Selection::Operation op = Keyboard::selection_type (event->button.state);
+
+       switch (op) {
+       case Selection::Toggle:
+               selection->toggle (marker);
+               break;
+       case Selection::Set:
+               if (!selection->selected (marker)) {
+                       selection->set (marker);
+               }
+               break;
+       case Selection::Extend:
+       {
+               Locations::LocationList ll;
+               list<Marker*> to_add;
+               nframes64_t s, e;
+               selection->markers.range (s, e);
+               s = min (marker->position(), s);
+               e = max (marker->position(), e);
+               s = min (s, e);
+               e = max (s, e);
+               if (e < max_frames) {
+                       ++e;
+               }
+               session->locations()->find_all_between (s, e, ll, Location::Flags (0));
+               for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) {
+                       LocationMarkers* lm = find_location_markers (*i);
+                       if (lm) {
+                               if (lm->start) {
+                                       to_add.push_back (lm->start);
+                               }
+                               if (lm->end) {
+                                       to_add.push_back (lm->end);
+                               }
+                       }
+               }
+               if (!to_add.empty()) {
+                       selection->add (to_add);
+               }
+               break;
+       }
+       case Selection::Add:
+               selection->add (marker);
+               break;
+       }
+
+       /* set up copies for us to manipulate during the drag */
+
+       drag_info.clear_copied_locations ();
+
+       for (MarkerSelection::iterator i = selection->markers.begin(); i != selection->markers.end(); ++i) {
+               Location  *l = find_location_from_marker (*i, is_start);
+               drag_info.copied_locations.push_back (new Location (*l));
        }
-       
-       if (is_start) show_verbose_time_cursor (location->start(), 10);
-       else show_verbose_time_cursor (location->end(), 10);
 }
 
 void
 Editor::marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
 {
-       nframes_t f_delta;      
-       Marker* marker = (Marker *) drag_info.data;
-       Location  *real_location;
-       Location  *copy_location;
+       nframes64_t f_delta = 0;
+       nframes64_t newframe;
        bool is_start;
        bool move_both = false;
+       Marker* dragged_marker = (Marker*) drag_info.data;
+       Marker* marker;
+       Location  *real_location;
+       Location  *copy_location;
 
-
-       nframes_t newframe;
-       if (drag_info.pointer_frame_offset <= (long) drag_info.current_pointer_frame) {
+       if (drag_info.pointer_frame_offset <= drag_info.current_pointer_frame) {
                newframe = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
-       }
-       else {
+       } else {
                newframe = 0;
        }
 
-       nframes_t next = newframe;
+       nframes64_t next = newframe;
 
        if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
                snap_to (newframe, 0, true);
@@ -2074,89 +2449,197 @@ Editor::marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
                return;
        }
 
-       /* call this to find out if its the start or end */
-       
-       real_location = find_location_from_marker (marker, is_start);
+       if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
+               move_both = true;
+       }
 
-       /* use the copy that we're "dragging" around */
-       
-       copy_location = drag_info.copied_location;
+       MarkerSelection::iterator i;
+       list<Location*>::iterator x;
 
-       f_delta = copy_location->end() - copy_location->start();
-       
-       if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
-               move_both = true;
+       /* find the marker we're dragging, and compute the delta */
+
+       for (i = selection->markers.begin(), x = drag_info.copied_locations.begin(); 
+            x != drag_info.copied_locations.end() && i != selection->markers.end(); 
+            ++i, ++x) {
+
+               copy_location = *x;
+               marker = *i;
+
+               if (marker == dragged_marker) {
+
+                       if ((real_location = find_location_from_marker (marker, is_start)) == 0) {
+                               /* que pasa ?? */
+                               return;
+                       }
+
+                       if (real_location->is_mark()) {
+                               f_delta = newframe - copy_location->start();
+                       } else {
+
+
+                               switch (marker->type()) {
+                               case Marker::Start:
+                               case Marker::LoopStart:
+                               case Marker::PunchIn:
+                                       f_delta = newframe - copy_location->start();
+                                       break;
+
+                               case Marker::End:
+                               case Marker::LoopEnd:
+                               case Marker::PunchOut:
+                                       f_delta = newframe - copy_location->end();
+                                       break;
+                               default:
+                                       /* what kind of marker is this ? */
+                                       return;
+                               }
+                       }
+                       break;
+               }
        }
 
-       if (copy_location->is_mark()) {
-               /* just move it */
+       if (i == selection->markers.end()) {
+               /* hmm, impossible - we didn't find the dragged marker */
+               return;
+       }
 
-               copy_location->set_start (newframe);
+       /* now move them all */
 
-       } else {
+       for (i = selection->markers.begin(), x = drag_info.copied_locations.begin(); 
+            x != drag_info.copied_locations.end() && i != selection->markers.end(); 
+            ++i, ++x) {
 
-               if (is_start) { // start-of-range marker
+               copy_location = *x;
+               marker = *i;
+
+               /* call this to find out if its the start or end */
+               
+               if ((real_location = find_location_from_marker (marker, is_start)) == 0) {
+                       continue;
+               }
+               
+               if (real_location->locked()) {
+                       continue;
+               }
+
+               if (copy_location->is_mark()) {
+
+                       /* just move it */
                        
-                       if (move_both) {
-                               copy_location->set_start (newframe);
-                               copy_location->set_end (newframe + f_delta);
-                       } else  if (newframe < copy_location->end()) {
-                               copy_location->set_start (newframe);
-                       } else { 
-                               snap_to (next, 1, true);
-                               copy_location->set_end (next);
-                               copy_location->set_start (newframe);
-                       }
+                       copy_location->set_start (copy_location->start() + f_delta);
+
+               } else {
                        
-               } else { // end marker
+                       nframes64_t new_start = copy_location->start() + f_delta;
+                       nframes64_t new_end = copy_location->end() + f_delta;
                        
-                       if (move_both) {
-                               copy_location->set_end (newframe);
-                               copy_location->set_start (newframe - f_delta);
-                       } else if (newframe > copy_location->start()) {
-                               copy_location->set_end (newframe);
+                       if (is_start) { // start-of-range marker
+                               
+                               if (move_both) {
+                                       copy_location->set_start (new_start);
+                                       copy_location->set_end (new_end);
+                               } else  if (new_start < copy_location->end()) {
+                                       copy_location->set_start (new_start);
+                               } else { 
+                                       snap_to (next, 1, true);
+                                       copy_location->set_end (next);
+                                       copy_location->set_start (newframe);
+                               }
                                
-                       } else if (newframe > 0) {
-                               snap_to (next, -1, true);
-                               copy_location->set_start (next);
-                               copy_location->set_end (newframe);
+                       } else { // end marker
+                               
+                               if (move_both) {
+                                       copy_location->set_end (new_end);
+                                       copy_location->set_start (new_start);
+                               } else if (new_end > copy_location->start()) {
+                                       copy_location->set_end (new_end);
+                               } else if (newframe > 0) {
+                                       snap_to (next, -1, true);
+                                       copy_location->set_start (next);
+                                       copy_location->set_end (newframe);
+                               }
                        }
                }
+               update_marker_drag_item (copy_location);
+
+               LocationMarkers* lm = find_location_markers (real_location);
+
+               if (lm) {
+                       lm->set_position (copy_location->start(), copy_location->end());
+               }
        }
 
        drag_info.last_pointer_frame = drag_info.current_pointer_frame;
        drag_info.first_move = false;
 
-       update_marker_drag_item (copy_location);
+       if (drag_info.copied_locations.empty()) {
+               abort();
+       }
 
-       LocationMarkers* lm = find_location_markers (real_location);
-       lm->set_position (copy_location->start(), copy_location->end());
-       
+       edit_point_clock.set (drag_info.copied_locations.front()->start());
        show_verbose_time_cursor (newframe, 10);
+
+#ifdef GTKOSX
+       track_canvas->update_now ();
+#endif
+       edit_point_clock.set (copy_location->start());
 }
 
 void
 Editor::marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
 {
        if (drag_info.first_move) {
-               marker_drag_motion_callback (item, event);
 
+               /* just a click, do nothing but finish
+                  off the selection process
+               */
+
+               Selection::Operation op = Keyboard::selection_type (event->button.state);
+               Marker* marker = (Marker *) drag_info.data;
+
+               switch (op) {
+               case Selection::Set:
+                       if (selection->selected (marker) && selection->markers.size() > 1) {
+                               selection->set (marker);
+                       }
+                       break;
+
+               case Selection::Toggle:
+               case Selection::Extend:
+               case Selection::Add:
+                       break;
+               }
+               
+               return;
        }
-       
-       Marker* marker = (Marker *) drag_info.data;
-       bool is_start;
 
+       _dragging_edit_point = false;
+       
 
        begin_reversible_command ( _("move marker") );
        XMLNode &before = session->locations()->get_state();
+
+       MarkerSelection::iterator i;
+       list<Location*>::iterator x;
+       bool is_start;
+
+       for (i = selection->markers.begin(), x = drag_info.copied_locations.begin(); 
+            x != drag_info.copied_locations.end() && i != selection->markers.end(); 
+            ++i, ++x) {
        
-       Location * location = find_location_from_marker (marker, is_start);
-       
-       if (location) {
-               if (location->is_mark()) {
-                       location->set_start (drag_info.copied_location->start());
-               } else {
-                       location->set (drag_info.copied_location->start(), drag_info.copied_location->end());
+               Location * location = find_location_from_marker ((*i), is_start);
+               
+               if (location) {
+                       
+                       if (location->locked()) {
+                               return;
+                       }
+                       
+                       if (location->is_mark()) {
+                               location->set_start ((*x)->start());
+                       } else {
+                               location->set ((*x)->start(), (*x)->end());
+                       }
                }
        }
 
@@ -2188,6 +2671,7 @@ Editor::start_meter_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
        }
 
        drag_info.item = item;
+       drag_info.copy = false;
        drag_info.data = marker;
        drag_info.motion_callback = &Editor::meter_marker_drag_motion_callback;
        drag_info.finished_callback = &Editor::meter_marker_drag_finished_callback;
@@ -2216,7 +2700,7 @@ Editor::start_meter_marker_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
        // The actual copying is not done before we reach the finish callback.
        char name[64];
        snprintf (name, sizeof(name), "%g/%g", meter_marker->meter().beats_per_bar(), meter_marker->meter().note_divisor ());
-       MeterMarker* new_marker = new MeterMarker(*this, *meter_group, color_map[cMeterMarker], name, 
+       MeterMarker* new_marker = new MeterMarker(*this, *meter_group, ARDOUR_UI::config()->canvasvar_MeterMarker.get(), name, 
                                                  *new MeterSection(meter_marker->meter()));
 
        drag_info.item = &new_marker->the_item();
@@ -2236,9 +2720,9 @@ void
 Editor::meter_marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
 {
        MeterMarker* marker = (MeterMarker *) drag_info.data;
-       nframes_t adjusted_frame;
+       nframes64_t adjusted_frame;
 
-       if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
+       if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
                adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
        }
        else {
@@ -2275,12 +2759,12 @@ Editor::meter_marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent*
        
        if (drag_info.copy == true) {
                begin_reversible_command (_("copy meter mark"));
-                XMLNode &before = map.get_state();
+               XMLNode &before = map.get_state();
                map.add_meter (marker->meter(), when);
                XMLNode &after = map.get_state();
-                session->add_command(new MementoCommand<TempoMap>(map, &before, &after));
+               session->add_command(new MementoCommand<TempoMap>(map, &before, &after));
                commit_reversible_command ();
-               
+
                // delete the dummy marker we used for visual representation of copying.
                // a new visual marker will show up automatically.
                delete marker;
@@ -2289,7 +2773,7 @@ Editor::meter_marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent*
                XMLNode &before = map.get_state();
                map.move_meter (marker->meter(), when);
                XMLNode &after = map.get_state();
-                session->add_command(new MementoCommand<TempoMap>(map, &before, &after));
+               session->add_command(new MementoCommand<TempoMap>(map, &before, &after));
                commit_reversible_command ();
        }
 }
@@ -2317,6 +2801,7 @@ Editor::start_tempo_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
        }
 
        drag_info.item = item;
+       drag_info.copy = false;
        drag_info.data = marker;
        drag_info.motion_callback = &Editor::tempo_marker_drag_motion_callback;
        drag_info.finished_callback = &Editor::tempo_marker_drag_finished_callback;
@@ -2347,7 +2832,7 @@ Editor::start_tempo_marker_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
        // The actual copying is not done before we reach the finish callback.
        char name[64];
        snprintf (name, sizeof (name), "%.2f", tempo_marker->tempo().beats_per_minute());
-       TempoMarker* new_marker = new TempoMarker(*this, *tempo_group, color_map[cTempoMarker], name, 
+       TempoMarker* new_marker = new TempoMarker(*this, *tempo_group, ARDOUR_UI::config()->canvasvar_TempoMarker.get(), name, 
                                                  *new TempoSection(tempo_marker->tempo()));
 
        drag_info.item = &new_marker->the_item();
@@ -2367,9 +2852,9 @@ void
 Editor::tempo_marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
 {
        TempoMarker* marker = (TempoMarker *) drag_info.data;
-       nframes_t adjusted_frame;
+       nframes64_t adjusted_frame;
        
-       if ((long)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
+       if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
                adjusted_frame = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
        }
        else {
@@ -2418,10 +2903,10 @@ Editor::tempo_marker_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent*
                delete marker;
        } else {
                begin_reversible_command (_("move tempo mark"));
-                XMLNode &before = map.get_state();
+               XMLNode &before = map.get_state();
                map.move_tempo (marker->tempo(), when);
-                XMLNode &after = map.get_state();
-                session->add_command (new MementoCommand<TempoMap>(map, &before, &after));
+               XMLNode &after = map.get_state();
+               session->add_command (new MementoCommand<TempoMap>(map, &before, &after));
                commit_reversible_command ();
        }
 }
@@ -2437,16 +2922,16 @@ Editor::remove_gain_control_point (ArdourCanvas::Item*item, GdkEvent* event)
        }
 
        // We shouldn't remove the first or last gain point
-       if (control_point->line.is_last_point(*control_point) ||
-               control_point->line.is_first_point(*control_point)) {   
+       if (control_point->line().is_last_point(*control_point) ||
+               control_point->line().is_first_point(*control_point)) { 
                return;
        }
 
-       control_point->line.remove_point (*control_point);
+       control_point->line().remove_point (*control_point);
 }
 
 void
-Editor::remove_control_point (ArdourCanvas::Item*item, GdkEvent* event)
+Editor::remove_control_point (ArdourCanvas::Item* item, GdkEvent* event)
 {
        ControlPoint* control_point;
 
@@ -2455,14 +2940,14 @@ Editor::remove_control_point (ArdourCanvas::Item*item, GdkEvent* event)
                /*NOTREACHED*/
        }
 
-       control_point->line.remove_point (*control_point);
+       control_point->line().remove_point (*control_point);
 }
 
 void
 Editor::start_control_point_grab (ArdourCanvas::Item* item, GdkEvent* event)
 {
        ControlPoint* control_point;
-       
+
        if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
                fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
                /*NOTREACHED*/
@@ -2475,11 +2960,21 @@ Editor::start_control_point_grab (ArdourCanvas::Item* item, GdkEvent* event)
 
        start_grab (event, fader_cursor);
 
-       control_point->line.start_drag (control_point, drag_info.grab_frame, 0);
+       // start the grab at the center of the control point so
+       // the point doesn't 'jump' to the mouse after the first drag
+       drag_info.grab_x = control_point->get_x();
+       drag_info.grab_y = control_point->get_y();
 
-       float fraction = 1.0 - (control_point->get_y() / control_point->line.height());
-       set_verbose_canvas_cursor (control_point->line.get_verbose_cursor_string (fraction), 
-                                  drag_info.current_pointer_x + 20, drag_info.current_pointer_y + 20);
+       control_point->line().parent_group().i2w(drag_info.grab_x, drag_info.grab_y);
+       track_canvas->w2c(drag_info.grab_x, drag_info.grab_y, drag_info.grab_x, drag_info.grab_y);
+
+       drag_info.grab_frame = pixel_to_frame(drag_info.grab_x);
+
+       control_point->line().start_drag (control_point, drag_info.grab_frame, 0);
+
+       float fraction = 1.0 - (control_point->get_y() / control_point->line().height());
+       set_verbose_canvas_cursor (control_point->line().get_verbose_cursor_string (fraction), 
+                                  drag_info.current_pointer_x + 10, drag_info.current_pointer_y + 10);
 
        show_verbose_canvas_cursor ();
 }
@@ -2489,11 +2984,28 @@ Editor::control_point_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent*
 {
        ControlPoint* cp = reinterpret_cast<ControlPoint *> (drag_info.data);
 
-       double cx = drag_info.current_pointer_x;
-       double cy = drag_info.current_pointer_y;
+       double dx = drag_info.current_pointer_x - drag_info.last_pointer_x;
+       double dy = drag_info.current_pointer_y - drag_info.last_pointer_y;
 
-       drag_info.cumulative_x_drag = cx - drag_info.grab_x ;
-       drag_info.cumulative_y_drag = cy - drag_info.grab_y ;
+       if (event->button.state & Keyboard::SecondaryModifier) {
+               dx *= 0.1;
+               dy *= 0.1;
+       }
+
+       double cx = drag_info.grab_x + drag_info.cumulative_x_drag + dx;
+       double cy = drag_info.grab_y + drag_info.cumulative_y_drag + dy;
+
+       // 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) * cp->line().height() - .01;
+       cp->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)
+                       or (cy > zero_gain_y and (cy - dy) < zero_gain_y)) {
+               cy = zero_gain_y;
+       }
 
        if (drag_info.x_constrained) {
                cx = drag_info.grab_x;
@@ -2502,32 +3014,35 @@ Editor::control_point_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent*
                cy = drag_info.grab_y;
        }
 
-       cp->line.parent_group().w2i (cx, cy);
+       drag_info.cumulative_x_drag = cx - drag_info.grab_x;
+       drag_info.cumulative_y_drag = cy - drag_info.grab_y;
+
+       cp->line().parent_group().w2i (cx, cy);
 
        cx = max (0.0, cx);
        cy = max (0.0, cy);
-       cy = min ((double) cp->line.height(), cy);
+       cy = min ((double) cp->line().height(), cy);
 
        //translate cx to frames
-       nframes_t cx_frames = unit_to_frame (cx);
+       nframes64_t cx_frames = unit_to_frame (cx);
 
        if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier()) && !drag_info.x_constrained) {
                snap_to (cx_frames);
        }
 
-       float fraction = 1.0 - (cy / cp->line.height());
-       
+       float fraction = 1.0 - (cy / cp->line().height());
+
        bool push;
 
-       if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
+       if (Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier)) {
                push = true;
        } else {
                push = false;
        }
 
-       cp->line.point_drag (*cp, cx_frames , fraction, push);
+       cp->line().point_drag (*cp, cx_frames , fraction, push);
        
-       set_verbose_canvas_cursor_text (cp->line.get_verbose_cursor_string (fraction));
+       set_verbose_canvas_cursor_text (cp->line().get_verbose_cursor_string (fraction));
 
        drag_info.first_move = false;
 }
@@ -2541,14 +3056,14 @@ Editor::control_point_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent
 
                /* just a click */
                
-               if ((event->type == GDK_BUTTON_RELEASE) && (event->button.button == 1) && Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
+               if ((event->type == GDK_BUTTON_RELEASE) && (event->button.button == 1) && Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
                        reset_point_selection ();
                }
 
        } else {
                control_point_drag_motion_callback (item, event);
        }
-       cp->line.end_drag (cp);
+       cp->line().end_drag (cp);
 }
 
 void
@@ -2582,7 +3097,7 @@ Editor::start_line_grab (AutomationLine* line, GdkEvent* event)
 {
        double cx;
        double cy;
-       nframes_t frame_within_region;
+       nframes64_t frame_within_region;
 
        /* need to get x coordinate in terms of parent (TimeAxisItemView)
           origin.
@@ -2591,7 +3106,7 @@ Editor::start_line_grab (AutomationLine* line, GdkEvent* event)
        cx = event->button.x;
        cy = event->button.y;
        line->parent_group().w2i (cx, cy);
-       frame_within_region = (nframes_t) floor (cx * frames_per_unit);
+       frame_within_region = (nframes64_t) floor (cx * frames_per_unit);
 
        if (!line->control_points_adjacent (frame_within_region, current_line_drag_info.before, 
                                            current_line_drag_info.after)) {
@@ -2611,7 +3126,7 @@ Editor::start_line_grab (AutomationLine* line, GdkEvent* event)
        line->start_drag (0, drag_info.grab_frame, fraction);
        
        set_verbose_canvas_cursor (line->get_verbose_cursor_string (fraction),
-                                  drag_info.current_pointer_x + 20, drag_info.current_pointer_y + 20);
+                                  drag_info.current_pointer_x + 10, drag_info.current_pointer_y + 10);
        show_verbose_canvas_cursor ();
 }
 
@@ -2619,17 +3134,37 @@ void
 Editor::line_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
 {
        AutomationLine* line = reinterpret_cast<AutomationLine *> (drag_info.data);
-       double cx = drag_info.current_pointer_x;
-       double cy = drag_info.current_pointer_y;
 
-       line->parent_group().w2i (cx, cy);
-       
-       double fraction;
-       fraction = 1.0 - (cy / line->height());
+       double dy = drag_info.current_pointer_y - drag_info.last_pointer_y;
+
+       if (event->button.state & Keyboard::SecondaryModifier) {
+               dy *= 0.1;
+       }
+
+       double cy = drag_info.grab_y + drag_info.cumulative_y_drag + dy;
+
+       // 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);
+
+       // make sure we hit zero when passing through
+       if ((cy < zero_gain_y and (cy - dy) > zero_gain_y)
+                       or (cy > zero_gain_y and (cy - dy) < zero_gain_y)) {
+               cy = zero_gain_y;
+       }
+
+       drag_info.cumulative_y_drag = cy - drag_info.grab_y;
+
+       cy = max (0.0, cy);
+       cy = min ((double) line->height(), cy);
+
+       double fraction = 1.0 - (cy / line->height());
 
        bool push;
 
-       if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
+       if (Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier)) {
                push = false;
        } else {
                push = true;
@@ -2654,32 +3189,55 @@ Editor::start_region_grab (ArdourCanvas::Item* item, GdkEvent* event)
        if (selection->regions.empty() || clicked_regionview == 0) {
                return;
        }
-
+       _region_motion_group->raise_to_top ();
        drag_info.copy = false;
        drag_info.item = item;
        drag_info.data = clicked_regionview;
-       drag_info.motion_callback = &Editor::region_drag_motion_callback;
-       drag_info.finished_callback = &Editor::region_drag_finished_callback;
+
+       if (Config->get_edit_mode() == Splice) {
+               drag_info.motion_callback = &Editor::region_drag_splice_motion_callback;
+               drag_info.finished_callback = &Editor::region_drag_splice_finished_callback;
+       } else {
+               drag_info.motion_callback = &Editor::region_drag_motion_callback;
+               drag_info.finished_callback = &Editor::region_drag_finished_callback;
+       }
 
        start_grab (event);
 
        double speed = 1.0;
-       TimeAxisView* tvp = clicked_trackview;
+       TimeAxisView* tvp = clicked_axisview;
        RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
 
-       if (tv && tv->is_audio_track()) {
+       if (tv && tv->is_track()) {
                speed = tv->get_diskstream()->speed();
        }
        
-       drag_info.last_frame_position = (nframes_t) (clicked_regionview->region()->position() / speed);
+       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.last_trackview = &clicked_regionview->get_time_axis_view();
+       drag_info.source_trackview = &clicked_regionview->get_time_axis_view();
+       drag_info.dest_trackview = drag_info.source_trackview;
        // we want a move threshold
        drag_info.want_move_threshold = true;
-       
        show_verbose_time_cursor (drag_info.last_frame_position, 10);
 
        begin_reversible_command (_("move region(s)"));
+
+       /* sync the canvas to what we think is its current state */
+       track_canvas->update_now();
+}
+
+void
+Editor::start_create_region_grab (ArdourCanvas::Item* item, GdkEvent* event)
+{
+       drag_info.copy = false;
+       drag_info.item = item;
+       drag_info.data = clicked_axisview;
+       drag_info.source_trackview = clicked_axisview;
+       drag_info.dest_trackview = drag_info.source_trackview;
+       drag_info.motion_callback = &Editor::create_region_drag_motion_callback;
+       drag_info.finished_callback = &Editor::create_region_drag_finished_callback;
+
+       start_grab (event);
 }
 
 void
@@ -2688,7 +3246,7 @@ Editor::start_region_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
        if (selection->regions.empty() || clicked_regionview == 0) {
                return;
        }
-
+       _region_motion_group->raise_to_top ();
        drag_info.copy = true;
        drag_info.item = item;
        drag_info.data = clicked_regionview;    
@@ -2696,15 +3254,16 @@ Editor::start_region_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
        start_grab(event);
 
        TimeAxisView* tv = &clicked_regionview->get_time_axis_view();
-       RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*>(tv);
+       RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
        double speed = 1.0;
 
-       if (atv && atv->is_audio_track()) {
-               speed = atv->get_diskstream()->speed();
+       if (rtv && rtv->is_track()) {
+               speed = rtv->get_diskstream()->speed();
        }
        
-       drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
-       drag_info.last_frame_position = (nframes_t) (clicked_regionview->region()->position() / speed);
+       drag_info.source_trackview = &clicked_regionview->get_time_axis_view();
+       drag_info.dest_trackview = drag_info.source_trackview;
+       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
        drag_info.want_move_threshold = true;
@@ -2716,7 +3275,7 @@ Editor::start_region_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
 void
 Editor::start_region_brush_grab (ArdourCanvas::Item* item, GdkEvent* event)
 {
-       if (selection->regions.empty() || clicked_regionview == 0) {
+       if (selection->regions.empty() || clicked_regionview == 0 || Config->get_edit_mode() == Splice) {
                return;
        }
 
@@ -2729,16 +3288,17 @@ Editor::start_region_brush_grab (ArdourCanvas::Item* item, GdkEvent* event)
        start_grab (event);
 
        double speed = 1.0;
-       TimeAxisView* tvp = clicked_trackview;
+       TimeAxisView* tvp = clicked_axisview;
        RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
 
-       if (tv && tv->is_audio_track()) {
+       if (tv && tv->is_track()) {
                speed = tv->get_diskstream()->speed();
        }
        
-       drag_info.last_frame_position = (nframes_t) (clicked_regionview->region()->position() / speed);
+       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.last_trackview = &clicked_regionview->get_time_axis_view();
+       drag_info.source_trackview = &clicked_regionview->get_time_axis_view();
+       drag_info.dest_trackview = drag_info.source_trackview;
        // we want a move threshold
        drag_info.want_move_threshold = true;
        drag_info.brushing = true;
@@ -2747,42 +3307,40 @@ Editor::start_region_brush_grab (ArdourCanvas::Item* item, GdkEvent* event)
 }
 
 void
-Editor::region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
+Editor::possibly_copy_regions_during_grab (GdkEvent* event)
 {
-       double x_delta;
-       double y_delta = 0;
-       RegionView* rv = reinterpret_cast<RegionView*> (drag_info.data); 
-       nframes_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
-       bool clamp_y_axis = false;
-       vector<int32_t>  height_list(512) ;
-       vector<int32_t>::iterator j;
-
        if (drag_info.copy && drag_info.move_threshold_passed && drag_info.want_move_threshold) {
 
                drag_info.want_move_threshold = false; // don't copy again
 
-               /* duplicate the region(s) */
-               
+               /* duplicate the regionview(s) and region(s) */
+
                vector<RegionView*> new_regionviews;
                
                for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
                        RegionView* rv;
                        RegionView* nrv;
-                       AudioRegionView* arv;
 
                        rv = (*i);
-
+                       AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
+                       MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(rv);
                        
-                       if ((arv = dynamic_cast<AudioRegionView*>(rv)) == 0) {
-                               /* XXX handle MIDI here */
-                               continue;
-                       }
+                       const boost::shared_ptr<const Region> original = rv->region();
+                       boost::shared_ptr<Region> region_copy = RegionFactory::create (original);
+
+                       if (arv) {
+                               boost::shared_ptr<AudioRegion> audioregion_copy
+                                       = boost::dynamic_pointer_cast<AudioRegion>(region_copy);
+                               nrv = new AudioRegionView (*arv, audioregion_copy);
+                       } else if (mrv) {
+                               boost::shared_ptr<MidiRegion> midiregion_copy
+                                       = boost::dynamic_pointer_cast<MidiRegion>(region_copy);
+                               nrv = new MidiRegionView (*mrv, midiregion_copy);
+                       } else {
+                               continue;
+                       }
 
-                       nrv = new AudioRegionView (*arv);
                        nrv->get_canvas_group()->show ();
-
                        new_regionviews.push_back (nrv);
                }
 
@@ -2790,38 +3348,150 @@ Editor::region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
                        return;
                }
 
-               /* reset selection to new regionviews */
-               
+               /* reset selection to new regionviews. This will not set selection visual status for 
+                  these regionviews since they don't belong to a track, so do that by hand too.
+                */
+
                selection->set (new_regionviews);
-               
+
+               for (vector<RegionView*>::iterator i = new_regionviews.begin(); i != new_regionviews.end(); ++i) {
+                       (*i)->set_selected (true);
+               }
+
                /* reset drag_info data to reflect the fact that we are dragging the copies */
                
                drag_info.data = new_regionviews.front();
-               
+
                swap_grab (new_regionviews.front()->get_canvas_group (), 0, event->motion.time);
+               /* 
+                  sync the canvas to what we think is its current state 
+                  without it, the canvas seems to 
+                  "forget" to update properly after the upcoming reparent() 
+                  ..only if the mouse is in rapid motion at the time of the grab. 
+                  something to do with regionview creation raking so long?
+                */
+               track_canvas->update_now();
        }
+}
 
+bool
+Editor::check_region_drag_possible (RouteTimeAxisView** tv)
+{
        /* Which trackview is this ? */
 
        TimeAxisView* tvp = trackview_by_y_position (drag_info.current_pointer_y);
-       AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
+       (*tv) = dynamic_cast<RouteTimeAxisView*>(tvp);
 
        /* The region motion is only processed if the pointer is over
           an audio track.
        */
        
-       if (!tv || !tv->is_audio_track()) {
+       if (!(*tv) || !(*tv)->is_track()) {
                /* To make sure we hide the verbose canvas cursor when the mouse is 
                   not held over and audiotrack. 
                */
                hide_verbose_canvas_cursor ();
-               return;
+               return false;
        }
+
+       return true;
+}
+
+struct RegionSelectionByPosition {
+    bool operator() (RegionView*a, RegionView* b) {
+           return a->region()->position () < b->region()->position();
+    }
+};
+
+void
+Editor::region_drag_splice_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
+{
+       RouteTimeAxisView* tv;
        
-       original_pointer_order = drag_info.last_trackview->order;
+       if (!check_region_drag_possible (&tv)) {
+               return;
+       }
+
+       if (!drag_info.move_threshold_passed) {
+               return;
+       }
+
+       int dir;
+
+       if (drag_info.current_pointer_x - drag_info.grab_x > 0) {
+               dir = 1;
+       } else {
+               dir = -1;
+       }
+
+       RegionSelection copy (selection->regions);
+
+       RegionSelectionByPosition cmp;
+       copy.sort (cmp);
+
+       for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
+
+               RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*> (&(*i)->get_time_axis_view());
+
+               if (!atv) {
+                       continue;
+               }
+
+               boost::shared_ptr<Playlist> playlist;
+
+               if ((playlist = atv->playlist()) == 0) {
+                       continue;
+               }
+
+               if (!playlist->region_is_shuffle_constrained ((*i)->region())) {
+                       continue;
+               } 
+
+               if (dir > 0) {
+                       if (drag_info.current_pointer_frame < (*i)->region()->last_frame() + 1) {
+                               continue;
+                       }
+               } else {
+                       if (drag_info.current_pointer_frame > (*i)->region()->first_frame()) {
+                               continue;
+                       }
+               }
+
                
+               playlist->shuffle ((*i)->region(), dir);
+
+               drag_info.grab_x = drag_info.current_pointer_x;
+       }
+}
+
+void
+Editor::region_drag_splice_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
+{
+}
+
+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
+       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)) {
+               return;
+       }
+
+       original_pointer_order = drag_info.dest_trackview->order;
+       
        /************************************************************
-                 Y-Delta Computation
+            Y-Delta Computation
        ************************************************************/   
 
        if (drag_info.brushing) {
@@ -2829,8 +3499,8 @@ Editor::region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
                pointer_y_span = 0;
                goto y_axis_done;
        }
-       
-       if ((pointer_y_span = (drag_info.last_trackview->order - tv->order)) != 0) {
+
+       if ((pointer_y_span = (drag_info.dest_trackview->order - tv->order)) != 0) {
 
                int32_t children = 0, numtracks = 0;
                // XXX hard coding track limit, oh my, so very very bad
@@ -2840,30 +3510,31 @@ Editor::region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
                for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
                        TimeAxisView *tracklist_timeview;
                        tracklist_timeview = (*i);
-                       AudioTimeAxisView* atv2 = dynamic_cast<AudioTimeAxisView*>(tracklist_timeview);
-                       list<TimeAxisView*> children_list;
+                       RouteTimeAxisView* rtv2 = dynamic_cast<RouteTimeAxisView*>(tracklist_timeview);
+                       TimeAxisView::Children children_list;
              
                        /* zeroes are audio tracks. ones are other types. */
              
-                       if (!atv2->hidden()) {
+                       if (!rtv2->hidden()) {
                                
-                               if (visible_y_high < atv2->order) {
-                                       visible_y_high = atv2->order;
+                               if (visible_y_high < rtv2->order) {
+                                       visible_y_high = rtv2->order;
                                }
-                               if (visible_y_low > atv2->order) {
-                                       visible_y_low = atv2->order;
+                               if (visible_y_low > rtv2->order) {
+                                       visible_y_low = rtv2->order;
                                }
                
-                               if (!atv2->is_audio_track()) {                            
-                                       tracks = tracks |= (0x01 << atv2->order);
+                               if (!rtv2->is_track()) {                                  
+                                       tracks = tracks |= (0x01 << rtv2->order);
                                }
        
-                               height_list[atv2->order] = (*i)->height;
+                               height_list[rtv2->order] = (*i)->current_height();
                                children = 1;
-                               if ((children_list = atv2->get_child_list()).size() > 0) {
-                                       for (list<TimeAxisView*>::iterator j = children_list.begin(); j != children_list.end(); ++j) { 
-                                               tracks = tracks |= (0x01 << (atv2->order + children));
-                                               height_list[atv2->order + children] =  (*j)->height;                
+
+                               if ((children_list = rtv2->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++;
                                                children++;     
                                        }
@@ -2874,16 +3545,16 @@ Editor::region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
                /* find the actual span according to the canvas */
 
                canvas_pointer_y_span = pointer_y_span;
-               if (drag_info.last_trackview->order >= tv->order) {
+               if (drag_info.dest_trackview->order >= tv->order) {
                        int32_t y;
-                       for (y = tv->order; y < drag_info.last_trackview->order; y++) {
+                       for (y = tv->order; y < drag_info.dest_trackview->order; y++) {
                                if (height_list[y] == 0 ) {
                                        canvas_pointer_y_span--;
                                }
                        }
                } else {
                        int32_t y;
-                       for (y = drag_info.last_trackview->order;y <= tv->order; y++) {
+                       for (y = drag_info.dest_trackview->order;y <= tv->order; y++) {
                                if (    height_list[y] == 0 ) {
                                        canvas_pointer_y_span++;
                                }
@@ -2895,30 +3566,36 @@ Editor::region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
                        double ix1, ix2, iy1, iy2;
                        int32_t n = 0;
 
+                       if (rv2->region()->locked()) {
+                               continue;
+                       }
+
                        rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
-                       rv2->get_canvas_group()->i2w (ix1, iy1);
+                       rv2->get_canvas_frame()->i2w (ix1, iy1);
+                       iy1 += vertical_adjustment.get_value() - canvas_timebars_vsize;
+
                        TimeAxisView* tvp2 = trackview_by_y_position (iy1);
-                       RouteTimeAxisView* atv2 = dynamic_cast<RouteTimeAxisView*>(tvp2);
+                       RouteTimeAxisView* rtv2 = dynamic_cast<RouteTimeAxisView*>(tvp2);
 
-                       if (atv2->order != original_pointer_order) {    
+                       if (rtv2->order != original_pointer_order) {    
                                /* this isn't the pointer track */      
 
                                if (canvas_pointer_y_span > 0) {
 
                                        /* moving up the canvas */
-                                       if ((atv2->order - canvas_pointer_y_span) >= visible_y_low) {
+                                       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[atv2->order - (visible_tracks - n)] == 0) {
+                                                       while (height_list[rtv2->order - (visible_tracks - n)] == 0) {
                                                                /* we're passing through a hidden track */
                                                                n--;
                                                        }                 
                                                }
                 
-                                               if (tracks[atv2->order - (canvas_pointer_y_span - n)] != 0x00) {                  
+                                               if (tracks[rtv2->order - (canvas_pointer_y_span - n)] != 0x00) {                  
                                                        clamp_y_axis = true;
                                                }
                    
@@ -2930,7 +3607,7 @@ Editor::region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
 
                                        /*moving down the canvas*/
 
-                                       if ((atv2->order - (canvas_pointer_y_span - n)) <= visible_y_high) { // we will overflow
+                                       if ((rtv2->order - (canvas_pointer_y_span - n)) <= visible_y_high) { // we will overflow
                    
                    
                                                int32_t visible_tracks = 0;
@@ -2938,11 +3615,11 @@ Editor::region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
                                                while (visible_tracks > canvas_pointer_y_span ) {
                                                        visible_tracks--;
                      
-                                                       while (height_list[atv2->order - (visible_tracks - n)] == 0) {             
+                                                       while (height_list[rtv2->order - (visible_tracks - n)] == 0) {             
                                                                n++;
                                                        }                
                                                }
-                                               if (  tracks[atv2->order - ( canvas_pointer_y_span - n)] != 0x00) {
+                                               if (  tracks[rtv2->order - ( canvas_pointer_y_span - n)] != 0x00) {
                                                        clamp_y_axis = true;
                            
                                                }
@@ -2955,9 +3632,9 @@ Editor::region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
                        } else {
                      
                                /* this is the pointer's track */
-                               if ((atv2->order - pointer_y_span) > visible_y_high) { // we will overflow 
+                               if ((rtv2->order - pointer_y_span) > visible_y_high) { // we will overflow 
                                        clamp_y_axis = true;
-                               } else if ((atv2->order - pointer_y_span) < visible_y_low) { // we will underflow
+                               } else if ((rtv2->order - pointer_y_span) < visible_y_low) { // we will underflow
                                        clamp_y_axis = true;
                                }
                        }             
@@ -2966,73 +3643,107 @@ Editor::region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
                        }
                }
 
-       } else  if (drag_info.last_trackview == tv) {
+       } else  if (drag_info.dest_trackview == tv) {
                clamp_y_axis = true;
        }         
 
   y_axis_done:
        if (!clamp_y_axis) {
-               drag_info.last_trackview = tv;        
+               drag_info.dest_trackview = tv;        
        }
          
        /************************************************************
-                       X DELTA COMPUTATION
+           X DELTA COMPUTATION
        ************************************************************/
 
        /* compute the amount of pointer motion in frames, and where
           the region would be if we moved it by that much.
        */
+       if ( drag_info.move_threshold_passed ) {
 
-       if (drag_info.move_threshold_passed) {
-
-               if ((int32_t)drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
+               if (drag_info.current_pointer_frame >= drag_info.pointer_frame_offset) {
 
-                       nframes_t sync_frame;
-                       nframes_t sync_offset;
+                       nframes64_t sync_frame;
+                       nframes64_t sync_offset;
                        int32_t sync_dir;
-           
+
                        pending_region_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
-           
-                       sync_offset = rv->region()->sync_offset (sync_dir);
-                       sync_frame = rv->region()->adjust_to_sync (pending_region_position);
 
-                       /* we snap if the snap modifier is not enabled.
+                       sync_offset = clicked_regionview->region()->sync_offset (sync_dir);
+
+                       /* we don't handle a sync point that lies before zero.
                         */
+                       if (sync_dir >= 0 || (sync_dir < 0 && pending_region_position >= sync_offset)) {
+                               sync_frame = pending_region_position + (sync_dir*sync_offset);
+
+                               /* we snap if the snap modifier is not enabled.
+                                */
            
-                       if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
-                               snap_to (sync_frame);   
-                       }
+                               if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
+                                       snap_to (sync_frame);   
+                               }
            
-                       if (sync_frame - sync_offset <= sync_frame) {
-                               pending_region_position = sync_frame - (sync_dir*sync_offset);
+                               pending_region_position = clicked_regionview->region()->adjust_to_sync (sync_frame);
+
                        } else {
-                               pending_region_position = 0;
+                               pending_region_position = drag_info.last_frame_position;
                        }
            
                } else {
                        pending_region_position = 0;
                }
          
-               if (pending_region_position > max_frames - rv->region()->length()) {
+               if (pending_region_position > max_frames - clicked_regionview->region()->length()) {
                        pending_region_position = drag_info.last_frame_position;
                }
-         
+
                // printf ("3: pending_region_position= %lu    %lu\n", pending_region_position, drag_info.last_frame_position );
-         
-               if (pending_region_position != drag_info.last_frame_position && !drag_info.x_constrained) {
 
-                       /* now compute the canvas unit distance we need to move the regiondrag_info.last_trackview->order
+               bool x_move_allowed;
+               
+               if (Config->get_edit_mode() == Lock) {
+                       if (drag_info.copy) {
+                               x_move_allowed = !drag_info.x_constrained;
+                       } else {
+                               /* in locked edit mode, reverse the usual meaning of x_constrained */
+                               x_move_allowed = drag_info.x_constrained;
+                       }
+               } else {
+                       x_move_allowed = !drag_info.x_constrained;
+               }
+
+               if (( pending_region_position != drag_info.last_frame_position) && x_move_allowed ) {
+
+                       /* now compute the canvas unit distance we need to move the regionview
                           to make it appear at the new location.
                        */
-           
+
                        if (pending_region_position > drag_info.last_frame_position) {
                                x_delta = ((double) (pending_region_position - drag_info.last_frame_position) / frames_per_unit);
                        } else {
                                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);
+
+                                       // 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);
+                       
+                                       if (-x_delta > ix1 + horizontal_adjustment.get_value()) {
+                                               //      do_move = false;
+                                               x_delta = 0;
+                                               pending_region_position = drag_info.last_frame_position;
+                                               break;
+                                       }
+                               }
+
                        }
-           
+               
                        drag_info.last_frame_position = pending_region_position;
-           
+
                } else {
                        x_delta = 0;
                }
@@ -3042,9 +3753,9 @@ Editor::region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
 
                x_delta = 0;
        }
-
+       
        /*************************************************************
-                        PREPARE TO MOVE
+           PREPARE TO MOVE
        ************************************************************/
 
        if (x_delta == 0 && (pointer_y_span == 0)) {
@@ -3052,69 +3763,76 @@ Editor::region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
                   trackviews. nothing to do.
                */
                return;
-       } 
-
-       if (x_delta < 0) {
-               for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
-
-                       RegionView* rv2 = (*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_group()->i2w (ix1, iy1);
-
-                       if (ix1 <= 1) {
-                               x_delta = 0;
-                               break;
-                       }
-               }
        }
 
        /*************************************************************
-                        MOTION                                                               
+           MOTION                                                                    
        ************************************************************/
-
-       bool do_move;
-
+       bool do_move = true;
        if (drag_info.first_move) {
-               if (drag_info.move_threshold_passed) {
-                       do_move = true;
-               } else {
+               if (!drag_info.move_threshold_passed) {
                        do_move = false;
                }
-       } else {
-               do_move = true;
        }
 
        if (do_move) {
 
                pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
                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.
                        */
 
                        rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
-                       rv->get_canvas_group()->i2w (ix1, iy1);
+                       rv->get_canvas_frame()->i2w (ix1, iy1);
+                       
+                       cerr << "adjust y from " << iy1 << " using "
+                            << vertical_adjustment.get_value() << " - "
+                            << canvas_timebars_vsize
+                            << endl;
+
+                       iy1 += get_trackview_group_vertical_offset ();;
+
+                       if (drag_info.first_move) {
+
+                               // hide any dependent views 
+       
+                               rv->get_time_axis_view().hide_dependent_views (*rv);
+
+                               /* 
+                                  reparent to a non scrolling group so that we can keep the 
+                                  region selection above all time axis views.
+                                  reparenting means we have to move the rv as the two 
+                                  parent groups have different coordinates.
+                               */
+
+                               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);
-                       AudioTimeAxisView* canvas_atv = dynamic_cast<AudioTimeAxisView*>(tvp2);
-                       AudioTimeAxisView* temp_atv;
+                       RouteTimeAxisView* canvas_rtv = dynamic_cast<RouteTimeAxisView*>(tvp2);
+                       RouteTimeAxisView* temp_rtv;
 
                        if ((pointer_y_span != 0) && !clamp_y_axis) {
                                y_delta = 0;
                                int32_t x = 0;
                                for (j = height_list.begin(); j!= height_list.end(); j++) {     
-                                       if (x == canvas_atv->order) {
+                                       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 */
@@ -3136,6 +3854,7 @@ Editor::region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
                                                        y_delta -= (*j);        
                                                        temp_pointer_y_span--;  
                                                }
+
                                                while (temp_pointer_y_span < 0) {                 
                                                        y_delta += (*j);
                                                        if (x != original_pointer_order) { 
@@ -3152,57 +3871,25 @@ Editor::region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
                                                /* find out where we'll be when we move and set height accordingly */
                  
                                                tvp2 = trackview_by_y_position (iy1 + y_delta);
-                                               temp_atv = dynamic_cast<AudioTimeAxisView*>(tvp2);
-                                               rv->set_height (temp_atv->height);
-       
+                                               temp_rtv = dynamic_cast<RouteTimeAxisView*>(tvp2);
+                                               rv->set_height (temp_rtv->current_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_atv->view->get_region_color());
+                                               //const GdkColor& col (temp_rtv->view->get_region_color());
                                                //rv->set_color (const_cast<GdkColor&>(col));
                                                break;          
                                        }
                                        x++;
                                }
                        }
-         
-                       /* prevent the regionview from being moved to before 
-                          the zero position on the canvas.
-                       */
-                       /* clamp */
-               
-                       if (x_delta < 0) {
-                               if (-x_delta > ix1) {
-                                       x_delta = -ix1;
-                               }
-                       } else if ((x_delta > 0) &&(rv->region()->last_frame() > max_frames - x_delta)) {
-                               x_delta = max_frames - rv->region()->last_frame();
-                       }
-
-                       if (drag_info.first_move) {
-
-                               /* hide any dependent views */
-                       
-                               rv->get_time_axis_view().hide_dependent_views (*rv);
-                       
-                               /* this is subtle. raising the regionview itself won't help,
-                                  because raise_to_top() just puts the item on the top of
-                                  its parent's stack. so, we need to put the trackview canvas_display group
-                                  on the top, since its parent is the whole canvas.
-                               */
-                       
-                               rv->get_canvas_group()->raise_to_top();
-                               rv->get_time_axis_view().canvas_display->raise_to_top();
-                               cursor_group->raise_to_top();
 
-                               rv->fake_set_opaque (true);
-                       }
-                       
                        if (drag_info.brushing) {
                                mouse_brush_insert_region (rv, pending_region_position);
                        } else {
-                               rv->move (x_delta, y_delta);                    
+                               rv->move (x_delta, y_delta);
                        }
 
                } /* foreach region */
@@ -3222,14 +3909,19 @@ Editor::region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
 void
 Editor::region_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
 {
-       nframes_t where;
-       RegionView* rv = reinterpret_cast<RegionView *> (drag_info.data);
-       pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
        bool nocommit = true;
-       double speed;
-       RouteTimeAxisView* atv;
-       bool regionview_y_movement;
-       bool regionview_x_movement;
+       vector<RegionView*> copies;
+       RouteTimeAxisView* source_tv;
+       boost::shared_ptr<Diskstream> ds;
+       boost::shared_ptr<Playlist> from_playlist;
+       vector<RegionView*> new_selection;
+       typedef set<boost::shared_ptr<Playlist> > PlaylistSet;
+       PlaylistSet modified_playlists;
+       PlaylistSet frozen_playlists;
+       list <sigc::connection> modified_playlist_connections;
+       pair<PlaylistSet::iterator,bool> insert_result, frozen_insert_result;
+       nframes64_t drag_delta;
+       bool changed_tracks, changed_position;
 
        /* first_move is set to false if the regionview has been moved in the 
           motion handler. 
@@ -3242,32 +3934,32 @@ Editor::region_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event
 
        nocommit = false;
 
-       /* The regionview has been moved at some stage during the grab so we need
-          to account for any mouse movement between this event and the last one. 
-       */      
-
-       region_drag_motion_callback (item, event);
+       if (Config->get_edit_mode() == Splice && !pre_drag_region_selection.empty()) {
+               selection->set (pre_drag_region_selection);
+               pre_drag_region_selection.clear ();
+       }
 
        if (drag_info.brushing) {
                /* all changes were made during motion event handlers */
+               
+               if (drag_info.copy) {
+                       for (list<RegionView*>::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
+                               copies.push_back (*i);
+                       }
+               }
+
                goto out;
        }
 
-       /* adjust for track speed */
-       speed = 1.0;
-
-       atv = dynamic_cast<AudioTimeAxisView*> (drag_info.last_trackview);
-       if (atv && atv->get_diskstream()) {
-               speed = atv->get_diskstream()->speed();
-       }
-       
-       regionview_x_movement = (drag_info.last_frame_position != (nframes_t) (rv->region()->position()/speed));
-       regionview_y_movement = (drag_info.last_trackview != &rv->get_time_axis_view());
+       char* op_string;
 
-       //printf ("last_frame: %s position is %lu  %g\n", rv->get_time_axis_view().name().c_str(), drag_info.last_frame_position, speed); 
-       //printf ("last_rackview: %s \n", drag_info.last_trackview->name().c_str()); 
+       /* reverse this here so that we have the correct logic to finalize
+          the drag.
+       */
        
-       char* op_string;
+       if (Config->get_edit_mode() == Lock && !drag_info.copy) {
+               drag_info.x_constrained = !drag_info.x_constrained;
+       }
 
        if (drag_info.copy) {
                if (drag_info.x_constrained) {
@@ -3285,70 +3977,139 @@ Editor::region_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event
 
        begin_reversible_command (op_string);
 
-       if (regionview_y_movement) {
+       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());
 
-               /* moved to a different audio track. */
-               
-               vector<RegionView*> new_selection;
+       drag_delta = clicked_regionview->region()->position() - drag_info.last_frame_position;
 
-               for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ) {
-           
-                       RegionView* rv = (*i);              
+       track_canvas->update_now ();
 
-                       double ix1, ix2, iy1, iy2;
+       for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ) {
                        
-                       rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
-                       rv->get_canvas_group()->i2w (ix1, iy1);
-                       TimeAxisView* tvp2 = trackview_by_y_position (iy1);
-                       AudioTimeAxisView* atv2 = dynamic_cast<AudioTimeAxisView*>(tvp2);
+               RegionView* rv = (*i);              
+               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* dest_tv = trackview_by_y_position (iy1);
+               RouteTimeAxisView* dest_rtv = dynamic_cast<RouteTimeAxisView*>(dest_tv);
+               nframes64_t where;
+
+               if (rv->region()->locked()) {
+                       ++i;
+                       continue;
+               }
+               
+               if (changed_position && !drag_info.x_constrained) {
+                       where = rv->region()->position() - drag_delta;
+               } else {
+                       where = rv->region()->position();
+               }
+                       
+               boost::shared_ptr<Region> new_region;
 
-                       boost::shared_ptr<Playlist> from_playlist = rv->region()->playlist();
-                       boost::shared_ptr<Playlist> to_playlist = atv2->playlist();
-           
-                       where = (nframes_t) (unit_to_frame (ix1) * speed);
-                       boost::shared_ptr<Region> new_region (RegionFactory::create (rv->region()));
+               if (drag_info.copy) {
+                       /* we already made a copy */
+                       new_region = rv->region();
 
                        /* undo the previous hide_dependent_views so that xfades don't
                           disappear on copying regions 
                        */
+               
+                       //rv->get_time_axis_view().reveal_dependent_views (*rv);
+               
+               } else if (changed_tracks && dest_rtv->playlist()) {
+                       new_region = RegionFactory::create (rv->region());
+               }
 
-                       rv->get_time_axis_view().reveal_dependent_views (*rv);
-
-                       if (!drag_info.copy) {
-                               
-                               /* the region that used to be in the old playlist is not
-                                  moved to the new one - we make a copy of it. as a result,
-                                  any existing editor for the region should no longer be
-                                  visible.
-                               */ 
-           
-                               rv->hide_region_editor();
-                               rv->fake_set_opaque (false);
+               if (changed_tracks || drag_info.copy) {
 
-                               session->add_command (new MementoCommand<Playlist>(*from_playlist, &from_playlist->get_state(), 0));    
-                               from_playlist->remove_region ((rv->region()));
-                               session->add_command (new MementoCommand<Playlist>(*from_playlist, 0, &from_playlist->get_state()));    
+                       boost::shared_ptr<Playlist> to_playlist = dest_rtv->playlist();
+                       if (!to_playlist) {
+                               ++i;
+                               continue;
                        }
 
-                       latest_regionview = 0;
+                       latest_regionviews.clear ();
+
+                       sigc::connection c = dest_rtv->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
                        
-                       sigc::connection c = atv2->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
-                       session->add_command (new MementoCommand<Playlist>(*to_playlist, &to_playlist->get_state(), 0));        
+                       insert_result = modified_playlists.insert (to_playlist);
+                       if (insert_result.second) {
+                               session->add_command (new MementoCommand<Playlist>(*to_playlist, &to_playlist->get_state(), 0));
+                       }
+
                        to_playlist->add_region (new_region, where);
-                       session->add_command (new MementoCommand<Playlist>(*to_playlist, 0, &to_playlist->get_state()));        
+
                        c.disconnect ();
                                                              
-                       if (latest_regionview) {
-                               new_selection.push_back (latest_regionview);
+                       if (!latest_regionviews.empty()) {
+                               // XXX why just the first one ? we only expect one
+                               // commented out in nick_m's canvas reworking. is that intended?
+                               //dest_atv->reveal_dependent_views (*latest_regionviews.front());
+                               new_selection.push_back (latest_regionviews.front());
                        }
 
-                       if (drag_info.copy) {
-                               // get rid of the copy
-                               delete rv;
-                       } 
+               } else {
+                       /* 
+                          motion on the same track. plonk the previously reparented region 
+                          back to its original canvas group (its streamview).
+                          No need to do anything for copies as they are fake regions which will be deleted.
+                       */
+
+                       rv->get_canvas_group()->reparent (*dest_rtv->view()->canvas_item());
+                       rv->get_canvas_group()->property_y() = 0;
+                 
+                       /* just change the model */
+                       
+                       boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
+
+                       insert_result = modified_playlists.insert (playlist);
+                       if (insert_result.second) {
+                               session->add_command (new MementoCommand<Playlist>(*playlist, &playlist->get_state(), 0));
+                       }
+                       /* freeze to avoid lots of relayering in the case of a multi-region drag */
+                       frozen_insert_result = frozen_playlists.insert(playlist);
+                       if (frozen_insert_result.second) {
+                               playlist->freeze();
+                       }
+
+                       rv->region()->set_position (where, (void*) this);
+               }
+
+               if (changed_tracks && !drag_info.copy) {
+
+                       /* get the playlist where this drag started. we can't use rv->region()->playlist()
+                          because we may have copied the region and it has not been attached to a playlist.
+                       */
+
+                       assert ((source_tv = dynamic_cast<RouteTimeAxisView*> (&rv->get_time_axis_view())));
+                       assert ((ds = source_tv->get_diskstream()));
+                       assert ((from_playlist = ds->playlist()));
+
+                       /* moved to a different audio track, without copying */
+
+                       /* the region that used to be in the old playlist is not
+                          moved to the new one - we use a copy of it. as a result,
+                          any existing editor for the region should no longer be
+                          visible.
+                       */ 
+           
+                       rv->hide_region_editor();
+                       rv->fake_set_opaque (false);
+                       
+                       /* remove the region from the old playlist */
 
+                       insert_result = modified_playlists.insert (from_playlist);
+                       if (insert_result.second) {
+                               session->add_command (new MementoCommand<Playlist>(*from_playlist, &from_playlist->get_state(), 0));
+                       }
+
+                       from_playlist->remove_region ((rv->region()));
+                       
                        /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
-                          was selected in all of them, then removing it from the playlist will have removed all
+                          was selected in all of them, then removing it from a playlist will have removed all
                           trace of it from the selection (i.e. there were N regions selected, we removed 1,
                           but since its the same playlist for N tracks, all N tracks updated themselves, removed the
                           corresponding regionview, and the selection is now empty).
@@ -3359,6 +4120,9 @@ Editor::region_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event
                           here. if the region selection is not empty, then restart the loop because we know that
                           we must have removed at least the region(view) we've just been working on as well as any
                           that we processed on previous iterations.
+
+                          EXCEPT .... if we are doing a copy drag, then the selection hasn't been modified and
+                          we can just iterate.
                        */
 
                        if (selection->regions.empty()) {
@@ -3366,110 +4130,99 @@ Editor::region_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event
                        } else { 
                                i = selection->regions.by_layer().begin();
                        }
-               } 
-
-               selection->set (new_selection);
-
-       } else {
-
-               /* motion within a single track */
-
-               list<RegionView*> regions = selection->regions.by_layer();
 
+               } else {
+                       ++i;
+               }
+               
+               if (drag_info.copy) {
+                       copies.push_back (rv);
+               }
+       }
+       
+       if (new_selection.empty()) {
                if (drag_info.copy) {
+                       /* the region(view)s that are selected and being dragged around
+                          are copies and do not belong to any track. remove them
+                          from the selection right here.
+                       */
                        selection->clear_regions();
                }
-               
-               for (list<RegionView*>::iterator i = regions.begin(); i != regions.end(); ++i) {
-
-                       rv = (*i);
+       } else {
+               /* this will clear any existing selection that would have been
+                  cleared in the other clause above
+               */
+               selection->set (new_selection);
+       }
 
-                       if (rv->region()->locked()) {
-                               continue;
-                       }
+       for (set<boost::shared_ptr<Playlist> >::iterator p = frozen_playlists.begin(); p != frozen_playlists.end(); ++p) {
+               (*p)->thaw();
+       }
                        
+  out:
+       if (!nocommit) {
+               for (set<boost::shared_ptr<Playlist> >::iterator p = modified_playlists.begin(); p != modified_playlists.end(); ++p) {
+                       session->add_command (new MementoCommand<Playlist>(*(*p), 0, &(*p)->get_state()));      
+               }
+               commit_reversible_command ();
+       }
 
-                       if (regionview_x_movement) {
-                               double ownspeed = 1.0;
-                               atv = dynamic_cast<AudioTimeAxisView*> (&(rv->get_time_axis_view()));
-
-                               if (atv && atv->get_diskstream()) {
-                                       ownspeed = atv->get_diskstream()->speed();
-                               }
-                               
-                               /* base the new region position on the current position of the regionview.*/
-                               
-                               double ix1, ix2, iy1, iy2;
-                               
-                               rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
-                               rv->get_canvas_group()->i2w (ix1, iy1);
-                               where = (nframes_t) (unit_to_frame (ix1) * ownspeed);
-                               
-                       } else {
-                               
-                               where = rv->region()->position();
-                       }
-
-                       boost::shared_ptr<Playlist> to_playlist = rv->region()->playlist();
+       for (vector<RegionView*>::iterator x = copies.begin(); x != copies.end(); ++x) {
+               delete *x;
+       }
 
-                       assert (to_playlist);
+}
+       
+void
+Editor::create_region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
+{
+       if (drag_info.move_threshold_passed) {
+               if (drag_info.first_move) {
+                       // TODO: create region-create-drag region view here
+                       drag_info.first_move = false;
+               }
 
-                       /* add the undo */
+               // TODO: resize region-create-drag region view here
+       }
+} 
 
-                       session->add_command (new MementoCommand<Playlist>(*to_playlist, &to_playlist->get_state(), 0));        
+void
+Editor::create_region_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
+{
+       MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*> (drag_info.dest_trackview);
+       if (!mtv)
+               return;
 
-                       if (drag_info.copy) {
+       const boost::shared_ptr<MidiDiskstream> diskstream =
+               boost::dynamic_pointer_cast<MidiDiskstream>(mtv->view()->trackview().track()->diskstream());
+       
+       if (!diskstream) {
+               warning << "Cannot create non-MIDI region" << endl;
+               return;
+       }
 
-                               boost::shared_ptr<Region> newregion;
-                               boost::shared_ptr<Region> ar;
+       if (drag_info.first_move) {
+               begin_reversible_command (_("create region"));
+               XMLNode &before = mtv->playlist()->get_state();
 
-                               if ((ar = boost::dynamic_pointer_cast<AudioRegion>(rv->region())) != 0) {
-                                       newregion = RegionFactory::create (ar);
-                               } else {
-                                       /* XXX MIDI HERE drobilla */
-                                       continue;
-                               }
+               nframes64_t start = drag_info.grab_frame;
+               snap_to (start, -1);
+               const Meter& m = session->tempo_map().meter_at(start);
+               const Tempo& t = session->tempo_map().tempo_at(start);
+               double length = floor (m.frames_per_bar(t, session->frame_rate()));
 
-                               /* add it */
-                               
-                               latest_regionview = 0;
-                               sigc::connection c = atv->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
-                               to_playlist->add_region (newregion, (nframes_t) (where * atv->get_diskstream()->speed()));
-                               c.disconnect ();
-                               
-                               if (latest_regionview) {
-                                       atv->reveal_dependent_views (*latest_regionview);
-                                       selection->add (latest_regionview);
-                               }
-                               
-                               /* if the original region was locked, we don't care for the new one */
+               boost::shared_ptr<Source> src = session->create_midi_source_for_session(*diskstream.get());
                                
-                               newregion->set_locked (false);                  
-
-                       } else {
-
-                               /* just change the model */
-
-                               rv->region()->set_position (where, (void*) this);
-
-                       }
-
-                       /* add the redo */
-
-                       session->add_command (new MementoCommand<Playlist>(*to_playlist, 0, &to_playlist->get_state()));
-
-                       /* get rid of the copy */
-
-                       if (drag_info.copy) {
-                               delete rv;
-                       }
-               }
-       }
+               mtv->playlist()->add_region (boost::dynamic_pointer_cast<MidiRegion>
+                                            (RegionFactory::create(src, 0, (nframes_t) length, 
+                                                                   PBD::basename_nosuffix(src->name()))), start);
+               XMLNode &after = mtv->playlist()->get_state();
+               session->add_command(new MementoCommand<Playlist>(*mtv->playlist().get(), &before, &after));
+               commit_reversible_command();
 
-  out:
-       
-       if (!nocommit) {
-               commit_reversible_command ();
+       } else {
+               create_region_drag_motion_callback (item, event);
+               // TODO: create region-create-drag region here
        }
 }
 
@@ -3480,44 +4233,57 @@ Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
           this is an alignment click (control used)
        */
        
-       if (Keyboard::modifier_state_contains (event->state, Keyboard::Control)) {
+       if (Keyboard::modifier_state_contains (event->state, Keyboard::PrimaryModifier)) {
                TimeAxisView* tv = &rv.get_time_axis_view();
-               AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*>(tv);
+               RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
                double speed = 1.0;
-               if (atv && atv->is_audio_track()) {
-                       speed = atv->get_diskstream()->speed();
+               if (rtv && rtv->is_track()) {
+                       speed = rtv->get_diskstream()->speed();
                }
 
-               if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::Control|Keyboard::Alt))) {
-
-                       align_region (rv.region(), SyncPoint, (nframes_t) (edit_cursor->current_frame * speed));
-
-               } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::Control|Keyboard::Shift))) {
+               nframes64_t where = get_preferred_edit_position();
 
-                       align_region (rv.region(), End, (nframes_t) (edit_cursor->current_frame * speed));
+               if (where >= 0) {
 
-               } else {
-
-                       align_region (rv.region(), Start, (nframes_t) (edit_cursor->current_frame * speed));
+                       if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
+                               
+                               align_region (rv.region(), SyncPoint, (nframes64_t) (where * speed));
+                               
+                       } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
+                               
+                               align_region (rv.region(), End, (nframes64_t) (where * speed));
+                               
+                       } else {
+                               
+                               align_region (rv.region(), Start, (nframes64_t) (where * speed));
+                       }
                }
        }
 }
 
 void
-Editor::show_verbose_time_cursor (nframes_t frame, double offset, double xpos, double ypos) 
+Editor::show_verbose_time_cursor (nframes64_t frame, double offset, double xpos, double ypos) 
 {
        char buf[128];
        SMPTE::Time smpte;
        BBT_Time bbt;
        int hours, mins;
-       nframes_t frame_rate;
+       nframes64_t frame_rate;
        float secs;
 
        if (session == 0) {
                return;
        }
 
-       switch (Profile->get_small_screen() ? ARDOUR_UI::instance()->primary_clock.mode () : ARDOUR_UI::instance()->secondary_clock.mode ()) {
+       AudioClock::Mode m;
+
+       if (Profile->get_sae() || Profile->get_small_screen()) {
+               m = ARDOUR_UI::instance()->primary_clock.mode();
+       } else {
+               m = ARDOUR_UI::instance()->secondary_clock.mode();
+       }
+
+       switch (m) {
        case AudioClock::BBT:
                session->bbt_time (frame, bbt);
                snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, bbt.bars, bbt.beats, bbt.ticks);
@@ -3540,7 +4306,7 @@ Editor::show_verbose_time_cursor (nframes_t frame, double offset, double xpos, d
                break;
 
        default:
-               snprintf (buf, sizeof(buf), "%u", frame);
+               snprintf (buf, sizeof(buf), "%" PRIi64, frame);
                break;
        }
 
@@ -3548,20 +4314,20 @@ Editor::show_verbose_time_cursor (nframes_t frame, double offset, double xpos, d
                set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
        }
        else {
-               set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset, drag_info.current_pointer_y + offset);
+               set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset - horizontal_adjustment.get_value(), drag_info.current_pointer_y + offset - vertical_adjustment.get_value() + canvas_timebars_vsize);
        }
        show_verbose_canvas_cursor ();
 }
 
 void
-Editor::show_verbose_duration_cursor (nframes_t start, nframes_t end, double offset, double xpos, double ypos) 
+Editor::show_verbose_duration_cursor (nframes64_t start, nframes64_t end, double offset, double xpos, double ypos) 
 {
        char buf[128];
        SMPTE::Time smpte;
        BBT_Time sbbt;
        BBT_Time ebbt;
        int hours, mins;
-       nframes_t distance, frame_rate;
+       nframes64_t distance, frame_rate;
        float secs;
        Meter meter_at_start(session->tempo_map().meter_at(start));
 
@@ -3569,7 +4335,15 @@ Editor::show_verbose_duration_cursor (nframes_t start, nframes_t end, double off
                return;
        }
 
-       switch (ARDOUR_UI::instance()->secondary_clock.mode ()) {
+       AudioClock::Mode m;
+
+       if (Profile->get_sae() || Profile->get_small_screen()) {
+               m = ARDOUR_UI::instance()->primary_clock.mode ();
+       } else {
+               m = ARDOUR_UI::instance()->secondary_clock.mode ();
+       }
+
+       switch (m) {
        case AudioClock::BBT:
                session->bbt_time (start, sbbt);
                session->bbt_time (end, ebbt);
@@ -3614,7 +4388,7 @@ Editor::show_verbose_duration_cursor (nframes_t start, nframes_t end, double off
                break;
 
        default:
-               snprintf (buf, sizeof(buf), "%u", end - start);
+               snprintf (buf, sizeof(buf), "%" PRIi64, end - start);
                break;
        }
 
@@ -3624,13 +4398,21 @@ Editor::show_verbose_duration_cursor (nframes_t start, nframes_t end, double off
        else {
                set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset, drag_info.current_pointer_y + offset);
        }
+
        show_verbose_canvas_cursor ();
 }
 
 void
 Editor::collect_new_region_view (RegionView* rv)
 {
-       latest_regionview = rv;
+       latest_regionviews.push_back (rv);
+}
+
+void
+Editor::collect_and_select_new_region_view (RegionView* rv)
+{
+       selection->add(rv);
+       latest_regionviews.push_back (rv);
 }
 
 void
@@ -3642,7 +4424,7 @@ Editor::start_selection_grab (ArdourCanvas::Item* item, GdkEvent* event)
 
        /* lets try to create new Region for the selection */
 
-       vector<boost::shared_ptr<AudioRegion> > new_regions;
+       vector<boost::shared_ptr<Region> > new_regions;
        create_region_from_selection (new_regions);
 
        if (new_regions.empty()) {
@@ -3660,8 +4442,8 @@ Editor::start_selection_grab (ArdourCanvas::Item* item, GdkEvent* event)
           set the regionview we want to then drag.
        */
        
-       latest_regionview = 0;
-       sigc::connection c = clicked_audio_trackview->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
+       latest_regionviews.clear();
+       sigc::connection c = clicked_routeview->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
        
        /* A selection grab currently creates two undo/redo operations, one for 
           creating the new region and another for moving it.
@@ -3669,35 +4451,37 @@ Editor::start_selection_grab (ArdourCanvas::Item* item, GdkEvent* event)
 
        begin_reversible_command (_("selection grab"));
 
-       boost::shared_ptr<Playlist> playlist = clicked_trackview->playlist();
+       boost::shared_ptr<Playlist> playlist = clicked_axisview->playlist();
 
-        XMLNode *before = &(playlist->get_state());
-       clicked_trackview->playlist()->add_region (region, selection->time[clicked_selection].start);
-        XMLNode *after = &(playlist->get_state());
+       XMLNode *before = &(playlist->get_state());
+       clicked_routeview->playlist()->add_region (region, selection->time[clicked_selection].start);
+       XMLNode *after = &(playlist->get_state());
        session->add_command(new MementoCommand<Playlist>(*playlist, before, after));
 
        commit_reversible_command ();
        
        c.disconnect ();
        
-       if (latest_regionview == 0) {
+       if (latest_regionviews.empty()) {
                /* something went wrong */
                return;
        }
 
        /* we need to deselect all other regionviews, and select this one
-          i'm ignoring undo stuff, because the region creation will take care of it */
-       selection->set (latest_regionview);
+          i'm ignoring undo stuff, because the region creation will take care of it 
+       */
+       selection->set (latest_regionviews);
        
-       drag_info.item = latest_regionview->get_canvas_group();
-       drag_info.data = latest_regionview;
+       drag_info.item = latest_regionviews.front()->get_canvas_group();
+       drag_info.data = latest_regionviews.front();
        drag_info.motion_callback = &Editor::region_drag_motion_callback;
        drag_info.finished_callback = &Editor::region_drag_finished_callback;
 
        start_grab (event);
        
-       drag_info.last_trackview = clicked_trackview;
-       drag_info.last_frame_position = latest_regionview->region()->position();
+       drag_info.source_trackview = clicked_routeview;
+       drag_info.dest_trackview = drag_info.source_trackview;
+       drag_info.last_frame_position = latest_regionviews.front()->region()->position();
        drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
        
        show_verbose_time_cursor (drag_info.last_frame_position, 10);
@@ -3706,20 +4490,18 @@ Editor::start_selection_grab (ArdourCanvas::Item* item, GdkEvent* event)
 void
 Editor::cancel_selection ()
 {
-        for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
+       for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
                (*i)->hide_selection ();
        }
-       begin_reversible_command (_("cancel selection"));
        selection->clear ();
        clicked_selection = 0;
-       commit_reversible_command ();
 }      
 
 void
 Editor::start_selection_op (ArdourCanvas::Item* item, GdkEvent* event, SelectionOp op)
 {
-       nframes_t start = 0;
-       nframes_t end = 0;
+       nframes64_t start = 0;
+       nframes64_t end = 0;
 
        if (session == 0) {
                return;
@@ -3733,7 +4515,7 @@ Editor::start_selection_op (ArdourCanvas::Item* item, GdkEvent* event, Selection
 
        switch (op) {
        case CreateSelection:
-               if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
+               if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
                        drag_info.copy = true;
                } else {
                        drag_info.copy = false;
@@ -3742,8 +4524,8 @@ Editor::start_selection_op (ArdourCanvas::Item* item, GdkEvent* event, Selection
                break;
 
        case SelectionStartTrim:
-               if (clicked_trackview) {
-                       clicked_trackview->order_selection_trims (item, true);
+               if (clicked_axisview) {
+                       clicked_axisview->order_selection_trims (item, true);
                } 
                start_grab (event, trimmer_cursor);
                start = selection->time[clicked_selection].start;
@@ -3751,8 +4533,8 @@ Editor::start_selection_op (ArdourCanvas::Item* item, GdkEvent* event, Selection
                break;
                
        case SelectionEndTrim:
-               if (clicked_trackview) {
-                       clicked_trackview->order_selection_trims (item, false);
+               if (clicked_axisview) {
+                       clicked_axisview->order_selection_trims (item, false);
                }
                start_grab (event, trimmer_cursor);
                end = selection->time[clicked_selection].end;
@@ -3776,15 +4558,14 @@ Editor::start_selection_op (ArdourCanvas::Item* item, GdkEvent* event, Selection
 void
 Editor::drag_selection (ArdourCanvas::Item* item, GdkEvent* event)
 {
-       nframes_t start = 0;
-       nframes_t end = 0;
-       nframes_t length;
-       nframes_t pending_position;
+       nframes64_t start = 0;
+       nframes64_t end = 0;
+       nframes64_t length;
+       nframes64_t pending_position;
 
-       if ((int32_t) drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
+       if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
                pending_position = drag_info.current_pointer_frame - drag_info.pointer_frame_offset;
-       }
-       else {
+       } else {
                pending_position = 0;
        }
        
@@ -3827,7 +4608,7 @@ Editor::drag_selection (ArdourCanvas::Item* item, GdkEvent* event)
                                drag_info.copy = false;
                        } else {
                                /* new selection-> */
-                               clicked_selection = selection->set (clicked_trackview, start, end);
+                               clicked_selection = selection->set (clicked_axisview, start, end);
                        }
                } 
                break;
@@ -3885,7 +4666,7 @@ Editor::drag_selection (ArdourCanvas::Item* item, GdkEvent* event)
        }
        
        if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
-               start_canvas_autoscroll (1);
+               start_canvas_autoscroll (1, 0);
        }
 
        if (start != end) {
@@ -3931,16 +4712,16 @@ void
 Editor::start_trim (ArdourCanvas::Item* item, GdkEvent* event)
 {
        double speed = 1.0;
-       TimeAxisView* tvp = clicked_trackview;
-       AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
+       TimeAxisView* tvp = clicked_axisview;
+       RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
 
-       if (tv && tv->is_audio_track()) {
+       if (tv && tv->is_track()) {
                speed = tv->get_diskstream()->speed();
        }
        
-       nframes_t region_start = (nframes_t) (clicked_regionview->region()->position() / speed);
-       nframes_t region_end = (nframes_t) (clicked_regionview->region()->last_frame() / speed);
-       nframes_t region_length = (nframes_t) (clicked_regionview->region()->length() / speed);
+       nframes64_t region_start = (nframes64_t) (clicked_regionview->region()->position() / speed);
+       nframes64_t region_end = (nframes64_t) (clicked_regionview->region()->last_frame() / speed);
+       nframes64_t region_length = (nframes64_t) (clicked_regionview->region()->length() / speed);
 
        //drag_info.item = clicked_regionview->get_name_highlight();
        drag_info.item = item;
@@ -3949,7 +4730,7 @@ Editor::start_trim (ArdourCanvas::Item* item, GdkEvent* event)
 
        start_grab (event, trimmer_cursor);
        
-       if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
+       if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
                trim_op = ContentsTrim;
        } else {
                /* These will get overridden for a point trim.*/
@@ -3979,7 +4760,7 @@ void
 Editor::trim_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
 {
        RegionView* rv = clicked_regionview;
-       nframes_t frame_delta = 0;
+       nframes64_t frame_delta = 0;
        bool left_direction;
        bool obey_snap = !Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier());
 
@@ -3989,11 +4770,11 @@ Editor::trim_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
        */ 
 
        double speed = 1.0;
-       TimeAxisView* tvp = clicked_trackview;
+       TimeAxisView* tvp = clicked_axisview;
        RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
        pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
 
-       if (tv && tv->is_audio_track()) {
+       if (tv && tv->is_track()) {
                speed = tv->get_diskstream()->speed();
        }
        
@@ -4040,7 +4821,8 @@ Editor::trim_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
                        boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
                        insert_result = motion_frozen_playlists.insert (pl);
                        if (insert_result.second) {
-                                session->add_command(new MementoCommand<Playlist>(*pl, &pl->get_state(), 0));
+                               session->add_command(new MementoCommand<Playlist>(*pl, &pl->get_state(), 0));
+                               pl->freeze();
                        }
                }
        }
@@ -4055,7 +4837,7 @@ Editor::trim_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
        case StartTrim:
                if ((left_direction == false) && (drag_info.current_pointer_frame <= rv->region()->first_frame()/speed)) {
                        break;
-                } else {
+               } else {
                        for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
                                single_start_trim (**i, frame_delta, left_direction, obey_snap);
                        }
@@ -4063,7 +4845,7 @@ Editor::trim_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
                }
                
        case EndTrim:
-               if ((left_direction == true) && (drag_info.current_pointer_frame > (nframes_t) (rv->region()->last_frame()/speed))) {
+               if ((left_direction == true) && (drag_info.current_pointer_frame > (nframes64_t) (rv->region()->last_frame()/speed))) {
                        break;
                } else {
                        for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
@@ -4076,7 +4858,7 @@ Editor::trim_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
                {
                        bool swap_direction = false;
 
-                       if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Control)) {
+                       if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
                                swap_direction = true;
                        }
                        
@@ -4091,10 +4873,10 @@ Editor::trim_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
 
        switch (trim_op) {
        case StartTrim:
-               show_verbose_time_cursor((nframes_t) (rv->region()->position()/speed), 10);     
+               show_verbose_time_cursor((nframes64_t) (rv->region()->position()/speed), 10);   
                break;
        case EndTrim:
-               show_verbose_time_cursor((nframes_t) (rv->region()->last_frame()/speed), 10);   
+               show_verbose_time_cursor((nframes64_t) (rv->region()->last_frame()/speed), 10); 
                break;
        case ContentsTrim:
                show_verbose_time_cursor(drag_info.current_pointer_frame, 10);  
@@ -4106,7 +4888,7 @@ Editor::trim_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
 }
 
 void
-Editor::single_contents_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool swap_direction, bool obey_snap)
+Editor::single_contents_trim (RegionView& rv, nframes64_t frame_delta, bool left_direction, bool swap_direction, bool obey_snap)
 {
        boost::shared_ptr<Region> region (rv.region());
 
@@ -4114,39 +4896,39 @@ Editor::single_contents_trim (RegionView& rv, nframes_t frame_delta, bool left_d
                return;
        }
 
-       nframes_t new_bound;
+       nframes64_t new_bound;
 
        double speed = 1.0;
-       TimeAxisView* tvp = clicked_trackview;
+       TimeAxisView* tvp = clicked_axisview;
        RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
 
-       if (tv && tv->is_audio_track()) {
+       if (tv && tv->is_track()) {
                speed = tv->get_diskstream()->speed();
        }
        
        if (left_direction) {
                if (swap_direction) {
-                       new_bound = (nframes_t) (region->position()/speed) + frame_delta;
+                       new_bound = (nframes64_t) (region->position()/speed) + frame_delta;
                } else {
-                       new_bound = (nframes_t) (region->position()/speed) - frame_delta;
+                       new_bound = (nframes64_t) (region->position()/speed) - frame_delta;
                }
        } else {
                if (swap_direction) {
-                       new_bound = (nframes_t) (region->position()/speed) - frame_delta;
+                       new_bound = (nframes64_t) (region->position()/speed) - frame_delta;
                } else {
-                       new_bound = (nframes_t) (region->position()/speed) + frame_delta;
+                       new_bound = (nframes64_t) (region->position()/speed) + frame_delta;
                }
        }
 
        if (obey_snap) {
                snap_to (new_bound);
        }
-       region->trim_start ((nframes_t) (new_bound * speed), this);     
+       region->trim_start ((nframes64_t) (new_bound * speed), this);   
        rv.region_changed (StartChanged);
 }
 
 void
-Editor::single_start_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool obey_snap)
+Editor::single_start_trim (RegionView& rv, nframes64_t frame_delta, bool left_direction, bool obey_snap)
 {
        boost::shared_ptr<Region> region (rv.region()); 
 
@@ -4154,33 +4936,33 @@ Editor::single_start_trim (RegionView& rv, nframes_t frame_delta, bool left_dire
                return;
        }
 
-       nframes_t new_bound;
+       nframes64_t new_bound;
 
        double speed = 1.0;
-       TimeAxisView* tvp = clicked_trackview;
-       AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
+       TimeAxisView* tvp = clicked_axisview;
+       RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
 
-       if (tv && tv->is_audio_track()) {
+       if (tv && tv->is_track()) {
                speed = tv->get_diskstream()->speed();
        }
        
        if (left_direction) {
-               new_bound = (nframes_t) (region->position()/speed) - frame_delta;
+               new_bound = (nframes64_t) (region->position()/speed) - frame_delta;
        } else {
-               new_bound = (nframes_t) (region->position()/speed) + frame_delta;
+               new_bound = (nframes64_t) (region->position()/speed) + frame_delta;
        }
 
        if (obey_snap) {
                snap_to (new_bound, (left_direction ? 0 : 1));  
        }
 
-       region->trim_front ((nframes_t) (new_bound * speed), this);
+       region->trim_front ((nframes64_t) (new_bound * speed), this);
 
        rv.region_changed (Change (LengthChanged|PositionChanged|StartChanged));
 }
 
 void
-Editor::single_end_trim (RegionView& rv, nframes_t frame_delta, bool left_direction, bool obey_snap)
+Editor::single_end_trim (RegionView& rv, nframes64_t frame_delta, bool left_direction, bool obey_snap)
 {
        boost::shared_ptr<Region> region (rv.region());
 
@@ -4188,26 +4970,26 @@ Editor::single_end_trim (RegionView& rv, nframes_t frame_delta, bool left_direct
                return;
        }
 
-       nframes_t new_bound;
+       nframes64_t new_bound;
 
        double speed = 1.0;
-       TimeAxisView* tvp = clicked_trackview;
-       AudioTimeAxisView* tv = dynamic_cast<AudioTimeAxisView*>(tvp);
+       TimeAxisView* tvp = clicked_axisview;
+       RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
 
-       if (tv && tv->is_audio_track()) {
+       if (tv && tv->is_track()) {
                speed = tv->get_diskstream()->speed();
        }
        
        if (left_direction) {
-               new_bound = (nframes_t) ((region->last_frame() + 1)/speed) - frame_delta;
+               new_bound = (nframes64_t) ((region->last_frame() + 1)/speed) - frame_delta;
        } else {
-               new_bound = (nframes_t) ((region->last_frame() + 1)/speed) + frame_delta;
+               new_bound = (nframes64_t) ((region->last_frame() + 1)/speed) + frame_delta;
        }
 
        if (obey_snap) {
                snap_to (new_bound);
        }
-       region->trim_end ((nframes_t) (new_bound * speed), this);
+       region->trim_end ((nframes64_t) (new_bound * speed), this);
        rv.region_changed (LengthChanged);
 }
        
@@ -4217,7 +4999,7 @@ Editor::trim_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
        if (!drag_info.first_move) {
                trim_motion_callback (item, event);
                
-               if (!clicked_regionview->get_selected()) {
+               if (!selection->selected (clicked_regionview)) {
                        thaw_region_after_trim (*clicked_regionview);           
                } else {
                        
@@ -4230,9 +5012,9 @@ Editor::trim_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
                }
                
                for (set<boost::shared_ptr<Playlist> >::iterator p = motion_frozen_playlists.begin(); p != motion_frozen_playlists.end(); ++p) {
-                       //(*p)->thaw ();
-                        session->add_command (new MementoCommand<Playlist>(*(*p).get(), 0, &(*p)->get_state()));
-                }
+                       (*p)->thaw ();
+                       session->add_command (new MementoCommand<Playlist>(*(*p).get(), 0, &(*p)->get_state()));
+               }
                
                motion_frozen_playlists.clear ();
 
@@ -4247,7 +5029,7 @@ void
 Editor::point_trim (GdkEvent* event)
 {
        RegionView* rv = clicked_regionview;
-       nframes_t new_bound = drag_info.current_pointer_frame;
+       nframes64_t new_bound = drag_info.current_pointer_frame;
 
        if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
                snap_to (new_bound);
@@ -4259,17 +5041,17 @@ Editor::point_trim (GdkEvent* event)
                trim_op = StartTrim;
                begin_reversible_command (_("Start point trim"));
 
-               if (rv->get_selected()) {
+               if (selection->selected (rv)) {
 
                        for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
                             i != selection->regions.by_layer().end(); ++i)
                        {
                                if (!(*i)->region()->locked()) {
                                        boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
-                                        XMLNode &before = pl->get_state();
+                                       XMLNode &before = pl->get_state();
                                        (*i)->region()->trim_front (new_bound, this);   
-                                        XMLNode &after = pl->get_state();
-                                        session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
+                                       XMLNode &after = pl->get_state();
+                                       session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
                                }
                        }
 
@@ -4279,7 +5061,7 @@ Editor::point_trim (GdkEvent* event)
                                boost::shared_ptr<Playlist> pl = rv->region()->playlist();
                                XMLNode &before = pl->get_state();
                                rv->region()->trim_front (new_bound, this);     
-                                XMLNode &after = pl->get_state();
+                               XMLNode &after = pl->get_state();
                                session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
                        }
                }
@@ -4291,7 +5073,7 @@ Editor::point_trim (GdkEvent* event)
                trim_op = EndTrim;
                begin_reversible_command (_("End point trim"));
 
-               if (rv->get_selected()) {
+               if (selection->selected (rv)) {
                        
                        for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
                        {
@@ -4310,7 +5092,7 @@ Editor::point_trim (GdkEvent* event)
                                boost::shared_ptr<Playlist> pl = rv->region()->playlist();
                                XMLNode &before = pl->get_state();
                                rv->region()->trim_end (new_bound, this);
-                                XMLNode &after = pl->get_state();
+                               XMLNode &after = pl->get_state();
                                session->add_command (new MementoCommand<Playlist>(*pl.get(), &before, &after));
                        }
                }
@@ -4333,7 +5115,7 @@ Editor::thaw_region_after_trim (RegionView& rv)
        }
 
        region->thaw (_("trimmed region"));
-        XMLNode &after = region->playlist()->get_state();
+       XMLNode &after = region->playlist()->get_state();
        session->add_command (new MementoCommand<Playlist>(*(region->playlist()), 0, &after));
 
        AudioRegionView* arv = dynamic_cast<AudioRegionView*>(&rv);
@@ -4377,8 +5159,9 @@ Editor::start_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event, Ran
        switch (op) {
        case CreateRangeMarker:
        case CreateTransportMarker:
-               
-               if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
+       case CreateCDMarker:
+       
+               if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
                        drag_info.copy = true;
                } else {
                        drag_info.copy = false;
@@ -4394,9 +5177,25 @@ Editor::start_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event, Ran
 void
 Editor::drag_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
 {
-       nframes_t start = 0;
-       nframes_t end = 0;
-       ArdourCanvas::SimpleRect *crect = (range_marker_op == CreateRangeMarker) ? range_bar_drag_rect: transport_bar_drag_rect;
+       nframes64_t start = 0;
+       nframes64_t end = 0;
+       ArdourCanvas::SimpleRect *crect;
+
+       switch (range_marker_op) {
+       case CreateRangeMarker:
+               crect = range_bar_drag_rect;
+               break;
+       case CreateTransportMarker:
+               crect = transport_bar_drag_rect;
+               break;
+       case CreateCDMarker:
+               crect = cd_marker_bar_drag_rect;
+               break;
+       default:
+               cerr << "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()" << endl;
+               return;
+               break;
+       }
        
        if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
                snap_to (drag_info.current_pointer_frame);
@@ -4411,6 +5210,7 @@ Editor::drag_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
        switch (range_marker_op) {
        case CreateRangeMarker:
        case CreateTransportMarker:
+       case CreateCDMarker:
                if (drag_info.first_move) {
                        snap_to (drag_info.grab_frame);
                }
@@ -4435,14 +5235,14 @@ Editor::drag_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
 
                        update_marker_drag_item (temp_location);
                        range_marker_drag_rect->show();
-                       range_marker_drag_rect->raise_to_top();
+                       //range_marker_drag_rect->raise_to_top();
                        
                } 
                break;          
        }
        
        if (event->button.x >= horizontal_adjustment.get_value() + canvas_width) {
-               start_canvas_autoscroll (1);
+               start_canvas_autoscroll (1, 0);
        }
        
        if (start != end) {
@@ -4468,23 +5268,32 @@ Editor::end_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
 {
        Location * newloc = 0;
        string rangename;
+       int flags;
        
        if (!drag_info.first_move) {
                drag_range_markerbar_op (item, event);
 
                switch (range_marker_op) {
                case CreateRangeMarker:
+               case CreateCDMarker:
                    {
                        begin_reversible_command (_("new range marker"));
-                        XMLNode &before = session->locations()->get_state();
+                       XMLNode &before = session->locations()->get_state();
                        session->locations()->next_available_name(rangename,"unnamed");
-                       newloc = new Location(temp_location->start(), temp_location->end(), rangename, Location::IsRangeMarker);
+                       if (range_marker_op == CreateCDMarker) {
+                               flags =  Location::IsRangeMarker|Location::IsCDMarker;
+                               cd_marker_bar_drag_rect->hide();
+                       }
+                       else {
+                               flags =  Location::IsRangeMarker;
+                               range_bar_drag_rect->hide();
+                       }
+                       newloc = new Location(temp_location->start(), temp_location->end(), rangename, (Location::Flags) flags);
                        session->locations()->add (newloc, true);
-                        XMLNode &after = session->locations()->get_state();
+                       XMLNode &after = session->locations()->get_state();
                        session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
                        commit_reversible_command ();
                        
-                       range_bar_drag_rect->hide();
                        range_marker_drag_rect->hide();
                        break;
                    }
@@ -4498,10 +5307,10 @@ Editor::end_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
        } else {
                /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
 
-               if (Keyboard::no_modifier_keys_pressed (&event->button)) {
+               if (Keyboard::no_modifier_keys_pressed (&event->button) && range_marker_op != CreateCDMarker) {
 
-                       nframes_t start;
-                       nframes_t end;
+                       nframes64_t start;
+                       nframes64_t end;
 
                        start = session->locations()->first_mark_before (drag_info.grab_frame);
                        end = session->locations()->first_mark_after (drag_info.grab_frame);
@@ -4517,7 +5326,7 @@ Editor::end_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
                        switch (mouse_mode) {
                        case MouseObject:
                                /* find the two markers on either side and then make the selection from it */
-                               select_all_within (start, end, 0.0f, FLT_MAX, Selection::Set);
+                               select_all_within (start, end, 0.0f, FLT_MAX, track_views, Selection::Set);
                                break;
 
                        case MouseRange:
@@ -4551,8 +5360,8 @@ Editor::start_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
 void
 Editor::drag_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
 {
-       nframes_t start;
-       nframes_t end;
+       nframes64_t start;
+       nframes64_t end;
 
        if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
                snap_to (drag_info.current_pointer_frame);
@@ -4612,7 +5421,7 @@ Editor::end_mouse_zoom (ArdourCanvas::Item* item, GdkEvent* event)
 }
 
 void
-Editor::reposition_zoom_rect (nframes_t start, nframes_t end)
+Editor::reposition_zoom_rect (nframes64_t start, nframes64_t end)
 {
        double x1 = frame_to_pixel (start);
        double x2 = frame_to_pixel (end);
@@ -4639,8 +5448,8 @@ Editor::start_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
 void
 Editor::drag_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
 {
-       nframes_t start;
-       nframes_t end;
+       nframes64_t start;
+       nframes64_t end;
        double y1;
        double y2;
 
@@ -4650,7 +5459,7 @@ Editor::drag_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
                return;
        }
 
-       if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
+       if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier()) && Config->get_rubberbanding_snaps_to_grid()) {
                if (drag_info.first_move) {
                        snap_to (drag_info.grab_frame);
                } 
@@ -4707,8 +5516,7 @@ Editor::end_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
                if (drag_info.current_pointer_y < drag_info.grab_y) {
                        y1 = drag_info.current_pointer_y;
                        y2 = drag_info.grab_y;
-               }
-               else {
+               } else {
                        y2 = drag_info.current_pointer_y;
                        y1 = drag_info.grab_y;
                }
@@ -4720,9 +5528,9 @@ Editor::end_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
                begin_reversible_command (_("rubberband selection"));
 
                if (drag_info.grab_frame < drag_info.last_pointer_frame) {
-                       commit = select_all_within (drag_info.grab_frame, drag_info.last_pointer_frame, y1, y2, op);
+                       commit = select_all_within (drag_info.grab_frame, drag_info.last_pointer_frame, y1, y2, track_views, op);
                } else {
-                       commit = select_all_within (drag_info.last_pointer_frame, drag_info.grab_frame, y1, y2, op);
+                       commit = select_all_within (drag_info.last_pointer_frame, drag_info.grab_frame, y1, y2, track_views, op);
                }               
 
                if (commit) {
@@ -4754,10 +5562,10 @@ Editor::mouse_rename_region (ArdourCanvas::Item* item, GdkEvent* event)
        prompter.show_all ();
        switch (prompter.run ()) {
        case Gtk::RESPONSE_ACCEPT:
-        string str;
+               string str;
                prompter.get_result(str);
                if (str.length()) {
-               clicked_regionview->region()->set_name (str);
+                       clicked_regionview->region()->set_name (str);
                }
                break;
        }
@@ -4807,26 +5615,40 @@ Editor::end_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
        if (drag_info.first_move) {
                return;
        }
+
+       if (drag_info.last_pointer_frame < clicked_regionview->region()->position()) {
+               /* backwards drag of the left edge - not usable */
+               return;
+       }
+       
+       nframes64_t newlen = drag_info.last_pointer_frame - clicked_regionview->region()->position();
+
+       float percentage = (double) newlen / (double) clicked_regionview->region()->length();
        
-       nframes_t newlen = drag_info.last_pointer_frame - clicked_regionview->region()->position();
-       float percentage = (float) ((double) newlen - (double) clicked_regionview->region()->length()) / ((double) newlen) * 100.0f;
+#ifndef USE_RUBBERBAND
+       // Soundtouch uses percentage / 100 instead of normal (/ 1) 
+       if (clicked_regionview->region()->data_type() == DataType::AUDIO) {
+               percentage = (float) ((double) newlen - (double) clicked_regionview->region()->length()) / ((double) newlen) * 100.0f;
+       }
+#endif 
        
        begin_reversible_command (_("timestretch"));
+       
+       // XXX how do timeFX on multiple regions ?
+       
+       RegionSelection rs;
+       rs.add (clicked_regionview);
 
-       if (run_timestretch (selection->regions, percentage) == 0) {
+       if (time_stretch (rs, percentage) == 0) {
                session->commit_reversible_command ();
        }
 }
 
 void
-Editor::mouse_brush_insert_region (RegionView* rv, nframes_t pos)
+Editor::mouse_brush_insert_region (RegionView* rv, nframes64_t pos)
 {
        /* no brushing without a useful snap setting */
 
-       // FIXME
-       AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
-       assert(arv);
-
        switch (snap_mode) {
        case SnapMagnetic:
                return; /* can't work because it allows region to be placed anywhere */
@@ -4835,9 +5657,7 @@ Editor::mouse_brush_insert_region (RegionView* rv, nframes_t pos)
        }
 
        switch (snap_type) {
-       case SnapToFrame:
        case SnapToMark:
-       case SnapToEditCursor:
                return;
 
        default:
@@ -4850,18 +5670,18 @@ Editor::mouse_brush_insert_region (RegionView* rv, nframes_t pos)
                return;
        }
 
-       RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*>(&arv->get_time_axis_view());
+       RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&rv->get_time_axis_view());
 
-       if (atv == 0 || !atv->is_audio_track()) {
+       if (rtv == 0 || !rtv->is_track()) {
                return;
        }
 
-       boost::shared_ptr<Playlist> playlist = atv->playlist();
-       double speed = atv->get_diskstream()->speed();
+       boost::shared_ptr<Playlist> playlist = rtv->playlist();
+       double speed = rtv->get_diskstream()->speed();
        
-        XMLNode &before = playlist->get_state();
-       playlist->add_region (boost::dynamic_pointer_cast<AudioRegion> (RegionFactory::create (arv->audio_region())), (nframes_t) (pos * speed));
-        XMLNode &after = playlist->get_state();
+       XMLNode &before = playlist->get_state();
+       playlist->add_region (RegionFactory::create (rv->region()), (nframes64_t) (pos * speed));
+       XMLNode &after = playlist->get_state();
        session->add_command(new MementoCommand<Playlist>(*playlist.get(), &before, &after));
        
        // playlist is frozen, so we have to update manually
@@ -4872,15 +5692,10 @@ Editor::mouse_brush_insert_region (RegionView* rv, nframes_t pos)
 gint
 Editor::track_height_step_timeout ()
 {
-       struct timeval now;
-       struct timeval delta;
-       
-       gettimeofday (&now, 0);
-       timersub (&now, &last_track_height_step_timestamp, &delta);
-       
-       if (delta.tv_sec * 1000000 + delta.tv_usec > 250000) { /* milliseconds */
+       if (get_microseconds() - last_track_height_step_timestamp < 250000) {
                current_stepping_trackview = 0;
                return false;
        }
        return true;
 }
+