HIG-ify the locations UI a bit. Should fix #3526.
[ardour.git] / gtk2_ardour / editor_mouse.cc
index 68d92300281d80ea762ea34c693daa832191014f..f2d49fa1d711ce812f303b97bb75a697b2174777 100644 (file)
@@ -35,6 +35,7 @@
 
 #include "ardour_ui.h"
 #include "actions.h"
+#include "canvas-note.h"
 #include "editor.h"
 #include "time_axis_view.h"
 #include "audio_time_axis.h"
@@ -53,6 +54,8 @@
 #include "rgb_macros.h"
 #include "control_point_dialog.h"
 #include "editor_drag.h"
+#include "automation_region_view.h"
+#include "edit_note_dialog.h"
 
 #include "ardour/types.h"
 #include "ardour/profile.h"
@@ -82,7 +85,7 @@ using namespace Editing;
 using Gtkmm2ext::Keyboard;
 
 bool
-Editor::mouse_frame (nframes64_t& where, bool& in_track_canvas) const
+Editor::mouse_frame (framepos_t& where, bool& in_track_canvas) const
 {
        int x, y;
        double wx, wy;
@@ -115,7 +118,7 @@ Editor::mouse_frame (nframes64_t& where, bool& in_track_canvas) const
        return true;
 }
 
-nframes64_t
+framepos_t
 Editor::event_frame (GdkEvent const * event, double* pcx, double* pcy) const
 {
        double cx, cy;
@@ -177,7 +180,7 @@ Editor::which_grabber_cursor ()
                        break;
 
                case MouseObject:
-                       c = grabber_cursor;
+                       c = grabber_note_cursor;
                        break;
 
                case MouseTimeFX:
@@ -195,6 +198,10 @@ Editor::which_grabber_cursor ()
                        c = grabber_edit_point_cursor;
                        break;
                default:
+                        boost::shared_ptr<Movable> m = _movable.lock();
+                        if (m && m->locked()) {
+                                c = speaker_cursor;
+                        } 
                        break;
                }
        }
@@ -202,6 +209,29 @@ Editor::which_grabber_cursor ()
        return c;
 }
 
+void
+Editor::set_current_trimmable (boost::shared_ptr<Trimmable> t)
+{
+        boost::shared_ptr<Trimmable> st = _trimmable.lock();
+
+        if (!st || st == t) {
+                _trimmable = t;
+                set_canvas_cursor ();
+        } 
+        
+}
+
+void
+Editor::set_current_movable (boost::shared_ptr<Movable> m)
+{
+        boost::shared_ptr<Movable> sm = _movable.lock();
+
+        if (!sm || sm != m) {
+                _movable = m;
+                set_canvas_cursor ();
+        }
+}
+
 void
 Editor::set_canvas_cursor ()
 {
@@ -240,7 +270,11 @@ Editor::set_canvas_cursor ()
                        break;
 
                case MouseZoom:
-                       current_canvas_cursor = zoom_cursor;
+                       if (Keyboard::the_keyboard().key_is_down (GDK_Control_L)) {
+                               current_canvas_cursor = zoom_out_cursor;
+                       } else {
+                               current_canvas_cursor = zoom_in_cursor;
+                       }
                        break;
 
                case MouseTimeFX:
@@ -264,9 +298,17 @@ Editor::set_canvas_cursor ()
                break;
        }
 
-       if (is_drawable()) {
-               track_canvas->get_window()->set_cursor(*current_canvas_cursor);
+       /* up-down cursor as a cue that automation can be dragged up and down when in join object/range mode */
+       if (join_object_range_button.get_active() && last_item_entered) {
+               if (last_item_entered->property_parent() && (*last_item_entered->property_parent()).get_data (X_("timeselection"))) {
+                       pair<TimeAxisView*, int> tvp = trackview_by_y_position (_last_motion_y + vertical_adjustment.get_value() - canvas_timebars_vsize);
+                       if (dynamic_cast<AutomationTimeAxisView*> (tvp.first)) {
+                               current_canvas_cursor = up_down_cursor;
+                       }
+               }
        }
+
+        set_canvas_cursor (current_canvas_cursor, true);
 }
 
 void
@@ -325,8 +367,6 @@ Editor::mouse_mode_toggled (MouseMode m)
 
        instant_save ();
         
