more profound changes to canvas scrolling, in particular find appropriate ScrollGroup...
authorPaul Davis <paul@linuxaudiosystems.com>
Tue, 3 Jun 2014 19:57:56 +0000 (15:57 -0400)
committerPaul Davis <paul@linuxaudiosystems.com>
Tue, 3 Jun 2014 20:13:12 +0000 (16:13 -0400)
14 files changed:
gtk2_ardour/editor.cc
gtk2_ardour/editor.h
gtk2_ardour/editor_canvas.cc
gtk2_ardour/editor_drag.cc
gtk2_ardour/editor_mouse.cc
gtk2_ardour/editor_ops.cc
gtk2_ardour/region_view.cc
gtk2_ardour/time_axis_view.cc
libs/canvas/canvas.cc
libs/canvas/canvas/canvas.h
libs/canvas/canvas/item.h
libs/canvas/canvas/scroll_group.h
libs/canvas/item.cc
libs/canvas/scroll_group.cc

index 5d16b09c02a6a25f0862abbbbf49a764808e8b2e..f38cbb217bee81092695fae4f8b0e6cbc47fc8fb 100644 (file)
@@ -2421,21 +2421,31 @@ Editor::get_state ()
        return *node;
 }
 
-
-
-/** @param y y offset from the top of all trackviews.
+/** @param y y is in canvas coordinate space, in pixel units
+ *
  *  @return pair: TimeAxisView that y is over, layer index.
+ *
  *  TimeAxisView may be 0.  Layer index is the layer number if the TimeAxisView is valid and is
  *  in stacked or expanded region display mode, otherwise 0.
  */
 std::pair<TimeAxisView *, double>
 Editor::trackview_by_y_position (double y)
 {
-       for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
+       /* convert y into an offset within the trackview group */
+
+       ArdourCanvas::Duple top_of_trackviews_canvas = _trackview_group->item_to_canvas (ArdourCanvas::Duple (0, 0));
+       
+       if (y >= top_of_trackviews_canvas.y) {
+               
+               y -= top_of_trackviews_canvas.y;
 
-               std::pair<TimeAxisView*, double> const r = (*iter)->covers_y_position (y);
-               if (r.first) {
-                       return r;
+               for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
+                       
+                       std::pair<TimeAxisView*, double> const r = (*iter)->covers_y_position (y);
+                       
+                       if (r.first) {
+                               return r;
+                       }
                }
        }
 
index daa417543bb17ad42968d0bd8827ce451286bf1b..d486e5b9f095a6b8470df133b52e081cdafe023a 100644 (file)
@@ -744,9 +744,6 @@ class Editor : public PublicEditor, public PBD::ScopedConnectionList, public ARD
        /* The group containing all trackviews. */
        ArdourCanvas::Group* _trackview_group;
 
-       /* The group used for region motion.  Sits on top of _trackview_group */
-       ArdourCanvas::Group* _region_motion_group;
-
         /* a rect that sits at the bottom of all tracks to act as a drag-no-drop/clickable
         * target area.
         */
index 836b5356dc6c71384085553416f115226c5b6ec0..637448146461c4709bee9236ad4c8f720e81c5c1 100644 (file)
@@ -116,12 +116,8 @@ Editor::initialize_canvas ()
        CANVAS_DEBUG_NAME (time_line_group, "time line group");
 
        _trackview_group = new ArdourCanvas::Group (hv_scroll_group);
-       //_trackview_group->set_scroll_sensitivity (ArdourCanvas::Group::ScrollSensitivity (ArdourCanvas::Group::ScrollsVertically|ArdourCanvas::Group::ScrollsHorizontally));  
        CANVAS_DEBUG_NAME (_trackview_group, "Canvas TrackViews");
        
-       _region_motion_group = new ArdourCanvas::Group (_trackview_group);
-       CANVAS_DEBUG_NAME (_region_motion_group, "Canvas Region Motion");
-
        /* TIME BAR CANVAS */
        
        _time_markers_group = new ArdourCanvas::Group (h_scroll_group);
@@ -453,7 +449,7 @@ Editor::drop_paths (const RefPtr<Gdk::DragContext>& context,
 
        if (convert_drop_to_paths (paths, context, x, y, data, info, time) == 0) {
 
-               /* D-n-D coordinates are window-relative, so convert to "world" coordinates
+               /* D-n-D coordinates are window-relative, so convert to canvas coordinates
                 */
 
                ev.type = GDK_BUTTON_RELEASE;
index 1c50d8668479449e941b002a0dac345e6178c874..44b50d0131f65ce6599a4c77d6059c7e118d3d4d 100644 (file)
@@ -1,4 +1,4 @@
-/*
+*
     Copyright (C) 2009 Paul Davis
 
     This program is free software; you can redistribute it and/or modify
@@ -40,6 +40,8 @@
 #include "ardour/region_factory.h"
 #include "ardour/session.h"
 
+#include "canvas/scroll_group.h"
+
 #include "editor.h"
 #include "i18n.h"
 #include "keyboard.h"
@@ -654,7 +656,7 @@ RegionMotionDrag::motion (GdkEvent* event, bool first_move)
        pair<TimeAxisView*, double> const r = _editor->trackview_by_y_position (_drags->current_pointer_y ());
        TimeAxisView* tv = r.first;
 
-       if (tv) {
+       if (tv && tv->view()) {
                double layer = r.second;
        
                if (first_move && tv->view()->layer_display() == Stacked) {
@@ -700,22 +702,9 @@ RegionMotionDrag::motion (GdkEvent* event, bool first_move)
                }
 
                if (first_move) {
-
                        rv->drag_start (); 
-
-                       /* Reparent to a non scrolling group so that we can keep the
-                          region selection above all time axis views.
-                          Reparenting means that we will have to move the region view
-                          within its new parent, as the two parent groups have different coordinates.
-                       */
-
-                       ArdourCanvas::Group* rvg = rv->get_canvas_group();
-                       Duple rv_canvas_offset = rvg->item_to_canvas (Duple (0,0));
-
-                       rv->get_canvas_group()->reparent (_editor->_region_motion_group);
-
                        rv->fake_set_opaque (true);
-                       rvg->set_position (rv_canvas_offset);
+                       rv->raise_to_top ();
                }
 
                /* If we have moved tracks, we'll fudge the layer delta so that the
@@ -730,7 +719,13 @@ RegionMotionDrag::motion (GdkEvent* event, bool first_move)
 
                if (tv) {
 
-                       /* The TimeAxisView that this region is now on */
+                       int track_index = i->time_axis_view + delta_time_axis_view;
+                        
+                       if (track_index < 0 || track_index >= (int) _time_axis_views.size()) {
+                               continue;
+                       }
+
+                       /* The TimeAxisView that this region is now over */
                        TimeAxisView* current_tv = _time_axis_views[i->time_axis_view + delta_time_axis_view];
 
                        /* Ensure it is moved from stacked -> expanded if appropriate */
@@ -762,39 +757,50 @@ RegionMotionDrag::motion (GdkEvent* event, bool first_move)
                        if (_brushing) {
                                _editor->mouse_brush_insert_region (rv, pending_region_position);
                        } else {
-                               double x = 0;
-                               double y = 0;
-
-                               /* Get the y coordinate of the top of the track that this region is now on */
-                               current_tv->canvas_display()->item_to_canvas (x, y);
+                               Duple track_origin;
 
+                               /* Get the y coordinate of the top of the track that this region is now over */
+                               track_origin = current_tv->canvas_display()->item_to_canvas (track_origin);
+                               
                                /* And adjust for the layer that it should be on */
                                StreamView* cv = current_tv->view ();
                                switch (cv->layer_display ()) {
                                case Overlaid:
                                        break;
                                case Stacked:
-                                       y += (cv->layers() - i->layer - 1) * cv->child_height ();
+                                       track_origin.y += (cv->layers() - i->layer - 1) * cv->child_height ();
                                        break;
                                case Expanded:
-                                       y += (cv->layers() - i->layer - 0.5) * 2 * cv->child_height ();
+                                       track_origin.y += (cv->layers() - i->layer - 0.5) * 2 * cv->child_height ();
                                        break;
                                }
 
+                               /* need to get the parent of the regionview
+                                * canvas group and get its position in
+                                * equivalent coordinate space as the trackview
+                                * we are now dragging over.
+                                */
+                               
                                /* Now move the region view */
-                               rv->move (x_delta, y - rv->get_canvas_group()->position().y);
+                               rv->move (x_delta, track_origin.y - rv->get_canvas_group()->item_to_canvas (Duple (0, 0)).y);
                        }
                } else {
-                       double y = 0;
-                       double x = 0;
-                       
-                       TimeAxisView* last = _time_axis_views.back();
-                       last->canvas_display()->item_to_canvas (x, y);
-                       y += last->effective_height();
-                       rv->move (x_delta, y - rv->get_canvas_group()->position().y);
-                       i->time_axis_view = -1;
-               }
 
+                       /* Only move the region into the empty dropzone at the bottom if the pointer
+                        * is down there.
+                        */
+
+                       if (_drags->current_pointer_y() >= _editor->get_trackview_group()->item_to_canvas (Duple (0,0)).y) {
+                               Duple track_origin;
+                               
+                               TimeAxisView* last = _time_axis_views.back();
+                               track_origin = last->canvas_display()->item_to_canvas (track_origin);
+                               track_origin.y += last->effective_height();
+                               rv->move (x_delta, track_origin.y - rv->get_canvas_group()->item_to_canvas (Duple (0,0)).y);
+                               i->time_axis_view = -1;
+                       }
+               }
+               
        } /* foreach region */
 
        _total_x_delta += x_delta;
