# HG changeset patch # User Chris Cannam # Date 1138292140 0 # Node ID 0183ebb725ca22bb762fe208170c296900688bf8 # Parent 02a718909b2de31f27c66ca912a63669cdb41846 * Add ability to create empty layers for editing * Add first basic editing capability (adding points to a time instant layer) * Add various keyboard and mouse shortcuts for navigation &c * Add ability to resize a selection by dragging its edges * Add maximum zoom level diff -r 02a718909b2d -r 0183ebb725ca layer/Colour3DPlotLayer.h --- a/layer/Colour3DPlotLayer.h Thu Jan 26 11:56:09 2006 +0000 +++ b/layer/Colour3DPlotLayer.h Thu Jan 26 16:15:40 2006 +0000 @@ -56,8 +56,6 @@ virtual void setProperty(const PropertyName &, int value); */ - virtual QString getPropertyContainerIconName() const { return "colour3d"; } - void setProperties(const QXmlAttributes &attributes) { } protected slots: diff -r 02a718909b2d -r 0183ebb725ca layer/LayerFactory.cpp --- a/layer/LayerFactory.cpp Thu Jan 26 11:56:09 2006 +0000 +++ b/layer/LayerFactory.cpp Thu Jan 26 16:15:40 2006 +0000 @@ -90,6 +90,16 @@ return types; } +LayerFactory::LayerTypeSet +LayerFactory::getValidEmptyLayerTypes() +{ + LayerTypeSet types; + types.insert(TimeInstants); + types.insert(TimeValues); + //!!! and in principle Colour3DPlot -- now that's a challenge + return types; +} + LayerFactory::LayerType LayerFactory::getLayerType(const Layer *layer) { @@ -103,6 +113,20 @@ } QString +LayerFactory::getLayerIconName(LayerType type) +{ + switch (type) { + case Waveform: return "waveform"; + case Spectrogram: return "spectrogram"; + case TimeRuler: return "timeruler"; + case TimeInstants: return "instants"; + case TimeValues: return "values"; + case Colour3DPlot: return "colour3d"; + default: return "unknown"; + } +} + +QString LayerFactory::getLayerTypeName(LayerType type) { switch (type) { @@ -153,6 +177,19 @@ return; } +Model * +LayerFactory::createEmptyModel(LayerType layerType, Model *baseModel) +{ + if (layerType == TimeInstants) { + return new SparseOneDimensionalModel(baseModel->getSampleRate(), 1); + } else if (layerType == TimeValues) { + return new SparseTimeValueModel(baseModel->getSampleRate(), 1, + 0.0, 0.0, true); + } else { + return 0; + } +} + Layer * LayerFactory::createLayer(LayerType type, View *view, Model *model, int channel) diff -r 02a718909b2d -r 0183ebb725ca layer/LayerFactory.h --- a/layer/LayerFactory.h Thu Jan 26 11:56:09 2006 +0000 +++ b/layer/LayerFactory.h Thu Jan 26 16:15:40 2006 +0000 @@ -43,6 +43,7 @@ typedef std::set LayerTypeSet; LayerTypeSet getValidLayerTypes(Model *model); + LayerTypeSet getValidEmptyLayerTypes(); LayerType getLayerType(const Layer *); @@ -52,7 +53,9 @@ QString getLayerPresentationName(LayerType type); void setModel(Layer *layer, Model *model); + Model *createEmptyModel(LayerType type, Model *baseModel); + QString getLayerIconName(LayerType); QString getLayerTypeName(LayerType); LayerType getLayerTypeForName(QString); @@ -62,6 +65,7 @@ LayerClass *layer = dynamic_cast(layerBase); if (!layer) return false; ModelClass *model = dynamic_cast(modelBase); + if (!model) return false; layer->setModel(model); return true; } diff -r 02a718909b2d -r 0183ebb725ca layer/SpectrogramLayer.h --- a/layer/SpectrogramLayer.h Thu Jan 26 11:56:09 2006 +0000 +++ b/layer/SpectrogramLayer.h Thu Jan 26 16:15:40 2006 +0000 @@ -136,8 +136,6 @@ virtual int getCompletion() const; - virtual QString getPropertyContainerIconName() const { return "spectrogram"; } - virtual QString toXmlString(QString indent = "", QString extraAttributes = "") const; diff -r 02a718909b2d -r 0183ebb725ca layer/TimeInstantLayer.cpp --- a/layer/TimeInstantLayer.cpp Thu Jan 26 11:56:09 2006 +0000 +++ b/layer/TimeInstantLayer.cpp Thu Jan 26 16:15:40 2006 +0000 @@ -17,12 +17,14 @@ #include "model/SparseOneDimensionalModel.h" #include +#include #include TimeInstantLayer::TimeInstantLayer(View *w) : Layer(w), m_model(0), + m_editingPoint(0, tr("New Point")), m_colour(QColor(200, 50, 255)) { m_view->addLayer(this); @@ -295,13 +297,22 @@ i != points.end(); ++i) { const SparseOneDimensionalModel::Point &p(*i); + SparseOneDimensionalModel::PointList::const_iterator j = i; + ++j; int x = (p.frame - startFrame) / zoomLevel; float w = float(m_model->getResolution()) / zoomLevel; int iw = w; if (iw < 2) { - if (w < 0.5) iw = 1; - else iw = 2; + if (iw < 1) { + iw = 2; + if (j != points.end()) { + int nx = ((*j).frame - startFrame) / zoomLevel; + if (nx < x + 3) iw = 1; + } + } else { + iw = 2; + } } if (p.frame == illuminateFrame) { @@ -319,8 +330,7 @@ int lw = paint.fontMetrics().width(p.label); bool good = true; - SparseOneDimensionalModel::PointList::const_iterator j = i; - if (++j != points.end()) { + if (j != points.end()) { int nx = (j->frame - startFrame) / zoomLevel; if (nx >= x && nx - x - w - 3 <= lw) good = false; } @@ -334,6 +344,40 @@ } } +void +TimeInstantLayer::drawStart(QMouseEvent *e) +{ + std::cerr << "TimeInstantLayer::drawStart(" << e->x() << ")" << std::endl; + + if (!m_model) return; + + long frame = e->x() * m_view->getZoomLevel() + m_view->getStartFrame(); + if (frame < 0) frame = 0; + m_editingPoint = SparseOneDimensionalModel::Point(frame, tr("New Point")); + m_model->addPoint(m_editingPoint); +} + +void +TimeInstantLayer::drawDrag(QMouseEvent *e) +{ + std::cerr << "TimeInstantLayer::drawDrag(" << e->x() << ")" << std::endl; + + if (!m_model) return; + + long frame = e->x() * m_view->getZoomLevel() + m_view->getStartFrame(); + if (frame < 0) frame = 0; + m_model->deletePoint(m_editingPoint); + m_editingPoint.frame = frame; + m_model->addPoint(m_editingPoint); +} + +void +TimeInstantLayer::drawEnd(QMouseEvent *e) +{ + std::cerr << "TimeInstantLayer::drawEnd(" << e->x() << ")" << std::endl; + if (!m_model) return; +} + QString TimeInstantLayer::toXmlString(QString indent, QString extraAttributes) const { diff -r 02a718909b2d -r 0183ebb725ca layer/TimeInstantLayer.h --- a/layer/TimeInstantLayer.h Thu Jan 26 11:56:09 2006 +0000 +++ b/layer/TimeInstantLayer.h Thu Jan 26 16:15:40 2006 +0000 @@ -35,6 +35,10 @@ size_t &resolution, bool snapRight = true) const; + virtual void drawStart(QMouseEvent *); + virtual void drawDrag(QMouseEvent *); + virtual void drawEnd(QMouseEvent *); + virtual const Model *getModel() const { return m_model; } void setModel(SparseOneDimensionalModel *model); @@ -49,8 +53,6 @@ void setBaseColour(QColor); QColor getBaseColour() const { return m_colour; } - virtual QString getPropertyContainerIconName() const { return "instants"; } - virtual bool isLayerScrollable() const; virtual int getCompletion() const { return m_model->getCompletion(); } @@ -64,6 +66,7 @@ SparseOneDimensionalModel::PointList getLocalPoints(int) const; SparseOneDimensionalModel *m_model; + SparseOneDimensionalModel::Point m_editingPoint; QColor m_colour; }; diff -r 02a718909b2d -r 0183ebb725ca layer/TimeRulerLayer.h --- a/layer/TimeRulerLayer.h Thu Jan 26 11:56:09 2006 +0000 +++ b/layer/TimeRulerLayer.h Thu Jan 26 16:15:40 2006 +0000 @@ -46,8 +46,6 @@ int value) const; virtual void setProperty(const PropertyName &, int value); - virtual QString getPropertyContainerIconName() const { return "timeruler"; } - virtual QString toXmlString(QString indent = "", QString extraAttributes = "") const; diff -r 02a718909b2d -r 0183ebb725ca layer/TimeValueLayer.h --- a/layer/TimeValueLayer.h Thu Jan 26 11:56:09 2006 +0000 +++ b/layer/TimeValueLayer.h Thu Jan 26 16:15:40 2006 +0000 @@ -54,8 +54,6 @@ void setPlotStyle(PlotStyle style); PlotStyle getPlotStyle() const { return m_plotStyle; } - virtual QString getPropertyContainerIconName() const { return "values"; } - virtual bool isLayerScrollable() const; virtual int getCompletion() const { return m_model->getCompletion(); } diff -r 02a718909b2d -r 0183ebb725ca layer/WaveformLayer.h --- a/layer/WaveformLayer.h Thu Jan 26 11:56:09 2006 +0000 +++ b/layer/WaveformLayer.h Thu Jan 26 16:15:40 2006 +0000 @@ -155,8 +155,6 @@ virtual int getCompletion() const; - virtual QString getPropertyContainerIconName() const { return "waveform"; } - virtual QString toXmlString(QString indent = "", QString extraAttributes = "") const; diff -r 02a718909b2d -r 0183ebb725ca widgets/Pane.cpp --- a/widgets/Pane.cpp Thu Jan 26 11:56:09 2006 +0000 +++ b/widgets/Pane.cpp Thu Jan 26 16:15:40 2006 +0000 @@ -29,6 +29,8 @@ m_clickedInRange(false), m_shiftPressed(false), m_ctrlPressed(false), + m_navigating(false), + m_resizing(false), m_centreLineVisible(true) { setObjectName("Pane"); @@ -236,6 +238,39 @@ paint.end(); } +Selection +Pane::getSelectionAt(int x, bool &closeToLeftEdge, bool &closeToRightEdge) +{ + closeToLeftEdge = closeToRightEdge = false; + + if (!m_manager) return Selection(); + + long testFrame = (x - 5) * m_zoomLevel + getStartFrame(); + if (testFrame < 0) { + testFrame = x * m_zoomLevel + getStartFrame(); + if (testFrame < 0) return Selection(); + } + + Selection selection = m_manager->getContainingSelection(testFrame, true); + if (selection.isEmpty()) return selection; + + int lx = (int(selection.getStartFrame()) - getStartFrame()) / m_zoomLevel; + int rx = (int(selection.getEndFrame()) - getStartFrame()) / m_zoomLevel; + + int fuzz = 2; + if (x < lx - fuzz || x > rx + fuzz) return Selection(); + + int width = rx - lx; + fuzz = 3; + if (width < 12) fuzz = width / 4; + if (fuzz < 1) fuzz = 1; + + if (x < lx + fuzz) closeToLeftEdge = true; + if (x > rx - fuzz) closeToRightEdge = true; + + return selection; +} + void Pane::mousePressEvent(QMouseEvent *e) { @@ -247,30 +282,66 @@ ViewManager::ToolMode mode = ViewManager::NavigateMode; if (m_manager) mode = m_manager->getToolMode(); - if (mode == ViewManager::NavigateMode) { + m_navigating = false; + if (mode == ViewManager::NavigateMode || (e->buttons() & Qt::MidButton)) { + + if (mode != ViewManager::NavigateMode) { + setCursor(Qt::PointingHandCursor); + } + + m_navigating = true; m_dragCentreFrame = m_centreFrame; } else if (mode == ViewManager::SelectMode) { - int mouseFrame = e->x() * m_zoomLevel + getStartFrame(); - size_t resolution = 1; - int snapFrame = mouseFrame; + bool closeToLeft = false, closeToRight = false; + Selection selection = getSelectionAt(e->x(), closeToLeft, closeToRight); + + if ((closeToLeft || closeToRight) && !(closeToLeft && closeToRight)) { + + m_manager->removeSelection(selection); + + if (closeToLeft) { + m_selectionStartFrame = selection.getEndFrame(); + } else { + m_selectionStartFrame = selection.getStartFrame(); + } + + m_manager->setInProgressSelection(selection, false); + m_resizing = true; + } else { + + int mouseFrame = e->x() * m_zoomLevel + getStartFrame(); + size_t resolution = 1; + int snapFrame = mouseFrame; + + Layer *layer = getSelectedLayer(); + if (layer) { + snapFrame = layer->getNearestFeatureFrame(mouseFrame, resolution, + false); + } + + if (snapFrame < 0) snapFrame = 0; + m_selectionStartFrame = snapFrame; + if (m_manager) { + m_manager->setInProgressSelection(Selection(snapFrame, + snapFrame + resolution), + !m_ctrlPressed); + } + + m_resizing = false; + } + + update(); + + } else if (mode == ViewManager::DrawMode) { + Layer *layer = getSelectedLayer(); if (layer) { - snapFrame = layer->getNearestFeatureFrame(mouseFrame, resolution, - false); + layer->drawStart(e); } - - if (snapFrame < 0) snapFrame = 0; - m_selectionStartFrame = snapFrame; - if (m_manager) { - m_manager->setInProgressSelection(Selection(snapFrame, - snapFrame + resolution), - !m_ctrlPressed); - } - update(); } emit paneInteractedWith(); @@ -286,7 +357,14 @@ mouseMoveEvent(e); } - if (mode == ViewManager::NavigateMode) { + if (m_navigating || mode == ViewManager::NavigateMode) { + + m_navigating = false; + + if (mode != ViewManager::NavigateMode) { + // restore cursor + toolModeChanged(); + } if (m_shiftPressed) { @@ -338,7 +416,15 @@ } update(); - } + + } else if (mode == ViewManager::DrawMode) { + + Layer *layer = getSelectedLayer(); + if (layer) { + layer->drawEnd(e); + update(); + } + } m_clickedInRange = false; @@ -353,26 +439,34 @@ if (!m_clickedInRange) { -// std::cerr << "Pane: calling identifyLocalFeatures" << std::endl; + if (mode == ViewManager::SelectMode) { + bool closeToLeft = false, closeToRight = false; + getSelectionAt(e->x(), closeToLeft, closeToRight); + if ((closeToLeft || closeToRight) && !(closeToLeft && closeToRight)) { + setCursor(Qt::SizeHorCursor); + } else { + setCursor(Qt::ArrowCursor); + } + } -//!!! identifyLocalFeatures(true, e->x(), e->y()); + if (mode != ViewManager::DrawMode) { - bool previouslyIdentifying = m_identifyFeatures; - QPoint prevPoint = m_identifyPoint; + bool previouslyIdentifying = m_identifyFeatures; + QPoint prevPoint = m_identifyPoint; - m_identifyFeatures = true; - m_identifyPoint = e->pos(); - - if (m_identifyFeatures != previouslyIdentifying || - m_identifyPoint != prevPoint) { - update(); + m_identifyFeatures = true; + m_identifyPoint = e->pos(); + + if (m_identifyFeatures != previouslyIdentifying || + m_identifyPoint != prevPoint) { + update(); + } } return; - } - if (mode == ViewManager::NavigateMode) { + if (m_navigating || mode == ViewManager::NavigateMode) { if (m_shiftPressed) { @@ -438,7 +532,7 @@ if (m_manager) { m_manager->setInProgressSelection(Selection(min, max), - !m_ctrlPressed); + !m_resizing && !m_ctrlPressed); } bool doScroll = false; @@ -463,6 +557,13 @@ } update(); + + } else if (mode == ViewManager::DrawMode) { + + Layer *layer = getSelectedLayer(); + if (layer) { + layer->drawDrag(e); + } } } @@ -485,8 +586,6 @@ { //std::cerr << "wheelEvent, delta " << e->delta() << std::endl; - int newZoomLevel = m_zoomLevel; - int count = e->delta(); if (count > 0) { @@ -498,25 +597,45 @@ if (count <= -120) count /= 120; else count = -1; } + + if (e->modifiers() & Qt::ControlModifier) { + + if (getStartFrame() < 0 && + getEndFrame() >= getModelsEndFrame()) return; + + long delta = ((width() / 2) * count * m_zoomLevel); + + if (int(m_centreFrame) < delta) { + setCentreFrame(0); + } else if (int(m_centreFrame) - delta >= int(getModelsEndFrame())) { + setCentreFrame(getModelsEndFrame()); + } else { + setCentreFrame(m_centreFrame - delta); + } + + } else { + + int newZoomLevel = m_zoomLevel; - while (count > 0) { - if (newZoomLevel <= 2) { - newZoomLevel = 1; - break; + while (count > 0) { + if (newZoomLevel <= 2) { + newZoomLevel = 1; + break; + } + newZoomLevel = getZoomConstraintBlockSize(newZoomLevel - 1, + ZoomConstraint::RoundDown); + --count; } - newZoomLevel = getZoomConstraintBlockSize(newZoomLevel - 1, - ZoomConstraint::RoundDown); - --count; - } - - while (count < 0) { - newZoomLevel = getZoomConstraintBlockSize(newZoomLevel + 1, - ZoomConstraint::RoundUp); - ++count; - } - - if (newZoomLevel != m_zoomLevel) { - setZoomLevel(newZoomLevel); + + while (count < 0) { + newZoomLevel = getZoomConstraintBlockSize(newZoomLevel + 1, + ZoomConstraint::RoundUp); + ++count; + } + + if (newZoomLevel != m_zoomLevel) { + setZoomLevel(newZoomLevel); + } } emit paneInteractedWith(); diff -r 02a718909b2d -r 0183ebb725ca widgets/Pane.h --- a/widgets/Pane.h Thu Jan 26 11:56:09 2006 +0000 +++ b/widgets/Pane.h Thu Jan 26 16:15:40 2006 +0000 @@ -53,6 +53,8 @@ virtual void leaveEvent(QEvent *e); virtual void wheelEvent(QWheelEvent *e); + Selection getSelectionAt(int x, bool &closeToLeft, bool &closeToRight); + bool m_identifyFeatures; QPoint m_identifyPoint; QPoint m_clickPos; @@ -60,6 +62,8 @@ bool m_clickedInRange; bool m_shiftPressed; bool m_ctrlPressed; + bool m_navigating; + bool m_resizing; size_t m_dragCentreFrame; bool m_centreLineVisible; size_t m_selectionStartFrame; diff -r 02a718909b2d -r 0183ebb725ca widgets/PaneStack.cpp --- a/widgets/PaneStack.cpp Thu Jan 26 11:56:09 2006 +0000 +++ b/widgets/PaneStack.cpp Thu Jan 26 16:15:40 2006 +0000 @@ -190,6 +190,10 @@ ++i; ++j; } + + Layer *layer = dynamic_cast(pc); + if (layer) emit currentLayerChanged(m_currentPane, layer); + else emit currentLayerChanged(m_currentPane, 0); } void diff -r 02a718909b2d -r 0183ebb725ca widgets/PaneStack.h --- a/widgets/PaneStack.h Thu Jan 26 11:56:09 2006 +0000 +++ b/widgets/PaneStack.h Thu Jan 26 16:15:40 2006 +0000 @@ -16,6 +16,7 @@ class QWidget; class QLabel; class Pane; +class Layer; class ViewManager; class PropertyContainer; class PropertyStack; @@ -37,6 +38,7 @@ signals: void currentPaneChanged(Pane *pane); + void currentLayerChanged(Pane *pane, Layer *layer); public slots: void propertyContainerAdded(PropertyContainer *);