merge from 2.0-ongoing by hand, minus key binding editor
[ardour.git] / gtk2_ardour / editor_mouse.cc
index 96b1ab591e9ce571a48a555e6ef41f86fb2af731..da95d50d95949e8088db8361d67a6d1850879f80 100644 (file)
@@ -39,6 +39,7 @@
 #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"
@@ -159,6 +160,12 @@ 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;
@@ -206,7 +213,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.
        */
@@ -243,12 +250,22 @@ Editor::set_mouse_mode (MouseMode m, bool force)
                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;
        }
 
+       if (mouse_mode == MouseNote)
+               midi_toolbar_frame.show();
+       else
+               midi_toolbar_frame.hide();
+
        ignore_mouse_mode_toggle = false;
 
        if (is_drawable()) {
-               track_canvas.get_window()->set_cursor(*current_canvas_cursor);
+               track_canvas.get_window()->set_cursor(*current_canvas_cursor);
        }
 }
 
@@ -285,6 +302,101 @@ 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::midi_edit_mode_toggled (MidiEditMode m)
+{
+       if (ignore_midi_edit_mode_toggle) {
+               return;
+       }
+
+       switch (m) {
+       case MidiEditSelect:
+               if (midi_tool_select_button.get_active()) {
+                       set_midi_edit_mode (m);
+               }
+               break;
+       
+       case MidiEditPencil:
+               if (midi_tool_pencil_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 MidiEditSelect:
+               midi_tool_select_button.set_active (true);
+               break;
+       
+       case MidiEditPencil:
+               midi_tool_pencil_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)
+{
+       switch (midi_edit_mode) {
+       case MidiEditSelect:
+               current_canvas_cursor = midi_select_cursor;
+               break;
+       case MidiEditPencil:
+               current_canvas_cursor = midi_pencil_cursor;
+               break;
+       case MidiEditErase:
+               current_canvas_cursor = midi_erase_cursor;
+               break;
        }
 }
 
@@ -337,8 +449,8 @@ Editor::button_selection (ArdourCanvas::Item* item, GdkEvent* event, ItemType it
                }
                break;
                
-       case RegionViewNameHighlight:
-       case RegionViewName:
+       case RegionViewNameHighlight:
+       case RegionViewName:
                if (mouse_mode != MouseRange) {
                        commit = set_selected_regionview_from_click (press, op, true);
                } else if (event->type == GDK_BUTTON_PRESS) {
@@ -346,6 +458,7 @@ Editor::button_selection (ArdourCanvas::Item* item, GdkEvent* event, ItemType it
                }
                break;
 
+
        case FadeInHandleItem:
        case FadeInItem:
        case FadeOutHandleItem:
@@ -356,10 +469,12 @@ Editor::button_selection (ArdourCanvas::Item* item, GdkEvent* event, ItemType it
                        commit = set_selected_track_from_click (press, op, false);
                }
                break;
-               
-       case GainAutomationControlPointItem:
-       case PanAutomationControlPointItem:
-       case RedirectAutomationControlPointItem:
+
+       case CrossfadeViewItem:
+               commit = set_selected_track_from_click (press, op, false);
+               break;
+                       
+       case ControlPointItem:
                commit = set_selected_track_from_click (press, op, true);
                if (mouse_mode != MouseRange) {
                        commit |= set_selected_control_point_from_click (op, false);
@@ -546,16 +661,12 @@ Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemTyp
                                                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;
@@ -565,7 +676,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) ;
@@ -582,16 +693,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:
                                        
@@ -610,17 +718,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;
@@ -632,15 +734,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;
 
@@ -671,7 +769,12 @@ Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemTyp
                        break;
 
                case MouseAudition:
-                       /* handled in release */
+                       _scrubbing = true;
+                       last_scrub_frame = 0;
+                       last_scrub_time = 0;
+                       have_full_mouse_speed = false;
+                       memset (mouse_speed, 0, sizeof (double) * mouse_speed_size);
+                       /* rest handled in motion & release */
                        break;
 
                default:
@@ -692,9 +795,7 @@ Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemTyp
                                        }
                                        
                                        break;
-                               case GainAutomationControlPointItem:
-                               case PanAutomationControlPointItem:
-                               case RedirectAutomationControlPointItem:
+                               case ControlPointItem:
                                        start_control_point_grab (item, event);
                                        return true;
                                        break;
@@ -825,21 +926,22 @@ Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemT
                                break;
 
                        case StreamItem:
-                               popup_track_context_menu (1, event->button.time, item_type, false, where);
+                               popup_track_context_menu (1, event->button.time, where);
                                break;
                                
                        case RegionItem:
                        case RegionViewNameHighlight:
                        case RegionViewName:
-                               popup_track_context_menu (1, event->button.time, item_type, false, where);
+                               popup_track_context_menu (1, event->button.time, where);
                                break;
                                
                        case SelectionItem:
-                               popup_track_context_menu (1, event->button.time, item_type, true, where);
+                               popup_track_context_menu (1, event->button.time, where);
                                break;
 
                        case AutomationTrackItem:
-                               popup_track_context_menu (1, event->button.time, item_type, false, where);
+                       case CrossfadeViewItem:
+                               popup_track_context_menu (1, event->button.time, where);
                                break;
 
                        case MarkerBarItem: 
@@ -862,11 +964,7 @@ Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemT
                                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 ;
@@ -879,8 +977,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;
@@ -913,18 +1010,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;
                }
@@ -940,9 +1033,7 @@ Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemT
                case PlayheadCursorItem:
                case MarkerItem:
                case GainLineItem:
-               case GainAutomationLineItem:
-               case PanAutomationLineItem:
-               case RedirectAutomationLineItem:
+               case AutomationLineItem:
                case StartSelectionTrimItem:
                case EndSelectionTrimItem:
                        return true;
@@ -1011,13 +1102,20 @@ 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;
+                       if (last_scrub_frame == 0) {
+                               /* no drag, just a click */
+                               switch (item_type) {
+                               case RegionItem:
+                                       audition_selected_region ();
+                                       break;
+                               default:
+                                       break;
+                               }
+                       } else {
+                               /* make sure we stop */
+                               session->request_transport_speed (0.0);
+                       }
                        break;
 
                default:
@@ -1032,25 +1130,6 @@ Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemT
        case 2:
                switch (mouse_mode) {
                        
-               case MouseObject:
-                       switch (item_type) {
-                       case RegionItem:
-                               if (Keyboard::modifier_state_equals (event->button.state, Keyboard::Shift)) {
-                                       raise_region ();
-                               } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::Shift|Keyboard::Alt))) {
-                                       lower_region ();
-                               } else {
-                                       // Button2 click is unused
-                               }
-                               return true;
-                               
-                               break;
-                               
-                       default:
-                               break;
-                       }
-                       break;
-                       
                case MouseRange:
                        
                        // x_style_paste (where, 1.0);