index 7eb801400a7764efd26e9f03f6c380dc01bd9b95..935c90272ddc8c5ed9e95cfbf88b822bb597ac94 100644 (file)
@@ -140,12 +140,6 @@ Editor::window_event_sample (GdkEvent const * event, double* pcx, double* pcy) c
                return 0;
        }
 
-       /* adjust for scrolling (the canvas used by Ardour has global scroll
-        * disabled, so we have to do the adjustment explicitly).
-        */
-
-       d.translate (ArdourCanvas::Duple (horizontal_adjustment.get_value(), vertical_adjustment.get_value()));
-
        /* event coordinates are in window units, so convert to canvas
         */
 
@@ -2710,8 +2704,6 @@ Editor::add_region_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region
                return;
        }
 
-       _region_motion_group->raise_to_top ();
-
        if (Config->get_edit_mode() == Splice) {
                _drags->add (new RegionSpliceDrag (this, item, region_view, selection->regions.by_layer()));
        } else {
@@ -2728,8 +2720,6 @@ Editor::add_region_copy_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* r
                return;
        }
 
-       _region_motion_group->raise_to_top ();
-
        _drags->add (new RegionMoveDrag (this, item, region_view, selection->regions.by_layer(), false, true));
 }
 
index e6b4fee43ef733052142ba050813a517a41d6489..0c689452dee85451f8d03c629b97058a83f10c18 100644 (file)
@@ -1333,8 +1333,6 @@ Editor::scroll_tracks_up_line ()
 bool
 Editor::scroll_down_one_track ()
 {
-       double vertical_pos = vertical_adjustment.get_value () + vertical_adjustment.get_page_size() - 1.0; 
-
        TrackViewList::reverse_iterator next = track_views.rend();
        std::pair<TimeAxisView*,double> res;
 
@@ -1343,7 +1341,8 @@ Editor::scroll_down_one_track ()
                        continue;
                }
                
