changeset 551:c2ba2796cbee sv-v1.7

* Big improvements to editing behaviour in note and region models
author Chris Cannam
date Fri, 02 Oct 2009 13:56:10 +0000
parents d666f5f8b154
children 2e8194a30f40
files layer/NoteLayer.cpp layer/NoteLayer.h layer/RegionLayer.cpp layer/RegionLayer.h view/Pane.cpp
diffstat 5 files changed, 206 insertions(+), 166 deletions(-) [+]
line wrap: on
line diff
--- a/layer/NoteLayer.cpp	Mon Sep 28 12:29:12 2009 +0000
+++ b/layer/NoteLayer.cpp	Fri Oct 02 13:56:10 2009 +0000
@@ -36,6 +36,7 @@
 
 #include <iostream>
 #include <cmath>
+#include <utility>
 
 NoteLayer::NoteLayer() :
     SingleColourLayer(),
@@ -414,7 +415,7 @@
     NoteModel::PointList onPoints = m_model->getPoints(frame);
     if (onPoints.empty()) return false;
 
-    std::cerr << "frame " << frame << ": " << onPoints.size() << " candidate points" << std::endl;
+//    std::cerr << "frame " << frame << ": " << onPoints.size() << " candidate points" << std::endl;
 
     int nearestDistance = -1;
 
@@ -731,12 +732,12 @@
     if (max == min) max = min + 1.0;
 
     QPoint localPos;
-    long illuminateFrame = -1;
+    NoteModel::Point illuminatePoint(0);
+    bool shouldIlluminate = false;
 
     if (v->shouldIlluminateLocalFeatures(this, localPos)) {
-	NoteModel::PointList localPoints =
-	    getLocalPoints(v, localPos.x());
-	if (!localPoints.empty()) illuminateFrame = localPoints.begin()->frame;
+        shouldIlluminate = getPointToDrag(v, localPos.x(), localPos.y(),
+                                          illuminatePoint);
     }
 
     paint.save();
@@ -761,18 +762,30 @@
 	paint.setPen(getBaseQColor());
 	paint.setBrush(brushColour);
 
-	if (illuminateFrame == p.frame) {
-	    if (localPos.y() >= y - h && localPos.y() < y) {
-		paint.setPen(v->getForeground());
-		paint.setBrush(v->getForeground());
-	    }
+	if (shouldIlluminate &&
+            // "illuminatePoint == p"
+            !NoteModel::Point::Comparator()(illuminatePoint, p) &&
+            !NoteModel::Point::Comparator()(p, illuminatePoint)) {
+
+            paint.setPen(v->getForeground());
+            paint.setBrush(v->getForeground());
+
+            QString vlabel = QString("%1%2").arg(p.value).arg(m_model->getScaleUnits());
+            v->drawVisibleText(paint, 
+                               x - paint.fontMetrics().width(vlabel) - 2,
+                               y + paint.fontMetrics().height()/2
+                                 - paint.fontMetrics().descent(), 
+                               vlabel, View::OutlinedText);
+
+            QString hlabel = RealTime::frame2RealTime
+                (p.frame, m_model->getSampleRate()).toText(true).c_str();
+            v->drawVisibleText(paint, 
+                               x,
+                               y - h/2 - paint.fontMetrics().descent() - 2,
+                               hlabel, View::OutlinedText);
 	}
 	
 	paint.drawRect(x, y - h/2, w, h);
-
-///	if (p.label != "") {
-///	    paint.drawText(x + 5, y - paint.fontMetrics().height() + paint.fontMetrics().ascent(), p.label);
-///	}
     }
 
     paint.restore();