@@ -1080,32 +1159,7 @@ Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_
        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;
-
-       case GainAutomationControlPointItem:
-       case PanAutomationControlPointItem:
-       case RedirectAutomationControlPointItem:
+       case ControlPointItem:
                if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
                        cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
                        cp->set_visible (true);
@@ -1113,13 +1167,13 @@ 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);
+                       cp->item()->i2w (at_x, at_y);
                        at_x += 20.0;
                        at_y += 20.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);
+                       set_verbose_canvas_cursor (cp->line().get_verbose_cursor_string (fraction), at_x, at_y);
                        show_verbose_canvas_cursor ();
 
                        if (is_drawable()) {
@@ -1132,21 +1186,19 @@ Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_
                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);
                        }
                }
                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);
@@ -1162,12 +1214,13 @@ Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_
 
        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);
@@ -1195,7 +1248,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;
@@ -1232,7 +1285,7 @@ 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]);
+               marker->set_color_rgba (ARDOUR_UI::config()->canvasvar_EnteredMarker.get());
                // fall through
        case MeterMarkerItem:
        case TempoMarkerItem:
@@ -1260,13 +1313,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;
@@ -1294,13 +1342,10 @@ 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);
                        }
                }
@@ -1317,21 +1362,21 @@ Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_
        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);
                }
                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);
