# HG changeset patch # User Chris Cannam # Date 1254491770 0 # Node ID c2ba2796cbeea309cf7657a45c29d2d79ed4fb4d # Parent d666f5f8b154a09a42334fdb88afcfa4c70763fd * Big improvements to editing behaviour in note and region models diff -r d666f5f8b154 -r c2ba2796cbee layer/NoteLayer.cpp --- 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 #include +#include 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; diff -r d666f5f8b154 -r c2ba2796cbee layer/NoteLayer.h --- 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; diff -r d666f5f8b154 -r c2ba2796cbee layer/RegionLayer.cpp --- 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 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::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) { diff -r d666f5f8b154 -r c2ba2796cbee layer/RegionLayer.h --- 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 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; diff -r d666f5f8b154 -r c2ba2796cbee view/Pane.cpp --- 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);