Visual tweaks to Soundcloud panel
[ardour.git] / gtk2_ardour / editor_mouse.cc
index c91e9d1b34d1566a03f5322883d1348d4911a7cd..928801d0b213cda746af011ff2c70dca01ac73e4 100644 (file)
@@ -24,6 +24,7 @@
 #include <set>
 #include <string>
 #include <algorithm>
+#include <bitset>
 
 #include "pbd/error.h"
 #include "pbd/enumwriter.h"
 #include "gtkmm2ext/utils.h"
 #include "gtkmm2ext/tearoff.h"
 
+#include "canvas/canvas.h"
+
+#include "ardour/audioregion.h"
+#include "ardour/operations.h"
+#include "ardour/playlist.h"
+#include "ardour/profile.h"
+#include "ardour/region_factory.h"
+#include "ardour/route.h"
+#include "ardour/session.h"
+#include "ardour/types.h"
+
 #include "ardour_ui.h"
 #include "actions.h"
-#include "canvas-note.h"
 #include "editor.h"
 #include "time_axis_view.h"
 #include "audio_time_axis.h"
 #include "mouse_cursors.h"
 #include "editor_cursors.h"
 #include "verbose_cursor.h"
-
-#include "ardour/audioregion.h"
-#include "ardour/operations.h"
-#include "ardour/playlist.h"
-#include "ardour/profile.h"
-#include "ardour/region_factory.h"
-#include "ardour/route.h"
-#include "ardour/session.h"
-#include "ardour/types.h"
-
-#include <bitset>
+#include "note.h"
 
 #include "i18n.h"
 
@@ -101,84 +102,89 @@ Editor::mouse_frame (framepos_t& where, bool& in_track_canvas) const
         }
 
        int x, y;
-       double wx, wy;
-       Gdk::ModifierType mask;
-       Glib::RefPtr<Gdk::Window> canvas_window = const_cast<Editor*>(this)->track_canvas->get_window();
-       Glib::RefPtr<const Gdk::Window> pointer_window;
+       Glib::RefPtr<Gdk::Window> canvas_window = const_cast<Editor*>(this)->_track_canvas->get_window();
 
        if (!canvas_window) {
                return false;
        }
 
-       pointer_window = canvas_window->get_pointer (x, y, mask);
+       Glib::RefPtr<const Gdk::Window> pointer_window = Gdk::Display::get_default()->get_window_at_pointer (x, y);
 
-       if (pointer_window == track_canvas->get_bin_window()) {
-               wx = x;
-               wy = y;
-               in_track_canvas = true;
+       if (!pointer_window) {
+               return false;
+       }
 
-       } else {
+       if (pointer_window != canvas_window && pointer_window != _time_bars_canvas->get_window()) {
                in_track_canvas = false;
-                       return false;
+               return false;
        }
 
+       in_track_canvas = true;
+
        GdkEvent event;
        event.type = GDK_BUTTON_RELEASE;
-       event.button.x = wx;
-       event.button.y = wy;
+       event.button.x = x;
+       event.button.y = y;
+
+       where = window_event_sample (&event, 0, 0);
 
-       where = event_frame (&event, 0, 0);
        return true;
 }
 
 framepos_t