@@ -1415,6 +1460,12 @@ Editor::left_automation_track ()
        return false;
 }
 
+static gboolean
+_update_mouse_speed (void *arg)
+{
+       return static_cast<Editor*>(arg)->update_mouse_speed ();
+}
+
 bool
 Editor::motion_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type, bool from_autoscroll)
 {
@@ -1446,14 +1497,74 @@ Editor::motion_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item
        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) {
+                       struct timeval tmnow;
+
+                       if (last_scrub_frame == 0) {
+
+                               /* first motion, just set up the variables */
+
+                               last_scrub_frame = (nframes64_t) drag_info.current_pointer_frame;
+                               gettimeofday (&tmnow, 0);
+                               last_scrub_time = tmnow.tv_sec * 1000000.0 + tmnow.tv_usec;
+                               session->request_locate (last_scrub_frame, true);
+
+                       } else {
+                               /* how fast is the mouse moving ? */
+
+                               double speed;
+                               nframes_t distance;
+                               double time;
+                               double dir;
+
+#if 1
+                               if (last_scrub_frame < (nframes64_t) drag_info.current_pointer_frame) {
+                                       distance = (nframes64_t) drag_info.current_pointer_frame - last_scrub_frame;
+                                       dir = 1.0;
+                               } else {
+                                       distance = last_scrub_frame - (nframes64_t) drag_info.current_pointer_frame;
+                                       dir = -1.0;
+                               }
+#else
+                               if (drag_info.grab_x < drag_info.current_pointer_x) {
+                                       distance = drag_info.current_pointer_x - drag_info.grab_x;
+                                       dir = -1.0;
+                               } else {
+                                       distance = drag_info.grab_x - drag_info.current_pointer_x;
+                                       dir = 1.0;
+                               }
+#endif
+
+                               gettimeofday (&tmnow, 0);
+                               time = (tmnow.tv_sec * 1000000.0 + tmnow.tv_usec) - last_scrub_time;
+                               last_scrub_frame = drag_info.current_pointer_frame;
+                               last_scrub_time = (tmnow.tv_sec * 1000000.0) + tmnow.tv_usec;
+                               speed = ((double)distance/session->frame_rate()) / (time/1000000.0); // frames/sec
+
+                               add_mouse_speed (speed, dir);
+
+                               if (mouse_speed_update < 0) {
+                                       mouse_speed_update = g_timeout_add (10, _update_mouse_speed, this);
+                                       update_mouse_speed ();
+                               }
+                       }
+               }
+
+       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 =  (abs ((nframes64_t) (drag_info.current_pointer_x - drag_info.grab_x)) > 4LL);
+                       bool y_threshold_passed =  (abs ((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
@@ -1472,10 +1583,7 @@ Editor::motion_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item
        case PlayheadCursorItem:
        case EditCursorItem:
        case MarkerItem:
-       case GainControlPointItem:
-       case RedirectAutomationControlPointItem:
-       case GainAutomationControlPointItem:
-       case PanAutomationControlPointItem:
+       case ControlPointItem:
        case TempoMarkerItem:
        case MeterMarkerItem:
        case RegionViewNameHighlight:
@@ -1483,17 +1591,17 @@ 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) {
@@ -1550,7 +1658,7 @@ Editor::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
                cursor = 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)) {
@@ -1565,7 +1673,7 @@ 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;
@@ -1699,7 +1807,7 @@ 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 - ((nframes_t) arv->audio_region()->fade_in()->back()->when + arv->region()->position()); 
 }
 
 void
@@ -1709,7 +1817,7 @@ Editor::fade_in_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
        nframes_t pos;
        nframes_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 {
@@ -1754,7 +1862,7 @@ Editor::fade_in_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* even
 
        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;
@@ -1778,13 +1886,13 @@ 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);
                
-               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 ();
@@ -1806,7 +1914,7 @@ 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() - (nframes_t) arv->audio_region()->fade_out()->back()->when + arv->region()->position());      
 }
 
 void
@@ -1816,10 +1924,9 @@ Editor::fade_out_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event
        nframes_t pos;
        nframes_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;
        }
 