-        cerr << "Mouse mode toggled to " << m << endl;
-
         if (!internal_editing()) {
                 if (mouse_mode != MouseRange && _join_object_range_state == JOIN_OBJECT_RANGE_NONE) {
                         
@@ -427,6 +467,10 @@ Editor::button_selection (ArdourCanvas::Item* /*item*/, GdkEvent* event, ItemTyp
           region alignment.
 
           note: not dbl-click or triple-click
+
+          Also note that there is no region selection in internal edit mode, otherwise
+          for operations operating on the selection (e.g. cut) it is not obvious whether
+          to cut notes or regions.
        */
 
        if (((mouse_mode != MouseObject) &&
@@ -435,8 +479,8 @@ Editor::button_selection (ArdourCanvas::Item* /*item*/, GdkEvent* event, ItemTyp
             (mouse_mode != MouseTimeFX || item_type != RegionItem) &&
             (mouse_mode != MouseGain) &&
             (mouse_mode != MouseRange)) ||
-
-           ((event->type != GDK_BUTTON_PRESS && event->type != GDK_BUTTON_RELEASE) || event->button.button > 3)) {
+           ((event->type != GDK_BUTTON_PRESS && event->type != GDK_BUTTON_RELEASE) || event->button.button > 3) ||
+           internal_editing()) {
 
                return;
        }
@@ -460,26 +504,25 @@ Editor::button_selection (ArdourCanvas::Item* /*item*/, GdkEvent* event, ItemTyp
 
        switch (item_type) {
        case RegionItem:
-               if (mouse_mode != MouseRange || internal_editing() || _join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
+               if (mouse_mode != MouseRange || _join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
                        set_selected_regionview_from_click (press, op, true);
                } else if (event->type == GDK_BUTTON_PRESS) {
                        selection->clear_tracks ();
-                       set_selected_track_as_side_effect (true);
+                       set_selected_track_as_side_effect (op, true);
                }
                if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT && !selection->regions.empty()) {
                        clicked_selection = select_range_around_region (selection->regions.front());
                }
-                       
                break;
 
        case RegionViewNameHighlight:
        case RegionViewName:
         case LeftFrameHandle:
         case RightFrameHandle:
-               if (mouse_mode != MouseRange || internal_editing() || _join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
+               if (mouse_mode != MouseRange || _join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
                        set_selected_regionview_from_click (press, op, true);
                } else if (event->type == GDK_BUTTON_PRESS) {
-                       set_selected_track_as_side_effect ();
+                       set_selected_track_as_side_effect (op);
                }
                break;
 
@@ -491,12 +534,12 @@ Editor::button_selection (ArdourCanvas::Item* /*item*/, GdkEvent* event, ItemTyp
                if (mouse_mode != MouseRange || _join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
                        set_selected_regionview_from_click (press, op, true);
                } else if (event->type == GDK_BUTTON_PRESS) {
-                       set_selected_track_as_side_effect ();
+                       set_selected_track_as_side_effect (op);
                }
                break;
 
        case ControlPointItem:
-               set_selected_track_as_side_effect ();
+               set_selected_track_as_side_effect (op, true);
                if (mouse_mode != MouseRange || _join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
                        set_selected_control_point_from_click (op, false);
                }
@@ -506,12 +549,12 @@ Editor::button_selection (ArdourCanvas::Item* /*item*/, GdkEvent* event, ItemTyp
                /* for context click, select track */
                if (event->button.button == 3) {
                        selection->clear_tracks ();
-                       set_selected_track_as_side_effect (true);
+                       set_selected_track_as_side_effect (op, true);
                }
                break;
 
        case AutomationTrackItem:
-               set_selected_track_as_side_effect (true);
+               set_selected_track_as_side_effect (op, true);
                break;
 
        default:
@@ -522,10 +565,6 @@ Editor::button_selection (ArdourCanvas::Item* /*item*/, GdkEvent* event, ItemTyp
 bool
 Editor::button_press_handler_1 (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
 {
-       if (_drags->active ()) {
-               _drags->abort ();
-       }
-
        /* single mouse clicks on any of these item types operate
           independent of mouse mode, mostly because they are
           not on the main track canvas or because we want
@@ -655,7 +694,7 @@ Editor::button_press_handler_1 (ArdourCanvas::Item* item, GdkEvent* event, ItemT
                                        AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (tvp.first);
                                        if (join_object_range_button.get_active() && atv) {
                                                /* smart "join" mode: drag automation */
-                                               _drags->set (new AutomationRangeDrag (this, atv->base_item(), selection->time), event);
+                                               _drags->set (new AutomationRangeDrag (this, atv->base_item(), selection->time), event, up_down_cursor);
                                        } else {
                                                /* this was debated, but decided the more common action was to
                                                   make a new selection */
@@ -668,14 +707,22 @@ Editor::button_press_handler_1 (ArdourCanvas::Item* item, GdkEvent* event, ItemT
                 case NoteItem:
                         if (internal_editing()) {
                                 /* trim notes if we're in internal edit mode and near the ends of the note */
-                                _drags->set (new NoteResizeDrag (this, item), event);
+                                ArdourCanvas::CanvasNote* cn = dynamic_cast<ArdourCanvas::CanvasNote*> (item);
+                                cerr << "NoteItem button press, cursor = " << current_canvas_cursor << endl;
+                                if (cn->mouse_near_ends()) {
+                                        _drags->set (new NoteResizeDrag (this, item), event, current_canvas_cursor);
+                                } else {
+                                        _drags->set (new NoteDrag (this, item), event);
+                                }
                         }
                        return true;
 
                 case StreamItem:
                         if (internal_editing()) {
-                                _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
-                                return true;
+                               if (dynamic_cast<MidiTimeAxisView*> (clicked_axisview)) {
+                                       _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
+                                       return true;
+                               }
                         } else {
                                _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
                                return true;
@@ -685,12 +732,12 @@ Editor::button_press_handler_1 (ArdourCanvas::Item* item, GdkEvent* event, ItemT
                 case RegionViewNameHighlight:
                 case LeftFrameHandle:
                 case RightFrameHandle:
-                {
-                        RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.property_id);
-                        _drags->set (new TrimDrag (this, item, clicked_regionview, s.by_layer()), event);
-                        return true;
+                        if (!clicked_regionview->region()->locked()) {
+                                RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.property_id);
+                                _drags->set (new TrimDrag (this, item, clicked_regionview, s.by_layer()), event);
+                                return true;
+                        }
                         break;
-                }
 
                default:
                         if (!internal_editing()) {
@@ -704,7 +751,12 @@ Editor::button_press_handler_1 (ArdourCanvas::Item* item, GdkEvent* event, ItemT
                switch (item_type) {
                case NoteItem:
                        if (internal_editing()) {
-                               _drags->set (new NoteDrag (this, item), event);
+                                ArdourCanvas::CanvasNote* cn = dynamic_cast<ArdourCanvas::CanvasNote*> (item);
+                                if (cn->mouse_near_ends()) {
+                                        _drags->set (new NoteResizeDrag (this, item), event, current_canvas_cursor);
+                                } else {
+                                        _drags->set (new NoteDrag (this, item), event);
+                                }
                                return true;
                        }
                        break;
@@ -748,6 +800,19 @@ Editor::button_press_handler_1 (ArdourCanvas::Item* item, GdkEvent* event, ItemT
                        }
 
                        case RegionItem:
+                               if (dynamic_cast<AutomationRegionView*> (clicked_regionview)) {
+                                       /* click on an automation region view; do nothing here and let the ARV's signal handler
+                                          sort it out.
+                                       */
+                                       break;
+                               }
+
+                               if (internal_editing ()) {
+                                       /* no region drags in internal edit mode */
+                                       break;
+                               }
+                               
+                               /* click on a normal region view */
                                if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
                                        add_region_copy_drag (item, event, clicked_regionview);
                                } 
@@ -756,7 +821,7 @@ Editor::button_press_handler_1 (ArdourCanvas::Item* item, GdkEvent* event, ItemT
                                } else {
                                        add_region_drag (item, event, clicked_regionview);
                                }
-
+                               
                                if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT && !selection->regions.empty()) {
                                        _drags->add (new SelectionDrag (this, clicked_axisview->get_selection_rect (clicked_selection)->rect, SelectionDrag::SelectionMove));
                                }
@@ -767,12 +832,12 @@ Editor::button_press_handler_1 (ArdourCanvas::Item* item, GdkEvent* event, ItemT
                        case RegionViewNameHighlight:
                        case LeftFrameHandle:
                         case RightFrameHandle:
-                       {
-                               RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.property_id);
-                               _drags->set (new TrimDrag (this, item, clicked_regionview, s.by_layer()), event);
-                               return true;
+                               if (!internal_editing () && !clicked_regionview->region()->locked()) {
+                                       RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.property_id);
+                                       _drags->set (new TrimDrag (this, item, clicked_regionview, s.by_layer()), event);
+                                       return true;
+                               }
                                break;
-                       }
 
                        case RegionViewName:
                        {
@@ -795,7 +860,9 @@ Editor::button_press_handler_1 (ArdourCanvas::Item* item, GdkEvent* event, ItemT
 
                        case StreamItem:
                                if (internal_editing()) {
-                                       _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
+                                       if (dynamic_cast<MidiTimeAxisView*> (clicked_axisview)) {
+                                               _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
+                                       }
                                        return true;
                                } else {
                                        _drags->set (new RubberbandSelectDrag (this, item), event);
@@ -817,7 +884,7 @@ Editor::button_press_handler_1 (ArdourCanvas::Item* item, GdkEvent* event, ItemT
                                                /* if we're over an automation track, start a drag of its data */
                                                AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (tvp.first);
                                                if (atv) {
-                                                       _drags->set (new AutomationRangeDrag (this, atv->base_item(), selection->time), event);
+                                                       _drags->set (new AutomationRangeDrag (this, atv->base_item(), selection->time), event, up_down_cursor);
                                                }
 
                                                /* if we're over a track and a region, and in the `object' part of a region,
@@ -940,7 +1007,7 @@ Editor::button_press_handler_1 (ArdourCanvas::Item* item, GdkEvent* event, ItemT
        case MouseTimeFX:
                if (internal_editing() && item_type == NoteItem) {
                        /* drag notes if we're in internal edit mode */
-                       _drags->set (new NoteResizeDrag (this, item), event);
+                       _drags->set (new NoteResizeDrag (this, item), event, current_canvas_cursor);
                        return true;
                } else if ((!internal_editing() || dynamic_cast<AudioRegionView*> (clicked_regionview)) && clicked_regionview) {
                        /* do time-FX if we're not in internal edit mode, or we are but we clicked on an audio region */
@@ -955,7 +1022,7 @@ Editor::button_press_handler_1 (ArdourCanvas::Item* item, GdkEvent* event, ItemT
                scrub_reverse_distance = 0;
                last_scrub_x = event->button.x;
                scrubbing_direction = 0;
-               track_canvas->get_window()->set_cursor (*transparent_cursor);
+               set_canvas_cursor (transparent_cursor);
                return true;
                break;
 
@@ -995,7 +1062,9 @@ Editor::button_press_handler_2 (ArdourCanvas::Item* item, GdkEvent* event, ItemT
                case RegionViewNameHighlight:
                 case LeftFrameHandle:
                 case RightFrameHandle:
-                       _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
+                       if (!internal_editing ()) {
+                               _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
+                       }
                        return true;
                        break;
 
@@ -1054,6 +1123,8 @@ Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemTyp
                }
        }
 
+        pre_press_cursor = current_canvas_cursor;
+
        track_canvas->grab_focus();
 
        if (_session && _session->actively_recording()) {
@@ -1094,16 +1165,21 @@ Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemTyp
 bool
 Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
 {
-       nframes64_t where = event_frame (event, 0, 0);
+       framepos_t where = event_frame (event, 0, 0);
        AutomationTimeAxisView* atv = 0;
 
+        if (pre_press_cursor) {
+                set_canvas_cursor (pre_press_cursor);
+                pre_press_cursor = 0;
+        }
+
        /* no action if we're recording */
 
        if (_session && _session->actively_recording()) {
                return true;
        }
 
-       /* first, see if we're finishing a drag ... */
+       /* see if we're finishing a drag */
 
        bool were_dragging = false;
        if (_drags->active ()) {
@@ -1116,14 +1192,14 @@ Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemT
                were_dragging = true;
        }
 
-       button_selection (item, event, item_type);
+        update_region_layering_order_editor ();
 
        /* edit events get handled here */
 
        if (!_drags->active () && Keyboard::is_edit_event (&event->button)) {
                switch (item_type) {
                case RegionItem:
-                       edit_region ();
+                       show_region_properties ();
                        break;
 
                case TempoMarkerItem:
@@ -1144,6 +1220,10 @@ Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemT
                        edit_control_point (item);
                        break;
 
+               case NoteItem:
+                       edit_note (item);
+                       break;
+
                default:
                        break;
                }
@@ -1368,7 +1448,7 @@ Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemT
                        break;
 
                case MouseAudition:
-                       track_canvas->get_window()->set_cursor (*current_canvas_cursor);
+                        set_canvas_cursor (current_canvas_cursor);
                        if (scrubbing_direction == 0) {
                                /* no drag, just a click */
                                switch (item_type) {
@@ -1444,10 +1524,7 @@ Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_
        double fraction;
         bool ret = true;
 
-       if (last_item_entered != item) {
-               last_item_entered = item;
-               last_item_entered_n = 0;
-       }
+       last_item_entered = item;
 
        switch (item_type) {
        case ControlPointItem:
@@ -1465,14 +1542,11 @@ Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_
                        fraction = 1.0 - (cp->get_y() / cp->line().height());
 
                        if (is_drawable() && !_drags->active ()) {
-                               track_canvas->get_window()->set_cursor (*fader_cursor);
+                               set_canvas_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 ();
-                       }
+                       show_verbose_canvas_cursor ();
                }
                break;
 
@@ -1482,7 +1556,7 @@ Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_
                        if (line)
                                line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredGainLine.get();
                        if (is_drawable()) {
-                               track_canvas->get_window()->set_cursor (*fader_cursor);
+                               set_canvas_cursor (fader_cursor);
                        }
                }
                break;
@@ -1495,26 +1569,36 @@ Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_
                                        line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredAutomationLine.get();
                        }
                        if (is_drawable()) {
-                               track_canvas->get_window()->set_cursor (*fader_cursor);
+                               set_canvas_cursor (fader_cursor);
                        }
                }
                break;
 
        case RegionViewNameHighlight:
-               if (is_drawable() && (mouse_mode == MouseObject || (internal_editing() && mouse_mode == MouseRange))) {
-                       track_canvas->get_window()->set_cursor (*trimmer_cursor);
+               if (is_drawable() && mouse_mode == MouseObject && !internal_editing()) {
+                       set_canvas_cursor (trimmer_cursor);
                }
                break;
 
        case LeftFrameHandle:
-               if (is_drawable() && (mouse_mode == MouseObject || (internal_editing() && mouse_mode == MouseRange))) {
-                       track_canvas->get_window()->set_cursor (*left_side_trim_cursor);
+               if (is_drawable() && mouse_mode == MouseObject && !internal_editing()) {
+                        if (entered_regionview) {
+                                Trimmable::CanTrim ct = entered_regionview->region()->can_trim();
+                                if ((ct & Trimmable::EndTrimEarlier) || (ct & Trimmable::EndTrimLater)) {
+                                        set_canvas_cursor (left_side_trim_cursor);
+                                }
+                        }
                }
                 break;
 
        case RightFrameHandle:
-               if (is_drawable() && (mouse_mode == MouseObject || (internal_editing() && mouse_mode == MouseRange))) {
-                       track_canvas->get_window()->set_cursor (*right_side_trim_cursor);
+               if (is_drawable() && mouse_mode == MouseObject && !internal_editing()) {
+                        if (entered_regionview) {
+                                Trimmable::CanTrim ct = entered_regionview->region()->can_trim();
+                                if ((ct & Trimmable::FrontTrimEarlier) || (ct & Trimmable::FrontTrimLater)) {
+                                        set_canvas_cursor (right_side_trim_cursor);
+                                }
+                        }
                }
                 break;
 
@@ -1529,7 +1613,7 @@ Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_
 #endif
 
                if (is_drawable()) {
-                       track_canvas->get_window()->set_cursor (*trimmer_cursor);
+                       set_canvas_cursor (trimmer_cursor);
                }
                break;
 
@@ -1537,10 +1621,10 @@ Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_
                if (is_drawable()) {
                        switch (_edit_point) {
                        case EditAtMouse:
-                               track_canvas->get_window()->set_cursor (*grabber_edit_point_cursor);
+                               set_canvas_cursor (grabber_edit_point_cursor);
                                break;
                        default:
-                               track_canvas->get_window()->set_cursor (*grabber_cursor);
+                               set_canvas_cursor (grabber_cursor);
                                break;
                        }
                }
@@ -1552,7 +1636,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);
+                               set_canvas_cursor (trimmer_cursor);
                        }
                }
                break;
@@ -1566,14 +1650,14 @@ Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_
                                cursor = selector_cursor;
                                break;
                        case MouseZoom:
-                               cursor = zoom_cursor;
+                               cursor = zoom_in_cursor;
                                break;
                        default:
                                cursor = cross_hair_cursor;
                                break;
                        }
 
-                       track_canvas->get_window()->set_cursor (*cursor);
+                       set_canvas_cursor (cursor);
 
                        AutomationTimeAxisView* atv;
                        if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
@@ -1590,7 +1674,7 @@ Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_
        case MeterBarItem:
        case TempoBarItem:
                if (is_drawable()) {
-                       track_canvas->get_window()->set_cursor (*timebar_cursor);
+                       set_canvas_cursor (timebar_cursor);
                }
                break;
 
@@ -1604,29 +1688,27 @@ Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_
        case MeterMarkerItem:
        case TempoMarkerItem:
                if (is_drawable()) {
-                       track_canvas->get_window()->set_cursor (*timebar_cursor);
+                       set_canvas_cursor (timebar_cursor);
                }
                break;
 
        case FadeInHandleItem:
-               if (mouse_mode == MouseObject) {
+               if (mouse_mode == MouseObject && !internal_editing()) {
                        ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
                        if (rect) {
-                               rect->property_fill_color_rgba() = 0;
-                               rect->property_outline_pixels() = 1;
+                               rect->property_fill_color_rgba() = 0xBBBBBBAA;
                        }
-                       track_canvas->get_window()->set_cursor (*fade_in_cursor);
+                       set_canvas_cursor (fade_in_cursor);
                }
                 break;
 
        case FadeOutHandleItem:
-               if (mouse_mode == MouseObject) {
+               if (mouse_mode == MouseObject && !internal_editing()) {
                        ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
                        if (rect) {
-                               rect->property_fill_color_rgba() = 0;
-                               rect->property_outline_pixels() = 1;
+                               rect->property_fill_color_rgba() = 0xBBBBBBAA;
                        }
-                       track_canvas->get_window()->set_cursor (*fade_out_cursor);
+                       set_canvas_cursor (fade_out_cursor);
                }
                break;
        case FeatureLineItem:
@@ -1635,6 +1717,12 @@ Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_
                        line->property_color_rgba() = 0xFF0000FF;
                }
                break;
+       case SelectionItem:
+               if (join_object_range_button.get_active()) {
+                       set_canvas_cursor ();
+               }
+               break;
+               
        default:
                break;
        }
@@ -1677,13 +1765,13 @@ Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_
        case ControlPointItem:
                cp = reinterpret_cast<ControlPoint*>(item->get_data ("control_point"));
                if (cp->line().the_list()->interpolation() != AutomationList::Discrete) {
-                       if (cp->line().npoints() > 1 && !cp->selected()) {
+                       if (cp->line().npoints() > 1 && !cp->get_selected()) {
                                cp->set_visible (false);
                        }
                }
 
                if (is_drawable()) {
-                       track_canvas->get_window()->set_cursor (*current_canvas_cursor);
+                        set_canvas_cursor (current_canvas_cursor);
                }
 
                hide_verbose_canvas_cursor ();
@@ -1704,7 +1792,7 @@ Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_
 #endif
 
                if (is_drawable()) {
-                       track_canvas->get_window()->set_cursor (*current_canvas_cursor);
+                        set_canvas_cursor (current_canvas_cursor);
                }
                break;
 
@@ -1717,7 +1805,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);
+                        set_canvas_cursor (current_canvas_cursor);
                }
                break;
 
@@ -1725,7 +1813,7 @@ 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);
+                                set_canvas_cursor (current_canvas_cursor);
                        }
                }
                break;
@@ -1737,7 +1825,7 @@ Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_
        case TempoBarItem:
        case MarkerBarItem:
                if (is_drawable()) {
-                       track_canvas->get_window()->set_cursor (*current_canvas_cursor);
+                        set_canvas_cursor (current_canvas_cursor);
                }
                break;
 
@@ -1754,7 +1842,7 @@ Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_
        case TempoMarkerItem:
 
                if (is_drawable()) {
-                       track_canvas->get_window()->set_cursor (*timebar_cursor);
+                       set_canvas_cursor (timebar_cursor);
                }
 
                break;
@@ -1769,12 +1857,12 @@ Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_
                                rect->property_outline_pixels() = 0;
                        }
                }
-               track_canvas->get_window()->set_cursor (*current_canvas_cursor);
+                set_canvas_cursor (current_canvas_cursor);
                break;
 
        case AutomationTrackItem:
                if (is_drawable()) {
-                       track_canvas->get_window()->set_cursor (*current_canvas_cursor);
+                        set_canvas_cursor (current_canvas_cursor);
                        clear_entered_track = true;
                        Glib::signal_idle().connect (sigc::mem_fun(*this, &Editor::left_automation_track));
                }
@@ -1804,7 +1892,7 @@ Editor::left_automation_track ()
 }
 
 void
-Editor::scrub (nframes64_t frame, double current_x)
+Editor::scrub (framepos_t frame, double current_x)
 {
        double delta;
 
@@ -1885,6 +1973,8 @@ Editor::scrub (nframes64_t frame, double current_x)
 bool
 Editor::motion_handler (ArdourCanvas::Item* /*item*/, GdkEvent* event, bool from_autoscroll)
 {
+       _last_motion_y = event->motion.y;
+       
        if (event->motion.is_hint) {
                gint x, y;
 
@@ -1921,6 +2011,7 @@ Editor::motion_handler (ArdourCanvas::Item* /*item*/, GdkEvent* event, bool from
        if (_drags->active ()) {
                handled = _drags->motion_handler (event, from_autoscroll);
        }
+       
        if (!handled) {
                return false;
        }
@@ -1982,6 +2073,19 @@ Editor::edit_control_point (ArdourCanvas::Item* item)
        p->line().modify_point_y (*p, d.get_y_fraction ());
 }
 
+void
+Editor::edit_note (ArdourCanvas::Item* item)
+{
+       ArdourCanvas::CanvasNoteEvent* e = dynamic_cast<ArdourCanvas::CanvasNoteEvent*> (item);
+       assert (e);
+
+       EditNoteDialog d (&e->region_view(), e);
+       d.set_position (Gtk::WIN_POS_MOUSE);
+       ensure_float (d);
+
+       d.run ();
+}
+       
 
 void
 Editor::visible_order_range (int* low, int* high) const
@@ -2021,34 +2125,34 @@ Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
                        speed = rtv->track()->speed();
                }
 
-               nframes64_t where = get_preferred_edit_position();
+               framepos_t where = get_preferred_edit_position();
 
                if (where >= 0) {
 
                        if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
 
-                               align_region (rv.region(), SyncPoint, (nframes64_t) (where * speed));
+                               align_region (rv.region(), SyncPoint, (framepos_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));
+                               align_region (rv.region(), End, (framepos_t) (where * speed));
 
                        } else {
 
-                               align_region (rv.region(), Start, (nframes64_t) (where * speed));
+                               align_region (rv.region(), Start, (framepos_t) (where * speed));
                        }
                }
        }
 }
 
 void
-Editor::show_verbose_time_cursor (nframes64_t frame, double offset, double xpos, double ypos)
+Editor::show_verbose_time_cursor (framepos_t frame, double offset, double xpos, double ypos)
 {
        char buf[128];
        Timecode::Time timecode;
        BBT_Time bbt;
        int hours, mins;
-       nframes64_t frame_rate;
+       framepos_t frame_rate;
        float secs;
 
        if (_session == 0) {
@@ -2099,14 +2203,14 @@ Editor::show_verbose_time_cursor (nframes64_t frame, double offset, double xpos,
 }
 
 void
-Editor::show_verbose_duration_cursor (nframes64_t start, nframes64_t end, double offset, double xpos, double ypos)
+Editor::show_verbose_duration_cursor (framepos_t start, framepos_t end, double offset, double xpos, double ypos)
 {
        char buf[128];
        Timecode::Time timecode;
        BBT_Time sbbt;
        BBT_Time ebbt;
        int hours, mins;
-       nframes64_t distance, frame_rate;
+       framepos_t distance, frame_rate;
        float secs;
        Meter meter_at_start(_session->tempo_map().meter_at(start));
 
@@ -2207,7 +2311,7 @@ Editor::cancel_selection ()
 
 
 void
-Editor::single_contents_trim (RegionView& rv, nframes64_t frame_delta, bool left_direction, bool swap_direction)
+Editor::single_contents_trim (RegionView& rv, framepos_t frame_delta, bool left_direction, bool swap_direction)
 {
        boost::shared_ptr<Region> region (rv.region());
 
@@ -2215,7 +2319,7 @@ Editor::single_contents_trim (RegionView& rv, nframes64_t frame_delta, bool left
                return;
        }
 
-       nframes64_t new_bound;
+       framepos_t new_bound;
 
        double speed = 1.0;
        TimeAxisView* tvp = clicked_axisview;
@@ -2227,24 +2331,24 @@ Editor::single_contents_trim (RegionView& rv, nframes64_t frame_delta, bool left
 
        if (left_direction) {
                if (swap_direction) {
-                       new_bound = (nframes64_t) (region->position()/speed) + frame_delta;
+                       new_bound = (framepos_t) (region->position()/speed) + frame_delta;
                } else {
-                       new_bound = (nframes64_t) (region->position()/speed) - frame_delta;
+                       new_bound = (framepos_t) (region->position()/speed) - frame_delta;
                }
        } else {
                if (swap_direction) {
-                       new_bound = (nframes64_t) (region->position()/speed) - frame_delta;
+                       new_bound = (framepos_t) (region->position()/speed) - frame_delta;
                } else {
-                       new_bound = (nframes64_t) (region->position()/speed) + frame_delta;
+                       new_bound = (framepos_t) (region->position()/speed) + frame_delta;
                }
        }
 
-       region->trim_start ((nframes64_t) (new_bound * speed), this);
+       region->trim_start ((framepos_t) (new_bound * speed), this);
        rv.region_changed (PropertyChange (ARDOUR::Properties::start));
 }
 
 void
-Editor::single_start_trim (RegionView& rv, nframes64_t new_bound, bool no_overlap)
+Editor::single_start_trim (RegionView& rv, framepos_t new_bound, bool no_overlap)
 {
        boost::shared_ptr<Region> region (rv.region());
 
@@ -2260,9 +2364,9 @@ Editor::single_start_trim (RegionView& rv, nframes64_t new_bound, bool no_overla
                speed = tv->track()->speed();
        }
 
-       nframes64_t pre_trim_first_frame = region->first_frame();
+       framepos_t pre_trim_first_frame = region->first_frame();
 
-       region->trim_front ((nframes64_t) (new_bound * speed), this);
+       region->trim_front ((framepos_t) (new_bound * speed), this);
 
        if (no_overlap) {
                //Get the next region on the left of this region and shrink/expand it.
@@ -2287,7 +2391,7 @@ Editor::single_start_trim (RegionView& rv, nframes64_t new_bound, bool no_overla
 }
 
 void
-Editor::single_end_trim (RegionView& rv, nframes64_t new_bound, bool no_overlap)
+Editor::single_end_trim (RegionView& rv, framepos_t new_bound, bool no_overlap)
 {
        boost::shared_ptr<Region> region (rv.region());
 
@@ -2303,9 +2407,9 @@ Editor::single_end_trim (RegionView& rv, nframes64_t new_bound, bool no_overlap)
                speed = tv->track()->speed();
        }
 
-       nframes64_t pre_trim_last_frame = region->last_frame();
+       framepos_t pre_trim_last_frame = region->last_frame();
 
-       region->trim_end ((nframes64_t) (new_bound * speed), this);
+       region->trim_end ((framepos_t) (new_bound * speed), this);
 
        if (no_overlap) {
                //Get the next region on the right of this region and shrink/expand it.
@@ -2334,14 +2438,14 @@ Editor::single_end_trim (RegionView& rv, nframes64_t new_bound, bool no_overlap)
 
 
 void
-Editor::point_trim (GdkEvent* event, nframes64_t new_bound)
+Editor::point_trim (GdkEvent* event, framepos_t new_bound)
 {
        RegionView* rv = clicked_regionview;
 
        /* Choose action dependant on which button was pressed */
        switch (event->button.button) {
        case 1:
-               begin_reversible_command (_("Start point trim"));
+               begin_reversible_command (_("start point trim"));
 
                if (selection->selected (rv)) {
                        for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
@@ -2352,7 +2456,7 @@ Editor::point_trim (GdkEvent* event, nframes64_t new_bound)
                                }
 
                                if (!(*i)->region()->locked()) {
-                                       (*i)->region()->clear_history ();
+                                       (*i)->region()->clear_changes ();
                                        (*i)->region()->trim_front (new_bound, this);
                                        _session->add_command(new StatefulDiffCommand ((*i)->region()));
                                }
@@ -2360,7 +2464,7 @@ Editor::point_trim (GdkEvent* event, nframes64_t new_bound)
 
                } else {
                        if (!rv->region()->locked()) {
-                               rv->region()->clear_history ();
+                               rv->region()->clear_changes ();
                                rv->region()->trim_front (new_bound, this);
                                _session->add_command(new StatefulDiffCommand (rv->region()));
                        }
@@ -2377,7 +2481,7 @@ Editor::point_trim (GdkEvent* event, nframes64_t new_bound)
                        for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
                        {
                                if (!(*i)->region()->locked()) {
-                                       (*i)->region()->clear_history();
+                                       (*i)->region()->clear_changes();
                                        (*i)->region()->trim_end (new_bound, this);
                                        _session->add_command(new StatefulDiffCommand ((*i)->region()));
                                }
@@ -2386,7 +2490,7 @@ Editor::point_trim (GdkEvent* event, nframes64_t new_bound)
                } else {
 
                        if (!rv->region()->locked()) {
-                               rv->region()->clear_history ();
+                               rv->region()->clear_changes ();
                                rv->region()->trim_end (new_bound, this);
                                _session->add_command (new StatefulDiffCommand (rv->region()));
                        }
@@ -2435,7 +2539,7 @@ Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* /*event*/)
 
 
 void
-Editor::reposition_zoom_rect (nframes64_t start, nframes64_t end)
+Editor::reposition_zoom_rect (framepos_t start, framepos_t end)
 {
        double x1 = frame_to_pixel (start);
        double x2 = frame_to_pixel (end);
@@ -2474,7 +2578,7 @@ Editor::mouse_rename_region (ArdourCanvas::Item* /*item*/, GdkEvent* /*event*/)
 
 
 void
-Editor::mouse_brush_insert_region (RegionView* rv, nframes64_t pos)
+Editor::mouse_brush_insert_region (RegionView* rv, framepos_t pos)
 {
        /* no brushing without a useful snap setting */
 
@@ -2508,9 +2612,9 @@ Editor::mouse_brush_insert_region (RegionView* rv, nframes64_t pos)
        boost::shared_ptr<Playlist> playlist = rtv->playlist();
        double speed = rtv->track()->speed();
 
-        playlist->clear_history ();
+        playlist->clear_changes ();
        boost::shared_ptr<Region> new_region (RegionFactory::create (rv->region()));
-        playlist->add_region (new_region, (nframes64_t) (pos * speed));
+        playlist->add_region (new_region, (framepos_t) (pos * speed));
        _session->add_command (new StatefulDiffCommand (playlist));
 
        // playlist is frozen, so we have to update manually XXX this is disgusting
@@ -2533,6 +2637,10 @@ Editor::add_region_drag (ArdourCanvas::Item* item, GdkEvent* event, RegionView*
 {
        assert (region_view);
 
+        if (!region_view->region()->playlist()) {
+                return;
+        }
+
        _region_motion_group->raise_to_top ();
 
        if (Config->get_edit_mode() == Splice) {
@@ -2551,6 +2659,10 @@ Editor::add_region_copy_drag (ArdourCanvas::Item* item, GdkEvent* event, RegionV
 {
        assert (region_view);
 
+        if (!region_view->region()->playlist()) {
+                return;
+        }
+
        _region_motion_group->raise_to_top ();
 
        RegionSelection s = get_equivalent_regions (selection->regions, ARDOUR::Properties::edit.property_id);
@@ -2562,6 +2674,10 @@ Editor::add_region_brush_drag (ArdourCanvas::Item* item, GdkEvent* event, Region
 {
        assert (region_view);
 
+        if (!region_view->region()->playlist()) {
+                return;
+        }
+        
        if (Config->get_edit_mode() == Splice) {
                return;
        }
@@ -2614,7 +2730,7 @@ Editor::start_selection_grab (ArdourCanvas::Item* /*item*/, GdkEvent* event)
 
        boost::shared_ptr<Playlist> playlist = clicked_axisview->playlist();
 
-        playlist->clear_history ();
+        playlist->clear_changes ();
        clicked_routeview->playlist()->add_region (region, selection->time[clicked_selection].start);
        _session->add_command(new StatefulDiffCommand (playlist));
 
@@ -2653,37 +2769,20 @@ Editor::set_internal_edit (bool yn)
        if (yn) {
                mouse_select_button.set_image (*(manage (new Image (::get_icon("midi_tool_pencil")))));
                mouse_select_button.get_image ()->show ();
-                ARDOUR_UI::instance()->tooltips().set_tip (mouse_select_button, _("Draw/Edit MIDI Notes"));
-
-               for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
-                       MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*> (*i);
-                       if (mtv) {
-                               mtv->start_step_editing ();
-                       }
-               }
-
-               for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
-                       (*i)->hide_selection ();
-               }
-
-               start_step_editing ();
+                ARDOUR_UI::instance()->set_tip (mouse_select_button, _("Draw/Edit MIDI Notes"));
                 set_canvas_cursor ();
 
+               /* deselect everything to avoid confusion when e.g. we can't now cut a previously selected
+                  region because cut means "cut note" rather than "cut region".
+               */
+               selection->clear ();
+
        } else {
 
                mouse_select_button.set_image (*(manage (new Image (::get_icon("tool_range")))));
                mouse_select_button.get_image ()->show ();
-                ARDOUR_UI::instance()->tooltips().set_tip (mouse_select_button, _("Select/Move Ranges"));
-               stop_step_editing ();
-
-               for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
-                       MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*> (*i);
-                       if (mtv) {
-                               mtv->stop_step_editing ();
-                       }
-               }
-
-                mouse_mode_toggled (mouse_mode);
+                ARDOUR_UI::instance()->set_tip (mouse_select_button, _("Select/Move Ranges"));
+                mouse_mode_toggled (mouse_mode); // sets cursor
        }
 }
 
@@ -2721,9 +2820,11 @@ Editor::update_join_object_range_location (double x, double y)
                        double cy = y;
                        rtv->canvas_display()->w2i (cx, cy);
 
-                       bool const top_half = cy < rtv->current_height () / 2;
-                       
-                       _join_object_range_state = top_half ? JOIN_OBJECT_RANGE_RANGE : JOIN_OBJECT_RANGE_OBJECT;
+                       double const c = cy / rtv->view()->child_height();
+                       double d;
+                       double const f = modf (c, &d);
+
+                       _join_object_range_state = f < 0.5 ? JOIN_OBJECT_RANGE_RANGE : JOIN_OBJECT_RANGE_OBJECT;
                }
        }
 }