-Editor::event_frame (GdkEvent const * event, double* pcx, double* pcy) const
+Editor::window_event_sample (GdkEvent const * event, double* pcx, double* pcy) const
 {
-       double cx, cy;
+       double x;
+       double y;
 
-       if (pcx == 0) {
-               pcx = &cx;
+       if (!gdk_event_get_coords (event, &x, &y)) {
+               return 0;
        }
-       if (pcy == 0) {
-               pcy = &cy;
+
+       /* event coordinates are in window units, so convert to canvas
+        * (i.e. account for scrolling)
+        */
+
+       ArdourCanvas::Duple d = _track_canvas->window_to_canvas (ArdourCanvas::Duple (x, y));
+
+       if (pcx) {
+               *pcx = d.x;
        }
 
-       *pcx = 0;
-       *pcy = 0;
+       if (pcy) {
+               *pcy = d.y;
+       }
 
-       switch (event->type) {
-       case GDK_BUTTON_RELEASE:
-       case GDK_BUTTON_PRESS:
-       case GDK_2BUTTON_PRESS:
-       case GDK_3BUTTON_PRESS:
-               *pcx = event->button.x;
-               *pcy = event->button.y;
-               _trackview_group->w2i(*pcx, *pcy);
-               break;
-       case GDK_MOTION_NOTIFY:
-               *pcx = event->motion.x;
-               *pcy = event->motion.y;
-               _trackview_group->w2i(*pcx, *pcy);
-               break;
-       case GDK_ENTER_NOTIFY:
-       case GDK_LEAVE_NOTIFY:
-               track_canvas->w2c(event->crossing.x, event->crossing.y, *pcx, *pcy);
-               break;
-       case GDK_KEY_PRESS:
-       case GDK_KEY_RELEASE:
-               // track_canvas->w2c(event->key.x, event->key.y, *pcx, *pcy);
-               break;
-       default:
-               warning << string_compose (_("Editor::event_frame() used on unhandled event type %1"), event->type) << endmsg;
-               break;
+       return pixel_to_sample (d.x);
+}
+
+framepos_t
+Editor::canvas_event_sample (GdkEvent const * event, double* pcx, double* pcy) const
+{
+       double x;
+       double y;
+
+       /* event coordinates are already in canvas units */
+
+       if (!gdk_event_get_coords (event, &x, &y)) {
+               cerr << "!NO c COORDS for event type " << event->type << endl;
+               return 0;
        }
 
-       /* note that pixel_to_frame() never returns less than zero, so even if the pixel
+       if (pcx) {
+               *pcx = x;
+       }
+
+       if (pcy) {
+               *pcy = y;
+       }
+
+       /* note that pixel_to_sample_from_event() never returns less than zero, so even if the pixel
           position is negative (as can be the case with motion events in particular),
           the frame location is always positive.
        */
 
-       return pixel_to_frame (*pcx);
+       return pixel_to_sample_from_event (x);
 }
 
 Gdk::Cursor*
@@ -303,13 +309,27 @@ Editor::set_canvas_cursor ()
 
        /* up-down cursor as a cue that automation can be dragged up and down when in join object/range mode */
        if (!_internal_editing && get_smart_mode() ) {
+
                double x, y;
                get_pointer_position (x, y);
-               ArdourCanvas::Item* i = track_canvas->get_item_at (x, y);
-               if (i && i->property_parent() && (*i->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 = _cursors->up_down;
+
+               if (x >= 0 && y >= 0) {
+                       
+                       vector<ArdourCanvas::Item const *> items;
+                       
+                       _track_canvas->root()->add_items_at_point (ArdourCanvas::Duple (x,y), items);
+                       
+                       // first item will be the upper most 
+                       
+                       if (!items.empty()) {
+                               const ArdourCanvas::Item* i = items.front();
+                               
+                               if (i && i->parent() && i->parent()->get_data (X_("timeselection"))) {
+                                       pair<TimeAxisView*, int> tvp = trackview_by_y_position (_last_motion_y);
+                                       if (dynamic_cast<AutomationTimeAxisView*> (tvp.first)) {
+                                               current_canvas_cursor = _cursors->up_down;
+                                       }
+                               }
                        }
                }
        }
@@ -326,9 +346,11 @@ Editor::mouse_mode_object_range_toggled()
        assert (act);
        Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
        assert (tact);
-       if (tact->get_active())
+
+       if (tact->get_active()) {
                m = MouseObject;  //Smart mode turned to ON, force editing to Object mode
-       
+       }
+
        set_mouse_mode(m, true);  //call this so the button styles can get updated
 }
 
@@ -477,10 +499,32 @@ Editor::mouse_mode_toggled (MouseMode m)
        
        set_canvas_cursor ();
        set_gain_envelope_visibility ();
+       
+       update_time_selection_display ();
 
        MouseModeChanged (); /* EMIT SIGNAL */
 }
 
+void
+Editor::update_time_selection_display ()
+{
+       if (smart_mode_action->get_active()) {
+               /* not sure what to do here */
+               if (mouse_mode == MouseObject) {
+               } else {
+               }
+       } else {
+               switch (mouse_mode) {
+               case MouseRange:
+                       selection->clear_objects ();
+                       break;
+               default:
+                       selection->clear_time ();
+                       break;
+               }
+       }
+}
+
 void
 Editor::step_mouse_mode (bool next)
 {
@@ -579,6 +623,15 @@ Editor::button_selection (ArdourCanvas::Item* /*item*/, GdkEvent* event, ItemTyp
           to cut notes or regions.
        */
 
+       MouseMode eff_mouse_mode = mouse_mode;
+
+       if (get_smart_mode() && eff_mouse_mode == MouseRange && event->button.button == 3 && item_type == RegionItem) {
+               /* context clicks are always about object properties, even if
+                  we're in range mode within smart mode.
+               */
+               eff_mouse_mode = MouseObject;
+       }
+
        if (((mouse_mode != MouseObject) &&
             (mouse_mode != MouseAudition || item_type != RegionItem) &&
             (mouse_mode != MouseTimeFX || item_type != RegionItem) &&
@@ -607,25 +660,23 @@ Editor::button_selection (ArdourCanvas::Item* /*item*/, GdkEvent* event, ItemTyp
 
        switch (item_type) {
        case RegionItem:
-               if (!get_smart_mode() || (_join_object_range_state != JOIN_OBJECT_RANGE_RANGE)) {
-                       if (press) {
-                               if (mouse_mode != MouseRange) {
-                                       set_selected_regionview_from_click (press, op);
-                               } else {
-                                       /* don't change the selection unless the
-                                          clicked track is not currently selected. if
-                                          so, "collapse" the selection to just this
-                                          track
-                                       */
-                                       if (!selection->selected (clicked_axisview)) {
-                                               set_selected_track_as_side_effect (Selection::Set);
-                                       }
-                               }
+               if (press) {
+                       if (eff_mouse_mode != MouseRange) {
+                               set_selected_regionview_from_click (press, op);
                        } else {
-                               if (mouse_mode != MouseRange) {
-                                       set_selected_regionview_from_click (press, op);
+                               /* don't change the selection unless the
+                                  clicked track is not currently selected. if
+                                  so, "collapse" the selection to just this
+                                  track
+                               */
+                               if (!selection->selected (clicked_axisview)) {
+                                       set_selected_track_as_side_effect (Selection::Set);
                                }
                        }
+               } else {
+                       if (eff_mouse_mode != MouseRange) {
+                               set_selected_regionview_from_click (press, op);
+                       }
                }
                break;
 
@@ -633,7 +684,7 @@ Editor::button_selection (ArdourCanvas::Item* /*item*/, GdkEvent* event, ItemTyp
        case RegionViewName:
        case LeftFrameHandle:
        case RightFrameHandle:
-               if ( mouse_mode != MouseRange ) {
+               if (eff_mouse_mode != MouseRange) {
                        set_selected_regionview_from_click (press, op);
                } else if (event->type == GDK_BUTTON_PRESS) {
                        set_selected_track_as_side_effect (op);
@@ -646,7 +697,8 @@ Editor::button_selection (ArdourCanvas::Item* /*item*/, GdkEvent* event, ItemTyp
        case FadeOutItem:
        case StartCrossFadeItem:
        case EndCrossFadeItem:
-               if ( mouse_mode != MouseRange ) {
+               if (eff_mouse_mode != MouseRange) {
+                       cerr << "Should be setting selected regionview\n";
                        set_selected_regionview_from_click (press, op);
                } else if (event->type == GDK_BUTTON_PRESS) {
                        set_selected_track_as_side_effect (op);
@@ -655,7 +707,7 @@ Editor::button_selection (ArdourCanvas::Item* /*item*/, GdkEvent* event, ItemTyp
 
        case ControlPointItem:
                set_selected_track_as_side_effect (op);
-               if ( mouse_mode != MouseRange ) {
+               if (eff_mouse_mode != MouseRange) {
                        set_selected_control_point_from_click (press, op);
                }
                break;
@@ -688,7 +740,7 @@ Editor::button_press_handler_1 (ArdourCanvas::Item* item, GdkEvent* event, ItemT
 
        switch (item_type) {
        case PlayheadCursorItem:
-               _drags->set (new CursorDrag (this, item, true), event);
+               _drags->set (new CursorDrag (this, *playhead_cursor, true), event);
                return true;
 
        case MarkerItem:
@@ -703,38 +755,30 @@ Editor::button_press_handler_1 (ArdourCanvas::Item* item, GdkEvent* event, ItemT
        {
                TempoMarker* m = reinterpret_cast<TempoMarker*> (item->get_data ("marker"));
                assert (m);
-               if (m->tempo().movable ()) {
-                       _drags->set (
-                               new TempoMarkerDrag (
-                                       this,
-                                       item,
-                                       Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)
-                                       ),
-                               event
-                               );
-                       return true;
-               } else {
-                       return false;
-               }
+               _drags->set (
+                       new TempoMarkerDrag (
+                               this,
+                               item,
+                               Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)
+                               ),
+                       event
+                       );
+               return true;
        }
 
        case MeterMarkerItem:
        {
                MeterMarker* m = reinterpret_cast<MeterMarker*> (item->get_data ("marker"));
                assert (m);
-               if (m->meter().movable ()) {
-                       _drags->set (
-                               new MeterMarkerDrag (
-                                       this,
-                                       item,
-                                       Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)
-                                       ),
-                               event
-                               );
-                       return true;
-               } else {
-                       return false;
-               }
+               _drags->set (
+                       new MeterMarkerDrag (
+                               this,
+                               item,
+                               Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)
+                               ),
+                       event
+                       );
+               return true;
        }
 
        case VideoBarItem:
@@ -746,7 +790,7 @@ Editor::button_press_handler_1 (ArdourCanvas::Item* item, GdkEvent* event, ItemT
        case TempoBarItem:
        case MeterBarItem:
                if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
-                       _drags->set (new CursorDrag (this, &playhead_cursor->canvas_item, false), event);
+                       _drags->set (new CursorDrag (this, *playhead_cursor, false), event);
                }
                return true;
                break;
@@ -754,7 +798,7 @@ Editor::button_press_handler_1 (ArdourCanvas::Item* item, GdkEvent* event, ItemT
 
        case RangeMarkerBarItem:
                if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
-                       _drags->set (new CursorDrag (this, &playhead_cursor->canvas_item, false), event);
+                       _drags->set (new CursorDrag (this, *playhead_cursor, false), event);
                } else {
                        _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateRangeMarker), event);
                }
@@ -763,7 +807,7 @@ Editor::button_press_handler_1 (ArdourCanvas::Item* item, GdkEvent* event, ItemT
 
        case CdMarkerBarItem:
                if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
-                       _drags->set (new CursorDrag (this, &playhead_cursor->canvas_item, false), event);
+                       _drags->set (new CursorDrag (this, *playhead_cursor, false), event);
                } else {
                        _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateCDMarker), event);
                }
@@ -772,7 +816,7 @@ Editor::button_press_handler_1 (ArdourCanvas::Item* item, GdkEvent* event, ItemT
 
        case TransportMarkerBarItem:
                if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
-                       _drags->set (new CursorDrag (this, &playhead_cursor->canvas_item, false), event);
+                       _drags->set (new CursorDrag (this, *playhead_cursor, false), event);
                } else {
                        _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateTransportMarker), event);
                }
@@ -823,11 +867,12 @@ Editor::button_press_handler_1 (ArdourCanvas::Item* item, GdkEvent* event, ItemT
                case SelectionItem:
                        if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
                                start_selection_grab (item, event);
+                               return true;
                        } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::SecondaryModifier)) {
                                /* grab selection for moving */
                                _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionMove), event);
                        } else {
-                               double const y = event->button.y + vertical_adjustment.get_value() - canvas_timebars_vsize;
+                               double const y = event->button.y;
                                pair<TimeAxisView*, int> tvp = trackview_by_y_position (y);
                                if (tvp.first) {
                                        AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (tvp.first);
@@ -881,16 +926,18 @@ Editor::button_press_handler_1 (ArdourCanvas::Item* item, GdkEvent* event, ItemT
        case MouseDraw:
                switch (item_type) {
                case NoteItem:
+                       /* Existing note: allow trimming/motion */
                        if (internal_editing()) {
                                /* trim notes if we're in internal edit mode and near the ends of the note */
-                               ArdourCanvas::CanvasNote* cn = dynamic_cast<ArdourCanvas::CanvasNote*> (item);
-                               if (cn && cn->big_enough_to_trim() && cn->mouse_near_ends()) {
+                               NoteBase* cn = reinterpret_cast<NoteBase*>(item->get_data ("notebase"));
+                               assert (cn);
+                               if (cn->big_enough_to_trim() && 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;
                case StreamItem:
                        if (internal_editing()) {
@@ -909,9 +956,11 @@ Editor::button_press_handler_1 (ArdourCanvas::Item* item, GdkEvent* event, ItemT
        case MouseObject:
                switch (item_type) {
                case NoteItem:
+                       /* Existing note: allow trimming/motion */
                        if (internal_editing()) {
-                               ArdourCanvas::CanvasNoteEvent* cn = dynamic_cast<ArdourCanvas::CanvasNoteEvent*> (item);
-                               if (cn->mouse_near_ends()) {
+                               NoteBase* cn = reinterpret_cast<NoteBase*> (item->get_data ("notebase"));
+                               assert (cn);
+                               if (cn->big_enough_to_trim() && cn->mouse_near_ends()) {
                                        _drags->set (new NoteResizeDrag (this, item), event, current_canvas_cursor);
                                } else {
                                        _drags->set (new NoteDrag (this, item), event);
@@ -992,6 +1041,7 @@ Editor::button_press_handler_1 (ArdourCanvas::Item* item, GdkEvent* event, ItemT
 //                             }
 
                                _drags->start_grab (event);
+                               return true;
                                break;
 
                        case RegionViewNameHighlight:
@@ -1047,7 +1097,7 @@ Editor::button_press_handler_1 (ArdourCanvas::Item* item, GdkEvent* event, ItemT
                                                _drags->set (new RegionCreateDrag (this, item, parent), event);
                                        } else {
                                                /* See if there's a region before the click that we can extend, and extend it if so */
-                                               framepos_t const t = event_frame (event);
+                                               framepos_t const t = canvas_event_sample (event);
                                                boost::shared_ptr<Region> prev = pl->find_next_region (t, End, -1);
                                                if (!prev) {
                                                        _drags->set (new RegionCreateDrag (this, item, parent), event);
@@ -1066,7 +1116,7 @@ Editor::button_press_handler_1 (ArdourCanvas::Item* item, GdkEvent* event, ItemT
                        {
                                if ( get_smart_mode() ) {
                                        /* we're in "smart" joined mode, and we've clicked on a Selection */
-                                       double const y = event->button.y + vertical_adjustment.get_value() - canvas_timebars_vsize;
+                                       double const y = event->button.y;
                                        pair<TimeAxisView*, int> tvp = trackview_by_y_position (y);
                                        if (tvp.first) {
                                                /* if we're over an automation track, start a drag of its data */
@@ -1085,7 +1135,7 @@ Editor::button_press_handler_1 (ArdourCanvas::Item* item, GdkEvent* event, ItemT
                                                                boost::shared_ptr<Playlist> pl = t->playlist ();
                                                                if (pl) {
 
-                                                                       boost::shared_ptr<Region> r = pl->top_region_at (event_frame (event));
+                                                                       boost::shared_ptr<Region> r = pl->top_region_at (canvas_event_sample (event));
                                                                        if (r) {
                                                                                RegionView* rv = rtv->view()->find_view (r);
                                                                                clicked_selection = select_range (rv->region()->position(), 
@@ -1095,6 +1145,7 @@ Editor::button_press_handler_1 (ArdourCanvas::Item* item, GdkEvent* event, ItemT
                                                                                rvs.push_back (rv);
                                                                                _drags->add (new RegionMoveDrag (this, item, rv, rvs, false, false));
                                                                                _drags->start_grab (event);
+                                                                               return true;
                                                                        }
                                                                }
                                                        }
@@ -1157,9 +1208,13 @@ Editor::button_press_handler_1 (ArdourCanvas::Item* item, GdkEvent* event, ItemT
                break;
 
        case MouseTimeFX:
-               if (internal_editing() && item_type == NoteItem) {
+               if (internal_editing() && item_type == NoteItem ) {
                        /* drag notes if we're in internal edit mode */
-                       _drags->set (new NoteResizeDrag (this, item), event, current_canvas_cursor);
+                       NoteBase* cn = reinterpret_cast<NoteBase*>(item->get_data ("notebase"));
+                       assert (cn);
+                       if (cn->big_enough_to_trim()) {
+                               _drags->set (new NoteResizeDrag (this, item), event, current_canvas_cursor);
+                       }
                        return true;
                } else if (clicked_regionview) {
                        /* do time-FX  */
@@ -1251,9 +1306,9 @@ Editor::button_press_handler_2 (ArdourCanvas::Item* item, GdkEvent* event, ItemT
 
        case MouseZoom:
                if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
-                       temporal_zoom_to_frame (false, event_frame (event));
+                       temporal_zoom_to_frame (false, canvas_event_sample (event));
                } else {
-                       temporal_zoom_to_frame (true, event_frame(event));
+                       temporal_zoom_to_frame (true, canvas_event_sample(event));
                }
                return true;
                break;
@@ -1268,15 +1323,17 @@ Editor::button_press_handler_2 (ArdourCanvas::Item* item, GdkEvent* event, ItemT
 bool
 Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
 {
+       if (event->type == GDK_2BUTTON_PRESS) {
+               _drags->mark_double_click ();
+               gdk_pointer_ungrab (GDK_CURRENT_TIME);
+               return true;
+       }
+
        if (event->type != GDK_BUTTON_PRESS) {
-               if (event->type == GDK_2BUTTON_PRESS) {
-                       gdk_pointer_ungrab (GDK_CURRENT_TIME);
-                       return button_double_click_handler (item, event, item_type);
-               }
                return false;
        }
 
-       Glib::RefPtr<Gdk::Window> canvas_window = const_cast<Editor*>(this)->track_canvas->get_window();
+       Glib::RefPtr<Gdk::Window> canvas_window = const_cast<Editor*>(this)->_track_canvas_viewport->get_window();
 
        if (canvas_window) {
                Glib::RefPtr<const Gdk::Window> pointer_window;
@@ -1286,14 +1343,14 @@ Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemTyp
 
                pointer_window = canvas_window->get_pointer (x, y, mask);
 
-               if (pointer_window == track_canvas->get_bin_window()) {
-                       track_canvas->window_to_world (x, y, wx, wy);
+               if (pointer_window == _track_canvas->get_window()) {
+                       _track_canvas->window_to_canvas (x, y, wx, wy);
                }
        }
 
         pre_press_cursor = current_canvas_cursor;
-       
-       track_canvas->grab_focus();
+
+       _track_canvas->grab_focus();
 
        if (_session && _session->actively_recording()) {
                return true;
@@ -1350,7 +1407,7 @@ Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemTyp
 
        //not rolling, range mode click + join_play_range :  locate the PH here
        if ( !_drags->active () && !_session->transport_rolling() && ( effective_mouse_mode() == MouseRange ) && Config->get_always_play_range() ) {
-               framepos_t where = event_frame (event, 0, 0);
+               framepos_t where = canvas_event_sample (event);
                snap_to(where);
                _session->request_locate (where, false);
        }
@@ -1396,41 +1453,10 @@ Editor::button_release_dispatch (GdkEventButton* ev)
         return button_bindings->activate (b, Gtkmm2ext::Bindings::Release);
 }
 
-bool
-Editor::button_double_click_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type) {
-
-       if (event->button.button != 1) {
-               return false;
-       }
-
-       switch (item_type) {
-               case RegionItem:
-               case NoteItem:
-               case PlayheadCursorItem:
-               case MarkerItem:
-               case RangeMarkerBarItem:
-               case CdMarkerBarItem:
-               case TempoMarkerItem:
-               case MeterMarkerItem:
-               case MarkerBarItem:
-               case TempoBarItem:
-               case MeterBarItem:
-               case TransportMarkerBarItem:
-               case StreamItem:
-                       break;
-
-               default:
-                       break;
-       }
-       return false;
-}
-
-
-
 bool
 Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
 {
-       framepos_t where = event_frame (event, 0, 0);
+       framepos_t where = canvas_event_sample (event);
        AutomationTimeAxisView* atv = 0;
 
         if (pre_press_cursor) {
@@ -1457,7 +1483,7 @@ Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemT
                were_dragging = true;
        }
 
-    update_region_layering_order_editor ();
+       update_region_layering_order_editor ();
 
        /* edit events get handled here */
 
@@ -1467,13 +1493,40 @@ Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemT
                        show_region_properties ();
                        break;
 
-               case TempoMarkerItem:
-                       edit_tempo_marker (item);
+               case TempoMarkerItem: {
+                       Marker* marker;
+                       TempoMarker* tempo_marker;
+                       
+                       if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
+                               fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
+                               /*NOTREACHED*/
+                       }
+                       
+                       if ((tempo_marker = dynamic_cast<TempoMarker*> (marker)) == 0) {
+                               fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
+                               /*NOTREACHED*/
+                       }
+                       
+                       edit_tempo_marker (*tempo_marker);
                        break;
+               }
 
-               case MeterMarkerItem:
-                       edit_meter_marker (item);
+               case MeterMarkerItem: {
+                       Marker* marker;
+                       MeterMarker* meter_marker;
+                       
+                       if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
+                               fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
+                               /*NOTREACHED*/
+                       }
+                       
+                       if ((meter_marker = dynamic_cast<MeterMarker*> (marker)) == 0) {
+                               fatal << _("programming error: marker for meter is not a meter marker!") << endmsg;
+                               /*NOTREACHED*/
+                       }
+                       edit_meter_marker (*meter_marker);
                        break;
+               }
 
                case RegionViewName:
                        if (clicked_regionview->name_active()) {
@@ -1652,7 +1705,7 @@ Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemT
 
                case MeterBarItem:
                        if (!_dragging_playhead) {
-                               mouse_add_new_meter_event (pixel_to_frame (event->button.x));
+                               mouse_add_new_meter_event (pixel_to_sample (event->button.x));
                        }
                        return true;
                        break;
@@ -1667,7 +1720,8 @@ Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemT
                        case AutomationTrackItem:
                                atv = dynamic_cast<AutomationTimeAxisView*>(clicked_axisview);
                                if (atv) {
-                                       atv->add_automation_event (event, where, event->button.y);
+                                       bool with_guard_points = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
+                                       atv->add_automation_event (event, where, event->button.y, with_guard_points);
                                }
                                return true;
                                break;
@@ -1686,17 +1740,20 @@ Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemT
                                */
                                AudioRegionView* arv = dynamic_cast<AudioRegionView*> (clicked_regionview);
                                if (!were_dragging && arv) {
-                                       arv->add_gain_point_event (item, event);
+                                       bool with_guard_points = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
+                                       arv->add_gain_point_event (item, event, with_guard_points);
                                }
                                return true;
                                break;
                        }
 
-                       case AutomationTrackItem:
+                       case AutomationTrackItem: {
+                               bool with_guard_points = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
                                dynamic_cast<AutomationTimeAxisView*>(clicked_axisview)->
-                                       add_automation_event (event, where, event->button.y);
+                                       add_automation_event (event, where, event->button.y, with_guard_points);
                                return true;
                                break;
+                       }
                        default:
                                break;
                        }
@@ -1788,7 +1845,7 @@ Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_
        case ControlPointItem:
                if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
                        cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
-                       cp->set_visible (true);
+                       cp->show ();
 
                        double at_x, at_y;
                        at_x = cp->get_x();
@@ -1811,8 +1868,9 @@ Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_
        case GainLineItem:
                if (mouse_mode == MouseGain) {
                        ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
-                       if (line)
-                               line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredGainLine.get();
+                       if (line) {
+                               line->set_outline_color (ARDOUR_UI::config()->get_canvasvar_EnteredGainLine());
+                       }
                        if (is_drawable()) {
                                set_canvas_cursor (_cursors->fader);
                        }
@@ -1823,7 +1881,7 @@ Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_
                if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
                        ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
                        if (line) {
-                               line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredAutomationLine.get();
+                               line->set_outline_color (ARDOUR_UI::config()->get_canvasvar_EnteredAutomationLine());
                        }
                        if (is_drawable()) {
                                set_canvas_cursor (_cursors->fader);
@@ -1845,6 +1903,17 @@ Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_
                }
                 break;
 
+       case RegionItem:
+               switch (effective_mouse_mode()) {
+               case MouseRange:
+                       set_canvas_cursor (_cursors->selector);
+                       break;
+               default:
+                       set_canvas_cursor (which_grabber_cursor());
+                       break;
+               }
+               break;  
+
        case StartSelectionTrimItem:
                if (is_drawable()) {
                        set_canvas_cursor (_cursors->left_side_trim);
@@ -1869,6 +1938,7 @@ Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_
                }
                break;
 
+
        case RegionViewName:
 
                /* when the name is not an active item, the entire name highlight is for trimming */
@@ -1923,7 +1993,7 @@ Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_
                        break;
                }
                entered_marker = marker;
-               marker->set_color_rgba (ARDOUR_UI::config()->canvasvar_EnteredMarker.get());
+               marker->set_color_rgba (ARDOUR_UI::config()->get_canvasvar_EnteredMarker());
                // fall through
        case MeterMarkerItem:
        case TempoMarkerItem:
@@ -1934,29 +2004,32 @@ Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_
 
        case FadeInHandleItem:
                if (mouse_mode == MouseObject && !internal_editing()) {
-                       ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
+                       ArdourCanvas::Rectangle *rect = dynamic_cast<ArdourCanvas::Rectangle *> (item);
                        if (rect) {
-                               rect->property_fill_color_rgba() = 0xBBBBBBAA;
+                               RegionView* rv = static_cast<RegionView*>(item->get_data ("regionview"));
+                               rect->set_fill_color (rv->get_fill_color());
+                               set_canvas_cursor (_cursors->fade_in);
                        }
-                       set_canvas_cursor (_cursors->fade_in);
                }
                break;
 
        case FadeOutHandleItem:
                if (mouse_mode == MouseObject && !internal_editing()) {
-                       ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
+                       ArdourCanvas::Rectangle *rect = dynamic_cast<ArdourCanvas::Rectangle *> (item);
                        if (rect) {
-                               rect->property_fill_color_rgba() = 0xBBBBBBAA;
+                               RegionView* rv = static_cast<RegionView*>(item->get_data ("regionview"));
+                               rect->set_fill_color (rv->get_fill_color ());
+                               set_canvas_cursor (_cursors->fade_out);
                        }
-                       set_canvas_cursor (_cursors->fade_out);
                }
                break;
        case FeatureLineItem:
-               {
-                       ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
-                       line->property_fill_color_rgba() = 0xFF0000FF;
-               }
-               break;
+       {
+               ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
+               line->set_outline_color (0xFF0000FF);
+       }
+       break;
+
        case SelectionItem:
                if ( get_smart_mode() ) {
                        set_canvas_cursor ();
@@ -1994,7 +2067,6 @@ bool
 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent*, ItemType item_type)
 {
        AutomationLine* al;
-       ControlPoint* cp;
        Marker *marker;
        Location *loc;
        RegionView* rv;
@@ -2003,13 +2075,6 @@ Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent*, ItemType item_type)
 
        switch (item_type) {
        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->get_selected()) {
-                               cp->set_visible (false);
-                       }
-               }
-
                if (is_drawable()) {
                        set_canvas_cursor (current_canvas_cursor);
                }
@@ -2036,8 +2101,9 @@ Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent*, ItemType item_type)
                al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
                {
                        ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
-                       if (line)
-                               line->property_fill_color_rgba() = al->get_line_color();
+                       if (line) {
+                               line->set_outline_color (al->get_line_color());
+                       }
                }
                if (is_drawable()) {
                        set_canvas_cursor (current_canvas_cursor);
@@ -2088,9 +2154,9 @@ Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent*, ItemType item_type)
        case FadeOutHandleItem:
                rv = static_cast<RegionView*>(item->get_data ("regionview"));
                {
-                       ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
+                       ArdourCanvas::Rectangle *rect = dynamic_cast<ArdourCanvas::Rectangle *> (item);
                        if (rect) {
-                               rect->property_fill_color_rgba() = rv->get_fill_color();
+                               rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_InactiveFadeHandle());
                        }
                }
                set_canvas_cursor (current_canvas_cursor);
@@ -2106,7 +2172,7 @@ Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent*, ItemType item_type)
        case FeatureLineItem:
                {
                        ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
-                       line->property_fill_color_rgba() = (guint) ARDOUR_UI::config()->canvasvar_ZeroLine.get();;
+                       line->set_outline_color (ARDOUR_UI::config()->get_canvasvar_ZeroLine());
                }
                break;
 
@@ -2223,7 +2289,7 @@ Editor::motion_handler (ArdourCanvas::Item* /*item*/, GdkEvent* event, bool from
                   event might do, its a good tradeoff.
                */
 
-               track_canvas->get_pointer (x, y);
+               _track_canvas->get_pointer (x, y);
        }
 
        if (current_stepping_trackview) {
@@ -2522,14 +2588,11 @@ Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* /*event*/)
 void
 Editor::reposition_zoom_rect (framepos_t start, framepos_t end)
 {
-       double x1 = frame_to_pixel (start);
-       double x2 = frame_to_pixel (end);
-       double y2 = full_canvas_height - 1.0;
-
-       zoom_rect->property_x1() = x1;
-       zoom_rect->property_y1() = 1.0;
-       zoom_rect->property_x2() = x2;
-       zoom_rect->property_y2() = y2;
+       double x1 = sample_to_pixel (start);
+       double x2 = sample_to_pixel (end);
+       double y2 = _full_canvas_height - 1.0;
+
+       zoom_rect->set (ArdourCanvas::Rect (x1, 1.0, x2, y2));
 }
 
 
@@ -2629,9 +2692,6 @@ Editor::add_region_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region
        } else {
                _drags->add (new RegionMoveDrag (this, item, region_view, selection->regions.by_layer(), false, false));
        }
-
-       /* sync the canvas to what we think is its current state */
-       update_canvas_now();
 }
 
 void
@@ -2807,7 +2867,7 @@ Editor::update_join_object_range_location (double /*x*/, double y)
        }
 
        /* XXX: maybe we should make entered_track work in all cases, rather than resorting to this */
-       pair<TimeAxisView*, int> tvp = trackview_by_y_position (y + vertical_adjustment.get_value() - canvas_timebars_vsize);
+       pair<TimeAxisView*, int> tvp = trackview_by_y_position (y);
 
        if (tvp.first) {
 
@@ -2816,7 +2876,7 @@ Editor::update_join_object_range_location (double /*x*/, double y)
 
                        double cx = 0;
                        double cy = y;
-                       rtv->canvas_display()->w2i (cx, cy);
+                       rtv->canvas_display()->canvas_to_item (cx, cy);
 
                        double const c = cy / (rtv->view()->child_height() - TimeAxisViewItem::NAME_HIGHLIGHT_SIZE);
 
@@ -2840,7 +2900,7 @@ Editor::effective_mouse_mode () const
 void
 Editor::remove_midi_note (ArdourCanvas::Item* item, GdkEvent *)
 {
-       ArdourCanvas::CanvasNoteEvent* e = dynamic_cast<ArdourCanvas::CanvasNoteEvent*> (item);
+       NoteBase* e = reinterpret_cast<NoteBase*> (item->get_data ("notebase"));
        assert (e);
 
        e->region_view().delete_note (e->note ());
@@ -2849,42 +2909,53 @@ Editor::remove_midi_note (ArdourCanvas::Item* item, GdkEvent *)
 void
 Editor::set_canvas_cursor_for_region_view (double x, RegionView* rv)
 {
-       assert (rv);
+       /* XXX: this check should not be necessary */
+       if (rv == 0) {
+               return;
+       }
 
        ArdourCanvas::Group* g = rv->get_canvas_group ();
-       ArdourCanvas::Group* p = g->get_parent_group ();
+       ArdourCanvas::Group* p = g->parent ();
 
        /* Compute x in region view parent coordinates */
        double dy = 0;
-       p->w2i (x, dy);
+       p->canvas_to_item (x, dy);
 
-       double x1, x2, y1, y2;
-       g->get_bounds (x1, y1, x2, y2);
+       boost::optional<ArdourCanvas::Rect> item_bbox = g->bounding_box ();
+       assert (item_bbox);
+       ArdourCanvas::Rect parent_bbox = g->item_to_parent (item_bbox.get ());
 
-       /* Halfway across the region */
-       double const h = (x1 + x2) / 2;
+       /* First or last 10% of region is used for trimming, if the whole
+          region is wider than 20 pixels at the current zoom level.
+       */
 
-       Trimmable::CanTrim ct = rv->region()->can_trim ();
-       if (x <= h) {
-               if (ct & Trimmable::FrontTrimEarlier) {
-                       set_canvas_cursor (_cursors->left_side_trim);
-               } else {
-                       set_canvas_cursor (_cursors->left_side_trim_right_only);
-               }
-       } else {
-               if (ct & Trimmable::EndTrimLater) {
-                       set_canvas_cursor (_cursors->right_side_trim);
-               } else {
-                       set_canvas_cursor (_cursors->right_side_trim_left_only);
+       double const w = parent_bbox.width();
+
+       if (w > 20.0 && x >= parent_bbox.x0 && x < parent_bbox.x1) {
+               
+               Trimmable::CanTrim ct = rv->region()->can_trim ();
+
+               if (((x - parent_bbox.x0) / w) < 0.10) {
+                       if (ct & Trimmable::FrontTrimEarlier) {
+                               set_canvas_cursor (_cursors->left_side_trim, true);
+                       } else {
+                               set_canvas_cursor (_cursors->left_side_trim_right_only, true);
+                       }
+               } else if (((parent_bbox.x1 - x) / w) < 0.10) {
+                       if (ct & Trimmable::EndTrimLater) {
+                               set_canvas_cursor (_cursors->right_side_trim, true);
+                       } else {
+                               set_canvas_cursor (_cursors->right_side_trim_left_only, true);
+                       }
                }
        }
 }
 
-/** Obtain the pointer position in world coordinates */
+/** Obtain the pointer position in canvas coordinates */
 void
 Editor::get_pointer_position (double& x, double& y) const
 {
        int px, py;
-       track_canvas->get_pointer (px, py);
-       track_canvas->window_to_world (px, py, x, y);
+       _track_canvas->get_pointer (px, py);
+       _track_canvas->window_to_canvas (px, py, x, y);
 }