@@ -1864,7 +1971,7 @@ Editor::fade_out_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* eve
        nframes_t pos;
        nframes_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 {
@@ -1895,13 +2002,13 @@ 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);
 
-               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 ();
@@ -1946,7 +2053,7 @@ Editor::cursor_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
        Cursor* cursor = (Cursor *) drag_info.data;
        nframes_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 {
@@ -2001,7 +2108,7 @@ 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;
        }
@@ -2062,10 +2169,9 @@ Editor::marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
 
 
        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;
        }
 
@@ -2221,7 +2327,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();
@@ -2243,7 +2349,7 @@ Editor::meter_marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* e
        MeterMarker* marker = (MeterMarker *) drag_info.data;
        nframes_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 {
@@ -2280,12 +2386,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;
@@ -2294,7 +2400,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 ();
        }
 }
@@ -2352,7 +2458,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();
@@ -2374,7 +2480,7 @@ Editor::tempo_marker_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* e
        TempoMarker* marker = (TempoMarker *) drag_info.data;
        nframes_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 {
@@ -2423,10 +2529,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 ();
        }
 }
@@ -2442,16 +2548,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;
 
@@ -2460,14 +2566,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*/
@@ -2480,10 +2586,10 @@ 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);
+       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), 
+       double fraction = 1.0 - ((control_point->get_y() - control_point->line().y_position()) / (double)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);
 
        show_verbose_canvas_cursor ();
@@ -2507,11 +2613,11 @@ Editor::control_point_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent*
                cy = drag_info.grab_y;
        }
 
-       cp->line.parent_group().w2i (cx, cy);
+       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().y_position() + cp->line().height()), cy);
 
        //translate cx to frames
        nframes_t cx_frames = unit_to_frame (cx);
@@ -2520,8 +2626,8 @@ Editor::control_point_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent*
                snap_to (cx_frames);
        }
 
-       float fraction = 1.0 - (cy / cp->line.height());
-       
+       const double fraction = 1.0 - ((cy - cp->line().y_position()) / (double)cp->line().height());
+
        bool push;
 
        if (Keyboard::modifier_state_contains (event->button.state, Keyboard::Control)) {
@@ -2530,9 +2636,9 @@ Editor::control_point_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent*
                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;
 }
@@ -2553,7 +2659,7 @@ Editor::control_point_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent
        } else {
                control_point_drag_motion_callback (item, event);
        }
-       cp->line.end_drag (cp);
+       cp->line().end_drag (cp);
 }
 
 void
@@ -2611,7 +2717,7 @@ Editor::start_line_grab (AutomationLine* line, GdkEvent* event)
 
        start_grab (event, fader_cursor);
 
-       double fraction = 1.0 - (cy / line->height());
+       const double fraction = 1.0 - ((cy - line->y_position()) / (double)line->height());
 
        line->start_drag (0, drag_info.grab_frame, fraction);
        
@@ -2629,8 +2735,7 @@ Editor::line_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
 
        line->parent_group().w2i (cx, cy);
        
-       double fraction;
-       fraction = 1.0 - (cy / line->height());
+       const double fraction = 1.0 - ((cy - line->y_position()) / (double)line->height());
 
        bool push;
 