-               res = (*t)->covers_y_position (vertical_pos);
+               /* find the trackview at the bottom of the trackview group */
+               res = (*t)->covers_y_position (_visible_canvas_height);
 
                if (res.first) {
                        break;
@@ -1376,7 +1375,8 @@ Editor::scroll_up_one_track ()
                        continue;
                }
 
-               res = (*t)->covers_y_position(vertical_pos);
+               /* find the trackview at the top of the trackview group */
+               res = (*t)->covers_y_position (0);
                
                if (res.first) {
                        break;
index 127ce79b8462636498e832e890e509a3f3ea2a2d..dfdf0651f62104e00fb2d37c31ce3a0bc7db748d 100644 (file)
@@ -694,7 +694,18 @@ RegionView::move (double x_delta, double y_delta)
                return;
        }
 
-       get_canvas_group()->move (ArdourCanvas::Duple (x_delta, y_delta));
+       /* items will not prevent Item::move() moving
+        * them to a negative x-axis coordinate, which
+        * is legal, but we don't want that here.
+        */
+
+       ArdourCanvas::Item *item = get_canvas_group ();
+       
+       if (item->position().x + x_delta < 0) {
+               x_delta = -item->position().x; /* move it to zero */
+       }
+
+       item->move (ArdourCanvas::Duple (x_delta, y_delta));
 
        /* note: ghosts never leave their tracks so y_delta for them is always zero */
 
