changeset 17:0183ebb725ca

* 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
author Chris Cannam
date Thu, 26 Jan 2006 16:15:40 +0000
parents 02a718909b2d
children 7c767d41bcee
files layer/Colour3DPlotLayer.h layer/LayerFactory.cpp layer/LayerFactory.h layer/SpectrogramLayer.h layer/TimeInstantLayer.cpp layer/TimeInstantLayer.h layer/TimeRulerLayer.h layer/TimeValueLayer.h layer/WaveformLayer.h widgets/Pane.cpp widgets/Pane.h widgets/PaneStack.cpp widgets/PaneStack.h
diffstat 13 files changed, 272 insertions(+), 65 deletions(-) [+]
line wrap: on
line diff
--- 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:
--- 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)
--- 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<LayerType> 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<LayerClass *>(layerBase);
 	if (!layer) return false;
 	ModelClass *model = dynamic_cast<ModelClass *>(modelBase);
+	if (!model) return false;
 	layer->setModel(model);
 	return true;
     }
--- 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;
 
--- 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 <QPainter>
+#include <QMouseEvent>
 
 #include <iostream>
 
 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
 {
--- 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;
 };
 
--- 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;
 
--- 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(); }
--- 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;
 
--- 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();
--- 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;
--- 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<Layer *>(pc);
+    if (layer) emit currentLayerChanged(m_currentPane, layer);
+    else emit currentLayerChanged(m_currentPane, 0);
 }
 
 void
--- 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 *);