@@ -2829,7 +2934,7 @@ Editor::region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
        original_pointer_order = drag_info.last_trackview->order;
                
        /************************************************************
-                 Y-Delta Computation
+            Y-Delta Computation
        ************************************************************/   
 
        if (drag_info.brushing) {
@@ -2849,7 +2954,7 @@ Editor::region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
                        TimeAxisView *tracklist_timeview;
                        tracklist_timeview = (*i);
                        RouteTimeAxisView* rtv2 = dynamic_cast<RouteTimeAxisView*>(tracklist_timeview);
-                       list<TimeAxisView*> children_list;
+                       TimeAxisView::Children children_list;
              
                        /* zeroes are audio tracks. ones are other types. */
              
@@ -2869,7 +2974,7 @@ Editor::region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
                                height_list[rtv2->order] = (*i)->height;
                                children = 1;
                                if ((children_list = rtv2->get_child_list()).size() > 0) {
-                                       for (list<TimeAxisView*>::iterator j = children_list.begin(); j != children_list.end(); ++j) { 
+                                       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)->height;                
                                                numtracks++;
@@ -2984,7 +3089,7 @@ Editor::region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
        }
          
        /************************************************************
-                       X DELTA COMPUTATION
+           X DELTA COMPUTATION
        ************************************************************/
 
        /* compute the amount of pointer motion in frames, and where
@@ -2993,14 +3098,14 @@ Editor::region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
 
        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;
                        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);
 
@@ -3032,13 +3137,13 @@ Editor::region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
                        /* now compute the canvas unit distance we need to move the regiondrag_info.last_trackview->order
                           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);
                        }
-           
+
                        drag_info.last_frame_position = pending_region_position;
            
                } else {
@@ -3052,7 +3157,7 @@ Editor::region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
        }
 
        /*************************************************************
-                        PREPARE TO MOVE
+           PREPARE TO MOVE
        ************************************************************/
 
        if (x_delta == 0 && (pointer_y_span == 0)) {
@@ -3062,6 +3167,7 @@ Editor::region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
                return;
        } 
 
+
        if (x_delta < 0) {
                for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
 
@@ -3081,7 +3187,7 @@ Editor::region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
        }
 
        /*************************************************************
-                        MOTION                                                               
+           MOTION                                                                    
        ************************************************************/
 
        bool do_move;
@@ -3161,7 +3267,7 @@ Editor::region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
                  
                                                tvp2 = trackview_by_y_position (iy1 + y_delta);
                                                temp_rtv = dynamic_cast<RouteTimeAxisView*>(tvp2);
-                                               rv->set_height (temp_rtv->height);
+                                               rv->set_y_position_and_height (0, temp_rtv->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.
@@ -3184,10 +3290,11 @@ Editor::region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
                                if (-x_delta > ix1) {
                                        x_delta = -ix1;
                                }
-                       } else if ((x_delta > 0) &&(rv->region()->last_frame() > max_frames - x_delta)) {
+                       } 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 */
@@ -3320,7 +3427,7 @@ Editor::region_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event
 
                        boost::shared_ptr<Playlist> from_playlist = rv->region()->playlist();
                        boost::shared_ptr<Playlist> to_playlist = rtv2->playlist();
-           
+
                        where = (nframes_t) (unit_to_frame (ix1) * speed);
                        boost::shared_ptr<Region> new_region (RegionFactory::create (rv->region()));
 
@@ -3408,10 +3515,9 @@ Editor::region_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event
 
                        rv = (*i);
 
-                       if (rv->region()->locked()) {
+                       if (!rv->region()->can_move()) {
                                continue;
                        }
-                       
 
                        if (regionview_x_movement) {
                                double ownspeed = 1.0;
@@ -3446,16 +3552,16 @@ Editor::region_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event
 
                                boost::shared_ptr<Region> newregion;
                                boost::shared_ptr<Region> ar;
+                               boost::shared_ptr<Region> mr;
 
                                if ((ar = boost::dynamic_pointer_cast<AudioRegion>(rv->region())) != 0) {
                                        newregion = RegionFactory::create (ar);
-                               } else {
-                                       /* XXX MIDI HERE drobilla */
-                                       continue;
+                               } else if ((mr = boost::dynamic_pointer_cast<MidiRegion>(rv->region())) != 0) {
+                                       newregion = RegionFactory::create (mr);
                                }
 
                                /* add it */
-                               
+
                                latest_regionview = 0;
                                sigc::connection c = rtv->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
                                to_playlist->add_region (newregion, (nframes_t) (where * rtv->get_diskstream()->speed()));
@@ -3465,11 +3571,11 @@ Editor::region_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event
                                        rtv->reveal_dependent_views (*latest_regionview);
                                        selection->add (latest_regionview);
                                }
-                               
+
                                /* if the original region was locked, we don't care for the new one */
                                
                                newregion->set_locked (false);                  
-
+                               
                        } else {
 
                                /* just change the model */
@@ -3732,7 +3838,7 @@ 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"));
@@ -3807,10 +3913,9 @@ Editor::drag_selection (ArdourCanvas::Item* item, GdkEvent* event)
        nframes_t length;
        nframes_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;
        }
        
@@ -4066,7 +4171,7 @@ 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));
                        }
                }
        }