index f67ca6a3cea5e70c0eda4861612a17912732b588..e36b78daaff45de3b893120eedb298aaf7f4be66 100644 (file)
@@ -1151,9 +1151,12 @@ TimeAxisView::color_handler ()
 }
 
 /** @return Pair: TimeAxisView, layer index.
- * TimeAxisView is non-0 if this object covers y, or one of its children does.
+ * TimeAxisView is non-0 if this object covers @param y, or one of its children
+ * does. @param y is an offset from the top of the trackview area.
+ *
  * If the covering object is a child axis, then the child is returned.
  * TimeAxisView is 0 otherwise.
+ *
  * Layer index is the layer number (possibly fractional) if the TimeAxisView is valid
  * and is in stacked or expanded * region display mode, otherwise 0.
  */
index e6feca641620efda8408d3137648f6578151beb1..8638344966724bf2f2d39e134d6c28966ff14fe1 100644 (file)
@@ -207,46 +207,63 @@ Canvas::item_changed (Item* item, boost::optional<Rect> pre_change_bounding_box)
 Duple
 Canvas::window_to_canvas (Duple const & d) const
 {
+       /* Find the scroll group that covers d (a window coordinate). Scroll groups are only allowed
+        * as children of the root group, so we just scan its first level
+        * children and see what we can find.
+        */
+
+       std::list<Item*> const& root_children (_root.items());
+       ScrollGroup* sg = 0;
+
+       for (std::list<Item*>::const_iterator i = root_children.begin(); i != root_children.end(); ++i) {
+               if (((sg = dynamic_cast<ScrollGroup*>(*i)) != 0) && sg->covers_window (d)) {
+                       break;
+               }
+       }
+
+       if (sg) {
+               return d.translate (sg->scroll_offset());
+       }
+
+       /* fallback to global canvas offset ... it would be nice to remove this */
+
        return d.translate (_scroll_offset);
 }
 
 Duple
 Canvas::canvas_to_window (Duple const & d, bool rounded) const
 {
-       Duple wd = d.translate (-_scroll_offset);
+       /* Find the scroll group that covers d (a canvas coordinate). Scroll groups are only allowed
+        * as children of the root group, so we just scan its first level
+        * children and see what we can find.
+        */
 
-       /* Note that this intentionally almost always returns integer coordinates */
+       std::list<Item*> const& root_children (_root.items());
+       ScrollGroup* sg = 0;
+       Duple wd;
 
-       if (rounded) {
-               wd.x = round (wd.x);
-               wd.y = round (wd.y);
+       for (std::list<Item*>::const_iterator i = root_children.begin(); i != root_children.end(); ++i) {
+               if (((sg = dynamic_cast<ScrollGroup*>(*i)) != 0) && sg->covers_canvas (d)) {
+                       break;
+               }
        }
+       
 
-       return wd;
-}
-
-Rect
-Canvas::window_to_canvas (Rect const & r) const
-{
-       return r.translate (Duple (_scroll_offset.x, _scroll_offset.y));
-}
-
-Rect
-Canvas::canvas_to_window (Rect const & r, bool rounded) const
-{
-       Rect wr = r.translate (Duple (-_scroll_offset.x, -_scroll_offset.y));
+       if (sg) {
+               wd = d.translate (-sg->scroll_offset());
+       } else {
+               wd = d.translate (-_scroll_offset);
+       }
 
        /* Note that this intentionally almost always returns integer coordinates */
 
        if (rounded) {
-               wr.x0 = round (wr.x0);
-               wr.x1 = round (wr.x1);
-               wr.y0 = round (wr.y0);
-               wr.y1 = round (wr.y1);
+               wd.x = round (wd.x);
+               wd.y = round (wd.y);
        }
 
-       return wr;
-}      
+       return wd;
+}
 
 /** Called when an item has moved.
  *  @param item Item that has moved.
index 1f801c3f00c4686fcc56c68bdd83f07a1e090ee4..187a773104f3b64bb8e201f92dc433424bb99006 100644 (file)
@@ -90,8 +90,7 @@ public:
        void item_moved (Item *, boost::optional<Rect>);
 
         virtual Cairo::RefPtr<Cairo::Context> context () = 0;
-        Rect canvas_to_window (Rect const&, bool rounded = true) const;
-        Rect window_to_canvas (Rect const&) const;
+
         Duple canvas_to_window (Duple const&, bool rounded = true) const;
         Duple window_to_canvas (Duple const&) const;
 
index fd41ddd4c5c3a68efbe8a76dd012ee02affcaedc..5483c5124cc3cb4975048a930a575c367a854624 100644 (file)
@@ -129,6 +129,10 @@ public:
                return _position;
        }
 
+       Duple window_origin() const;
+
+       ScrollGroup* scroll_parent() const { return _scroll_parent; }
+
        boost::optional<Rect> bounding_box () const;
         Coord height() const;
         Coord width() const;
index 2df491e93b01237e339da47d675fde1cda40e2ae..d33e9b908061cdd7273751ab6f863085b379b718 100644 (file)
@@ -37,6 +37,9 @@ class LIBCANVAS_API ScrollGroup : public Group
        void scroll_to (Duple const& d);
        Duple scroll_offset() const { return _scroll_offset; }
 
+       bool covers_canvas (Duple const& d) const;
+       bool covers_window (Duple const& d) const;
+
   private:
        ScrollSensitivity _scroll_sensitivity;
        Duple             _scroll_offset;
index 3b13a12ca82e3874b448fdd8af92e20f2ab0eab7..ddc05a817a51a96bb3c12f1529f4b93a8dcdcd3b 100644 (file)
@@ -82,6 +82,20 @@ Item::~Item ()
        }
 }
 
+Duple
+Item::window_origin () const 
+{
+       /* This is slightly subtle. Our _position is in the coordinate space of 
+          our parent. So to find out where that is in window coordinates, we
+          have to ask our parent.
+       */
+       if (_parent) {
+               return _parent->item_to_window (_position);
+       } else {
+               return _parent->item_to_window (Duple (0,0));
+       }
+}
+
 ArdourCanvas::Rect
 Item::item_to_parent (ArdourCanvas::Rect const & r) const
 {
index 76bca50aeee473e6a7fef4a300606cc6e29d63f8..615be8007cb91f06fa423da245d1e710f794ee07 100644 (file)
@@ -49,3 +49,29 @@ ScrollGroup::scroll_to (Duple const& d)
                _scroll_offset.y = d.y;
        }
 }
+
+bool
+ScrollGroup::covers_canvas (Duple const& d) const
+{
+       boost::optional<Rect> r = bounding_box ();
+
+       if (!r) {
+               return false;
+       }
+
+       return r->contains (d);
+}
+
+bool
+ScrollGroup::covers_window (Duple const& d) const
+{
+       boost::optional<Rect> r = bounding_box ();
+
+       if (!r) {
+               return false;
+       }
+
+       Rect w = r->translate (-_scroll_offset);
+
+       return w.contains (d);
+}