@@ -846,11 +859,6 @@
 {
     if (!m_model) return;
 
-//    NoteModel::PointList points = getLocalPoints(v, e->x());
-//    if (points.empty()) return;
-
-//    m_editingPoint = *points.begin();
-
     if (!getPointToDrag(v, e->x(), e->y(), m_editingPoint)) return;
 
     if (m_editingCommand) {
@@ -873,13 +881,6 @@
 
     m_editing = false;
 
-/*
-    NoteModel::PointList points = getLocalPoints(v, e->x());
-    if (points.empty()) return;
-    if (points.begin()->frame != m_editingPoint.frame ||
-        points.begin()->value != m_editingPoint.value) return;
-*/
-
     NoteModel::Point p(0);
     if (!getPointToDrag(v, e->x(), e->y(), p)) return;
     if (p.frame != m_editingPoint.frame || p.value != m_editingPoint.value) return;
@@ -900,22 +901,20 @@
 
     if (!m_model) return;
 
-/*
-    NoteModel::PointList points = getLocalPoints(v, e->x());
-    if (points.empty()) return;
-
-    m_editingPoint = *points.begin();
-*/
-
     if (!getPointToDrag(v, e->x(), e->y(), m_editingPoint)) return;
     m_originalPoint = m_editingPoint;
 
+    m_dragPointX = v->getXForFrame(m_editingPoint.frame);
+    m_dragPointY = getYForValue(v, m_editingPoint.value);
+
     if (m_editingCommand) {
 	finish(m_editingCommand);
 	m_editingCommand = 0;
     }
 
     m_editing = true;
+    m_dragStartX = e->x();
+    m_dragStartY = e->y();
 }
 
 void
@@ -925,11 +924,16 @@
 
     if (!m_model || !m_editing) return;
 
-    long frame = v->getFrameForX(e->x());
+    int xdist = e->x() - m_dragStartX;
+    int ydist = e->y() - m_dragStartY;
+    int newx = m_dragPointX + xdist;
+    int newy = m_dragPointY + ydist;
+
+    long frame = v->getFrameForX(newx);
     if (frame < 0) frame = 0;
     frame = frame / m_model->getResolution() * m_model->getResolution();
 
-    float value = getValueForY(v, e->y());
+    float value = getValueForY(v, newy);
 
     if (!m_editingCommand) {
 	m_editingCommand = new NoteModel::EditCommand(m_model,
@@ -974,10 +978,6 @@
 NoteLayer::editOpen(View *v, QMouseEvent *e)
 {
     if (!m_model) return false;
-/*
-    NoteModel::PointList points = getLocalPoints(v, e->x());
-    if (points.empty()) return false;
-*/
 
     NoteModel::Point note(0);
     if (!getPointToDrag(v, e->x(), e->y(), note)) return false;
--- a/layer/NoteLayer.h	Mon Sep 28 12:29:12 2009 +0000
+++ b/layer/NoteLayer.h	Fri Oct 02 13:56:10 2009 +0000
@@ -139,6 +139,10 @@
 
     NoteModel *m_model;
     bool m_editing;
+    int m_dragPointX;
+    int m_dragPointY;
+    int m_dragStartX;
+    int m_dragStartY;
     NoteModel::Point m_originalPoint;
     NoteModel::Point m_editingPoint;
     NoteModel::EditCommand *m_editingCommand;
--- a/layer/RegionLayer.cpp	Mon Sep 28 12:29:12 2009 +0000
+++ b/layer/RegionLayer.cpp	Fri Oct 02 13:56:10 2009 +0000
@@ -236,24 +236,23 @@
 RegionLayer::recalcSpacing()
 {
     m_spacingMap.clear();
+    m_distributionMap.clear();
     if (!m_model) return;
 
-    std::cerr << "RegionLayer::recalcSpacing" << std::endl;
-
-    std::set<float> values;
+//    std::cerr << "RegionLayer::recalcSpacing" << std::endl;
 
     for (RegionModel::PointList::const_iterator i = m_model->getPoints().begin();
          i != m_model->getPoints().end(); ++i) {
-        values.insert(i->value);
-        std::cerr << "RegionLayer::recalcSpacing: value found: " << i->value << std::endl;
+        m_distributionMap[i->value]++;
+//        std::cerr << "RegionLayer::recalcSpacing: value found: " << i->value << " (now have " << m_distributionMap[i->value] << " of this value)" <<  std::endl;
     }
 
     int n = 0;
 
-    for (std::set<float>::const_iterator i = values.begin();
-         i != values.end(); ++i) {
-        m_spacingMap[*i] = n++;
-        std::cerr << "RegionLayer::recalcSpacing: " << *i << " -> " << m_spacingMap[*i] << std::endl;
+    for (SpacingMap::const_iterator i = m_distributionMap.begin();
+         i != m_distributionMap.end(); ++i) {
+        m_spacingMap[i->first] = n++;
+//        std::cerr << "RegionLayer::recalcSpacing: " << i->first << " -> " << m_spacingMap[i->first] << std::endl;
     }
 }
 
@@ -575,8 +574,8 @@
     // we return an inexact result here (float rather than int)
     int h = v->height();
     int n = m_spacingMap.size();
-    // from y = h - ((h * i) / n) + (h / (2 * n)) as above
-    float vh = ((h + (h / float(2 * n)) - y) * n) / h;
+    // from y = h - ((h * i) / n) + (h / (2 * n)) as above (vh taking place of i)
+    float vh = float(2*h*n - h - 2*n*y) / float(2*h);
     return vh;
 }
 
@@ -596,7 +595,7 @@
 
         int y = spacingIndexToY(v, i->second);
 
-        std::cerr << "RegionLayer::getYForValue: value " << val << " -> i->second " << i->second << " -> y " << y << std::endl;
+//        std::cerr << "RegionLayer::getYForValue: value " << val << " -> i->second " << i->second << " -> y " << y << std::endl;
         return y;
 
 
@@ -616,7 +615,7 @@
 }
 
 float
-RegionLayer::getValueForY(View *v, int y) const
+RegionLayer::getValueForY(View *v, int y, int avoid) const
 {
     float min = 0.0, max = 0.0;
     bool logarithmic = false;
@@ -649,7 +648,7 @@
         int dist = iy - y;
         int gap = h / n; // between region lines
 
-        std::cerr << "getValueForY: y = " << y << ", n = " << n << ", vh = " << vh << ", iy = " << iy << ", dist = " << dist << ", gap = " << gap << std::endl;
+//        std::cerr << "getValueForY: y = " << y << ", vh = " << vh << ", ivh = " << ivh << " of " << n << ", iy = " << iy << ", dist = " << dist << ", gap = " << gap << std::endl;
 
         SpacingMap::const_iterator i = m_spacingMap.begin();
         while (i != m_spacingMap.end()) {
@@ -658,34 +657,42 @@
         }
         if (i == m_spacingMap.end()) i = m_spacingMap.begin();
 
+//        std::cerr << "nearest existing value = " << i->first << " at " << iy << std::endl;
+
         float val = 0;
 
-        if (dist > -gap/3 && dist < gap/3) {
-            // snap
-            val = i->first;
-            std::cerr << "snapped to " << val << std::endl;
-        } else if (dist < 0) {
+//        std::cerr << "note: avoid = " << avoid << ", i->second = " << i->second << std::endl;
+
+        if (dist < -gap/3 &&
+            ((avoid == -1) ||
+             (avoid != i->second && avoid != i->second - 1))) {
             // bisect gap to prior
             if (i == m_spacingMap.begin()) {
                 val = i->first - 1.f;
-                std::cerr << "extended down to " << val << std::endl;
+//                std::cerr << "extended down to " << val << std::endl;
             } else {
                 SpacingMap::const_iterator j = i;
                 --j;
                 val = (i->first + j->first) / 2;
-                std::cerr << "bisected down to " << val << std::endl;
+//                std::cerr << "bisected down to " << val << std::endl;
             }
-        } else {
+        } else if (dist > gap/3 &&
+                   ((avoid == -1) ||
+                    (avoid != i->second && avoid != i->second + 1))) {
             // bisect gap to following
             SpacingMap::const_iterator j = i;
             ++j;
             if (j == m_spacingMap.end()) {
                 val = i->first + 1.f;
-                std::cerr << "extended up to " << val << std::endl;
+//                std::cerr << "extended up to " << val << std::endl;
             } else {
                 val = (i->first + j->first) / 2;
-                std::cerr << "bisected up to " << val << std::endl;
+//                std::cerr << "bisected up to " << val << std::endl;
             }
+        } else {
+            // snap
+            val = i->first;
+//            std::cerr << "snapped to " << val << std::endl;
         }            
 
         return val;
@@ -764,12 +771,12 @@
     if (max == min) max = min + 1.0;
 
     QPoint localPos;
-    long illuminateFrame = -1;
+    RegionModel::Point illuminatePoint(0);
+    bool shouldIlluminate = false;
 
     if (v->shouldIlluminateLocalFeatures(this, localPos)) {
-	RegionModel::PointList localPoints =
-	    getLocalPoints(v, localPos.x());
-	if (!localPoints.empty()) illuminateFrame = localPoints.begin()->frame;
+        shouldIlluminate = getPointToDrag(v, localPos.x(), localPos.y(),
+                                          illuminatePoint);
     }
 
     paint.save();
@@ -781,11 +788,6 @@
     //!!! if it does have distinct values, we should still ensure y
     //!!! coord is never completely flat on the top or bottom
 
-    int textY = 0;
-    if (m_plotStyle == PlotSegmentation) {
-        textY = v->getTextLabelHeight(this, paint);
-    }
-    
     int fontHeight = paint.fontMetrics().height();
     int fontAscent = paint.fontMetrics().ascent();
 
@@ -809,13 +811,6 @@
             if (nx < ex) ex = nx;
         }
 
-        if (m_plotStyle != PlotSegmentation) {
-            textY = y - fontHeight + fontAscent;
-            if (textY < fontAscent + 1) {
-                textY = fontAscent + 1;
-            }
-        }
-
 	if (m_model->getValueQuantization() != 0.0) {
 	    h = y - getYForValue(v, p.value + m_model->getValueQuantization());
 	    if (h < 3) h = 3;
@@ -831,37 +826,76 @@
             paint.setBrush(brushColour);
         }
 
+        bool illuminated = false;
+
 	if (m_plotStyle == PlotSegmentation) {
 
 	    if (ex <= x) continue;
 
-	    if (illuminateFrame != p.frame &&
-		(ex < x + 5 || x >= v->width() - 1)) {
-		paint.setPen(Qt::NoPen);
-	    }
+            if (!shouldIlluminate ||
+                // "illuminatePoint != p"
+                RegionModel::Point::Comparator()(illuminatePoint, p) ||
+                RegionModel::Point::Comparator()(p, illuminatePoint)) {
+
+//	    if (illuminateFrame != p.frame &&
+//		(ex < x + 5 || x >= v->width() - 1)) {
+                paint.setPen(Qt::NoPen);
+            }
 
 	    paint.drawRect(x, -1, ex - x, v->height() + 1);
 
 	} else {
 
-            if (illuminateFrame == p.frame) {
-                if (localPos.y() >= y - h && localPos.y() < y) {
-                    paint.setPen(v->getForeground());
-                    paint.setBrush(v->getForeground());
-                }
+            if (shouldIlluminate &&
+                // "illuminatePoint == p"
+                !RegionModel::Point::Comparator()(illuminatePoint, p) &&
+                !RegionModel::Point::Comparator()(p, illuminatePoint)) {
+
+                paint.setPen(v->getForeground());
+                paint.setBrush(v->getForeground());
+
+                QString vlabel = QString("%1%2").arg(p.value).arg(m_model->getScaleUnits());
+                v->drawVisibleText(paint, 
+                                   x - paint.fontMetrics().width(vlabel) - 2,
+                                   y + paint.fontMetrics().height()/2
+                                   - paint.fontMetrics().descent(), 
+                                   vlabel, View::OutlinedText);
+                
+                QString hlabel = RealTime::frame2RealTime
+                    (p.frame, m_model->getSampleRate()).toText(true).c_str();
+                v->drawVisibleText(paint, 
+                                   x,
+                                   y - h/2 - paint.fontMetrics().descent() - 2,
+                                   hlabel, View::OutlinedText);
+
+                illuminated = true;
             }
-	
+            
             paint.drawLine(x, y-1, x + w, y-1);
             paint.drawLine(x, y+1, x + w, y+1);
             paint.drawLine(x, y - h/2, x, y + h/2);
             paint.drawLine(x+w, y - h/2, x + w, y + h/2);
         }
 
-        QString label = p.label;
-	if (label == "") {
-            label = QString("[%1]").arg(p.value);
+        if (!illuminated) {
+            QString label = p.label;
+            if (label == "") {
+                label = QString("%1%2").arg(p.value).arg(m_model->getScaleUnits());
+            }
+
+            if (m_plotStyle != PlotSegmentation) {
+                v->drawVisibleText(paint,
+                                   x - paint.fontMetrics().width(label) - 2,
+                                   y + paint.fontMetrics().height()/2
+                                     - paint.fontMetrics().descent(), 
+                                   label, View::OutlinedText);
+            } else {
+                v->drawVisibleText(paint,
+                                   x + 5,
+                                   v->getTextLabelHeight(this, paint),
+                                   label, View::OutlinedText);
+            }
         }
-        v->drawVisibleText(paint, x + 5, textY, label, View::OutlinedText);
     }
 
     paint.restore();
@@ -870,8 +904,6 @@
 void
 RegionLayer::drawStart(View *v, QMouseEvent *e)
 {
-//    std::cerr << "RegionLayer::drawStart(" << e->x() << "," << e->y() << ")" << std::endl;
-
     if (!m_model) return;
 
     long frame = v->getFrameForX(e->x());
@@ -896,8 +928,6 @@
 void
 RegionLayer::drawDrag(View *v, QMouseEvent *e)
 {
-//    std::cerr << "RegionLayer::drawDrag(" << e->x() << "," << e->y() << ")" << std::endl;
-
     if (!m_model || !m_editing) return;
 
     long frame = v->getFrameForX(e->x());
@@ -922,13 +952,12 @@
     m_editingPoint.duration = newDuration;
     m_editingCommand->addPoint(m_editingPoint);
 
-//    recalcSpacing();
+    recalcSpacing();
 }
 
 void
 RegionLayer::drawEnd(View *, QMouseEvent *)
 {
-//    std::cerr << "RegionLayer::drawEnd(" << e->x() << "," << e->y() << ")" << std::endl;
     if (!m_model || !m_editing) return;
     finish(m_editingCommand);
     m_editingCommand = 0;
@@ -943,12 +972,7 @@
     if (!m_model) return;
 
     if (!getPointToDrag(v, e->x(), e->y(), m_editingPoint)) return;
-/*
-    RegionModel::PointList points = getLocalPoints(v, e->x());
-    if (points.empty()) return;
 
-    m_editingPoint = *points.begin();
-*/
     if (m_editingCommand) {
 	finish(m_editingCommand);
 	m_editingCommand = 0;
@@ -969,12 +993,7 @@
     if (!m_model || !m_editing) return;
 
     m_editing = false;
-/*
-    RegionModel::PointList points = getLocalPoints(v, e->x());
-    if (points.empty()) return;
-    if (points.begin()->frame != m_editingPoint.frame ||
-        points.begin()->value != m_editingPoint.value) return;
-*/
+
     RegionModel::Point p(0);
     if (!getPointToDrag(v, e->x(), e->y(), p)) return;
     if (p.frame != m_editingPoint.frame || p.value != m_editingPoint.value) return;
@@ -993,22 +1012,15 @@
 void
 RegionLayer::editStart(View *v, QMouseEvent *e)
 {
-    std::cerr << "RegionLayer::editStart(" << e->x() << "," << e->y() << ")" << std::endl;
-
     if (!m_model) return;
 
-//    OrderedPointList opoints = getNearbyPoints(v, e->x(), e->y());
-//    RegionLayer:
-
-//    RegionModel::PointList points = getLocalPoints(v, e->x());
-//    if (points.empty()) return;
-
-//    m_editingPoint = *points.begin();
-    
     if (!getPointToDrag(v, e->x(), e->y(), m_editingPoint)) {
         return;
     }
 
+    m_dragPointX = v->getXForFrame(m_editingPoint.frame);
+    m_dragPointY = getYForValue(v, m_editingPoint.value);
+
     m_originalPoint = m_editingPoint;
 
     if (m_editingCommand) {
@@ -1017,36 +1029,39 @@
     }
 
     m_editing = true;
-    m_dragYOrigin = e->y();
-    m_dragYRebase = e->y();
+    m_dragStartX = e->x();
+    m_dragStartY = e->y();
     recalcSpacing();
 }
 
 void
 RegionLayer::editDrag(View *v, QMouseEvent *e)
 {
-    std::cerr << "RegionLayer::editDrag(" << e->x() << "," << e->y() << ")" << std::endl;
-
     if (!m_model || !m_editing) return;
 
-    long frame = v->getFrameForX(e->x());
+    int xdist = e->x() - m_dragStartX;
+    int ydist = e->y() - m_dragStartY;
+    int newx = m_dragPointX + xdist;
+    int newy = m_dragPointY + ydist;
+
+    long frame = v->getFrameForX(newx);
     if (frame < 0) frame = 0;
     frame = frame / m_model->getResolution() * m_model->getResolution();
 
-    int activeY = e->y() + (m_dragYRebase - m_dragYOrigin);
-    float value = getValueForY(v, activeY);
+    // Do not bisect between two values, if one of those values is
+    // that of the point we're actually moving ...
+    int avoid = m_spacingMap[m_editingPoint.value];
+
+    // ... unless there are other points with the same value
+    if (m_distributionMap[m_editingPoint.value] > 1) avoid = -1;
+
+    float value = getValueForY(v, newy, avoid);
 
     if (!m_editingCommand) {
 	m_editingCommand = new RegionModel::EditCommand(m_model,
 						      tr("Drag Region"));
     }
 
-    if (m_verticalScale == EqualSpaced) {
-        if (getYForValue(v, value) != getYForValue(v, m_editingPoint.value)) {
-            m_dragYRebase = e->y();
-        }
-    }
-
     m_editingCommand->deletePoint(m_editingPoint);
     m_editingPoint.frame = frame;
     m_editingPoint.value = value;
@@ -1057,7 +1072,6 @@
 void
 RegionLayer::editEnd(View *, QMouseEvent *e)
 {
-    std::cerr << "RegionLayer::editEnd(" << e->x() << "," << e->y() << ")" << std::endl;
     if (!m_model || !m_editing) return;
 
     if (m_editingCommand) {
--- a/layer/RegionLayer.h	Mon Sep 28 12:29:12 2009 +0000
+++ b/layer/RegionLayer.h	Fri Oct 02 13:56:10 2009 +0000
@@ -120,7 +120,7 @@
 protected:
     void getScaleExtents(View *, float &min, float &max, bool &log) const;
     int getYForValue(View *v, float value) const;
-    float getValueForY(View *v, int y) const;
+    float getValueForY(View *v, int y, int avoid = -1) const;
     QColor getColourForValue(View *v, float value) const;
 
     virtual int getDefaultColourHint(bool dark, bool &impose);
@@ -131,8 +131,10 @@
 
     RegionModel *m_model;
     bool m_editing;
-    int m_dragYOrigin;
-    int m_dragYRebase;
+    int m_dragPointX;
+    int m_dragPointY;
+    int m_dragStartX;
+    int m_dragStartY;
     RegionModel::Point m_originalPoint;
     RegionModel::Point m_editingPoint;
     RegionModel::EditCommand *m_editingCommand;
@@ -141,9 +143,13 @@
     PlotStyle m_plotStyle;
 
     typedef std::map<float, int> SpacingMap;
+
     // region value -> ordering
     SpacingMap m_spacingMap;
 
+    // region value -> number of regions with this value
+    SpacingMap m_distributionMap;
+
     int spacingIndexToY(View *v, int i) const;
     float yToSpacingIndex(View *v, int y) const;
 
--- a/view/Pane.cpp	Mon Sep 28 12:29:12 2009 +0000
+++ b/view/Pane.cpp	Fri Oct 02 13:56:10 2009 +0000
@@ -1558,38 +1558,29 @@
 
     } else if (mode == ViewManager::EditMode) {
 
-        if (m_editing) {
-            if (!editSelectionDrag(e)) {
-                Layer *layer = getSelectedLayer();
-                if (layer && layer->isLayerEditable()) {
-                    layer->editDrag(this, e);
-                }
-            }
+        bool resist = true;
+
+        if ((e->modifiers() & Qt::ShiftModifier)) {
+            m_shiftPressed = true;
+            // ... but don't set it false if shift has been
+            // released -- we want the state when we started
+            // dragging to be used most of the time
         }
 
+        if (m_shiftPressed) resist = false;
+
+        m_dragMode = updateDragMode
+            (m_dragMode,
+             m_clickPos,
+             e->pos(),
+             true,    // can move horiz
+             true,    // can move vert
+             resist,  // resist horiz
+             resist); // resist vert
+
         if (!m_editing) {
 
-            bool resist = true;
-
-            if ((e->modifiers() & Qt::ShiftModifier)) {
-                m_shiftPressed = true;
-                // ... but don't set it false if shift has been
-                // released -- we want the state when we started
-                // dragging to be used most of the time
-            }
-
-            if (m_shiftPressed) resist = false;
-
-            DragMode newDragMode = updateDragMode
-                (m_dragMode,
-                 m_clickPos,
-                 e->pos(),
-                 true,    // can move horiz
-                 true,    // can move vert
-                 resist,  // resist horiz
-                 resist); // resist vert
-
-            if (newDragMode != UnresolvedDrag) {
+            if (m_dragMode != UnresolvedDrag) {
 
                 m_editing = true;
 
@@ -1606,6 +1597,29 @@
                     }
                 }
             }
+
+        } else {
+
+            if (!editSelectionDrag(e)) {
+
+                Layer *layer = getSelectedLayer();
+
+                if (layer && layer->isLayerEditable()) {
+
+                    int x = e->x();
+                    int y = e->y();
+                    if (m_dragMode == VerticalDrag) x = m_clickPos.x();
+                    else if (m_dragMode == HorizontalDrag) y = m_clickPos.y();
+
+                    QMouseEvent moveEvent(QEvent::MouseMove,
+                                          QPoint(x, y),
+                                          Qt::NoButton,
+                                          e->buttons(),
+                                          e->modifiers());
+                                              
+                    layer->editDrag(this, &moveEvent);
+                }
+            }
         }
 
     } else if (mode == ViewManager::MeasureMode) {
@@ -1966,6 +1980,8 @@
             m_dragStartMinValue = dmin;
         }
     }
+
+    m_clickedInRange = false; // in case mouseReleaseEvent is not properly called
 }
 
 void
@@ -2502,7 +2518,7 @@
         
         //!!! could call through to layer
 	if (editable) {
-            help = tr("Click and drag an item in the active layer to move it");
+            help = tr("Click and drag an item in the active layer to move it; hold Shift to override initial resistance");
             if (pos) {
                 bool closeToLeft = false, closeToRight = false;
                 Selection selection = getSelectionAt(pos->x(), closeToLeft, closeToRight);