@@ -4081,7 +4186,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);
                        }
@@ -4257,8 +4362,8 @@ 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()));
-                }
+                       session->add_command (new MementoCommand<Playlist>(*(*p).get(), 0, &(*p)->get_state()));
+               }
                
                motion_frozen_playlists.clear ();
 
@@ -4292,10 +4397,10 @@ Editor::point_trim (GdkEvent* event)
                        {
                                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));
                                }
                        }
 
@@ -4305,7 +4410,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));
                        }
                }
@@ -4336,7 +4441,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));
                        }
                }
@@ -4359,7 +4464,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);
@@ -4502,11 +4607,11 @@ Editor::end_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
                case CreateRangeMarker:
                    {
                        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);
                        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 ();
                        
@@ -4780,7 +4885,7 @@ 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);
@@ -4833,6 +4938,11 @@ 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;
+       }
        
        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;
@@ -4885,9 +4995,9 @@ Editor::mouse_brush_insert_region (RegionView* rv, nframes_t pos)
        boost::shared_ptr<Playlist> playlist = rtv->playlist();
        double speed = rtv->get_diskstream()->speed();
        
-        XMLNode &before = playlist->get_state();
+       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 &after = playlist->get_state();
        session->add_command(new MementoCommand<Playlist>(*playlist.get(), &before, &after));
        
        // playlist is frozen, so we have to update manually
@@ -4910,3 +5020,91 @@ Editor::track_height_step_timeout ()
        }
        return true;
 }
+
+
+void
+Editor::add_mouse_speed (double speed, double dir)
+{
+       size_t index;
+
+       mouse_direction = dir;
+
+       index = mouse_speed_entries;
+
+       if (++index >= mouse_speed_size) {
+               index = 0;
+               have_full_mouse_speed = true;
+       }
+       
+       mouse_speed[index] = speed;
+       mouse_speed_entries = index;
+}
+
+double
+Editor::compute_mouse_speed ()
+{
+       double total = 0;
+
+       if (!have_full_mouse_speed) {
+
+               /* partial speed buffer, just use whatever we have so far */
+
+               if (mouse_speed_entries == 0 ) {
+                       return 0.0;
+               }
+
+               for (size_t n = 0; n < mouse_speed_entries; ++n) {
+                       total += mouse_speed[n];
+               }
+               
+               return mouse_direction * total/mouse_speed_entries;
+       }
+
+       /* compute the average (effectively low-pass filtering) mouse speed
+          across the entire buffer.
+       */
+
+       for (size_t n = 0; n < mouse_speed_size; ++n) {
+               total += mouse_speed[n];
+       }
+
+
+       return mouse_direction * total/mouse_speed_size;
+}
+
+bool
+Editor::update_mouse_speed ()
+{
+       double speed;
+
+       if (!_scrubbing) {
+               session->request_transport_speed (0.0);
+               mouse_speed_update = -1;
+               return false;
+       }
+
+       speed = compute_mouse_speed ();
+
+       struct timeval tmnow;
+       
+       gettimeofday (&tmnow, 0);
+       double now = (tmnow.tv_sec * 1000000.0) + tmnow.tv_usec;
+
+       if (now - last_scrub_time > 250000) {
+           
+               // 0.25 seconds since last mouse motion, start to brake
+
+               if (fabs (speed) < 0.1) {
+                       /* don't asymptotically approach zero */
+                       memset (mouse_speed, 0, sizeof (double) * mouse_speed_size);
+                       speed = 0.0;
+               } else if (fabs (speed) < 0.25) {
+                       add_mouse_speed (fabs (speed * 0.2), mouse_direction);
+               } else {
+                       add_mouse_speed (fabs (speed * 0.6), mouse_direction);
+               }
+       } 
+
+       session->request_transport_speed (speed);
+       return _scrubbing;
+}