changeset 101:0f36cdf407a6 sv1-v0.9rc1

* Make vertical scale alignment modes work in note layer as well as time-value layer, and several significant fixes to it * Make it possible to draw notes properly on the note layer * Show units (and frequencies etc in note layer's case) in the time-value and note layer description boxes * Minor fix to item edit dialog layout * Some minor menu rearrangement * Comment out a lot of debug output * Add SV website and reference URLs to Help menu, and add code to (attempt to) open them in the user's preferred browser
author Chris Cannam
date Fri, 12 May 2006 14:40:43 +0000
parents 0db5e7492ce8
children 8ce53683d0d7
files layer/Colour3DPlotLayer.h layer/LayerFactory.cpp layer/NoteLayer.cpp layer/NoteLayer.h layer/SpectrogramLayer.cpp layer/SpectrogramLayer.h layer/TextLayer.cpp layer/TextLayer.h layer/TimeInstantLayer.h layer/TimeRulerLayer.h layer/TimeValueLayer.cpp layer/TimeValueLayer.h layer/WaveformLayer.cpp layer/WaveformLayer.h widgets/ItemEditDialog.cpp widgets/Pane.cpp
diffstat 16 files changed, 339 insertions(+), 172 deletions(-) [+]
line wrap: on
line diff
--- a/layer/Colour3DPlotLayer.h	Thu May 11 15:02:14 2006 +0000
+++ b/layer/Colour3DPlotLayer.h	Fri May 12 14:40:43 2006 +0000
@@ -64,7 +64,7 @@
 
     virtual int getCompletion() const { return m_model->getCompletion(); }
 
-    virtual bool getValueExtents(float &, float &, QString &) const { return false; }
+    virtual bool getValueExtents(float &, float &, bool &, QString &) const { return false; }
 
     virtual QString getPropertyLabel(const PropertyName &) const { return ""; }
 /*
--- a/layer/LayerFactory.cpp	Thu May 11 15:02:14 2006 +0000
+++ b/layer/LayerFactory.cpp	Fri May 12 14:40:43 2006 +0000
@@ -315,8 +315,8 @@
 	std::cerr << "LayerFactory::createLayer: Unknown layer type " 
 		  << type << std::endl;
     } else {
-	std::cerr << "LayerFactory::createLayer: Setting object name "
-		  << getLayerPresentationName(type).toStdString() << " on " << layer << std::endl;
+//	std::cerr << "LayerFactory::createLayer: Setting object name "
+//		  << getLayerPresentationName(type).toStdString() << " on " << layer << std::endl;
 	layer->setObjectName(getLayerPresentationName(type));
     }
 
--- a/layer/NoteLayer.cpp	Thu May 11 15:02:14 2006 +0000
+++ b/layer/NoteLayer.cpp	Fri May 12 14:40:43 2006 +0000
@@ -42,7 +42,7 @@
     m_editingPoint(0, 0.0, 0, tr("New Point")),
     m_editingCommand(0),
     m_colour(Qt::black),
-    m_verticalScale(MinMaxRangeScale)
+    m_verticalScale(AutoAlignScale)
 {
     
 }
@@ -60,7 +60,7 @@
     connect(m_model, SIGNAL(completionChanged()),
 	    this, SIGNAL(modelCompletionChanged()));
 
-    std::cerr << "NoteLayer::setModel(" << model << ")" << std::endl;
+//    std::cerr << "NoteLayer::setModel(" << model << ")" << std::endl;
 
     emit modelReplaced();
 }
@@ -114,7 +114,7 @@
     } else if (name == "Vertical Scale") {
 	
 	if (min) *min = 0;
-	if (max) *max = 2;
+	if (max) *max = 3;
 	
 	deft = int(m_verticalScale);
 
@@ -150,9 +150,10 @@
     } else if (name == "Vertical Scale") {
 	switch (value) {
 	default:
-	case 0: return tr("Note Range In Use");
-	case 1: return tr("MIDI Note Range");
-	case 2: return tr("Frequency");
+	case 0: return tr("Auto-Align");
+	case 1: return tr("Linear Scale");
+	case 2: return tr("Log Scale");
+	case 3: return tr("MIDI Note Range");
 	}
     }
     return tr("<unknown>");
@@ -206,12 +207,52 @@
 }
 
 bool
-NoteLayer::getValueExtents(float &min, float &max, QString &unit) const
+NoteLayer::shouldConvertMIDIToHz() const
+{
+    QString unit = m_model->getScaleUnits();
+    return (unit != "Hz");
+//    if (unit == "" ||
+//        unit.startsWith("MIDI") ||
+//        unit.startsWith("midi")) return true;
+//    return false;
+}
+
+bool
+NoteLayer::getValueExtents(float &min, float &max,
+                           bool &logarithmic, QString &unit) const
 {
     if (!m_model) return false;
     min = m_model->getValueMinimum();
     max = m_model->getValueMaximum();
-    unit = m_model->getScaleUnits();
+
+    if (shouldConvertMIDIToHz()) unit = "Hz";
+    else unit = m_model->getScaleUnits();
+
+    if (m_verticalScale == MIDIRangeScale ||
+        m_verticalScale == LogScale) logarithmic = true;
+
+    return true;
+}
+
+bool
+NoteLayer::getDisplayExtents(float &min, float &max) const
+{
+    if (!m_model || m_verticalScale == AutoAlignScale) return false;
+
+    if (m_verticalScale == MIDIRangeScale) {
+        min = Pitch::getFrequencyForPitch(0);
+        max = Pitch::getFrequencyForPitch(127);
+        return true;
+    }
+
+    min = m_model->getValueMinimum();
+    max = m_model->getValueMaximum();
+
+    if (shouldConvertMIDIToHz()) {
+        min = Pitch::getFrequencyForPitch(lrintf(min));
+        max = Pitch::getFrequencyForPitch(lrintf(max + 1));
+    }
+
     return true;
 }
 
@@ -301,17 +342,38 @@
     RealTime rd = RealTime::frame2RealTime(note.duration,
 					   m_model->getSampleRate());
     
+    QString pitchText;
+
+    if (shouldConvertMIDIToHz()) {
+
+        int mnote = lrintf(note.value);
+        int cents = lrintf((note.value - mnote) * 100);
+        float freq = Pitch::getFrequencyForPitch(mnote, cents);
+        pitchText = QString("%1 (%2 Hz)")
+            .arg(Pitch::getPitchLabel(mnote, cents)).arg(freq);
+
+    } else if (m_model->getScaleUnits() == "Hz") {
+
+        pitchText = QString("%1 Hz (%2)")
+            .arg(note.value)
+            .arg(Pitch::getPitchLabelForFrequency(note.value));
+
+    } else {
+        pitchText = QString("%1 %2")
+            .arg(note.value).arg(m_model->getScaleUnits());
+    }
+
     QString text;
 
     if (note.label == "") {
 	text = QString(tr("Time:\t%1\nPitch:\t%2\nDuration:\t%3\nNo label"))
 	    .arg(rt.toText(true).c_str())
-	    .arg(note.value)
+	    .arg(pitchText)
 	    .arg(rd.toText(true).c_str());
     } else {
 	text = QString(tr("Time:\t%1\nPitch:\t%2\nDuration:\t%3\nLabel:\t%4"))
 	    .arg(rt.toText(true).c_str())
-	    .arg(note.value)
+	    .arg(pitchText)
 	    .arg(rd.toText(true).c_str())
 	    .arg(note.label);
     }
@@ -393,66 +455,107 @@
     return found;
 }
 
-int
-NoteLayer::getYForValue(View *v, float value) const
+void
+NoteLayer::getScaleExtents(View *v, float &min, float &max, bool &log) const
 {
-    float min, max, h = v->height();
+    min = 0.0;
+    max = 0.0;
+    log = false;
 
-    switch (m_verticalScale) {
+    QString queryUnits;
+    if (shouldConvertMIDIToHz()) queryUnits = "Hz";
+    else queryUnits = m_model->getScaleUnits();
 
-    case MIDIRangeScale:
-	min = 0.0;
-	max = 127.0;
-	break;
+    if (m_verticalScale == AutoAlignScale) {
 
-    case MinMaxRangeScale:
-	min = m_model->getValueMinimum();
-	max = m_model->getValueMaximum();
-	break;
+        if (!v->getValueExtents(queryUnits, min, max, log)) {
 
-    case FrequencyScale:
-    
-	value = Pitch::getFrequencyForPitch(lrintf(value),
-					    value - lrintf(value));
+            min = m_model->getValueMinimum();
+            max = m_model->getValueMaximum();
 
-	// If we have a spectrogram layer on the same view as us, align
-	// ourselves with it...
-	for (int i = 0; i < v->getLayerCount(); ++i) {
-	    SpectrogramLayer *spectrogram = dynamic_cast<SpectrogramLayer *>
-		(v->getLayer(i));
-	    if (spectrogram) {
-		return spectrogram->getYForFrequency(v, value);
-	    }
-	}
+            if (shouldConvertMIDIToHz()) {
+                min = Pitch::getFrequencyForPitch(lrintf(min));
+                max = Pitch::getFrequencyForPitch(lrintf(max + 1));
+            }
 
-	// ...otherwise just interpolate
-	std::cerr << "FrequencyScale: value in = " << value << std::endl;
-	min = m_model->getValueMinimum();
-	min = Pitch::getFrequencyForPitch(lrintf(min), min - lrintf(min));
-	max = m_model->getValueMaximum();
-	max = Pitch::getFrequencyForPitch(lrintf(max), max - lrintf(max));
-	std::cerr << "FrequencyScale: min = " << min << ", max = " << max << ", value = " << value << std::endl;
-	break;
+        } else if (log) {
+
+//            std::cerr << "NoteLayer[" << this << "]::getScaleExtents: min = " << min << ", max = " << max << ", log = " << log << std::endl;
+
+            min = (min < 0.0) ? -log10(-min) : (min == 0.0) ? 0.0 : log10(min);
+            max = (max < 0.0) ? -log10(-max) : (max == 0.0) ? 0.0 : log10(max);
+        }
+
+    } else {
+
+        min = m_model->getValueMinimum();
+        max = m_model->getValueMaximum();
+
+        if (m_verticalScale == MIDIRangeScale) {
+            min = Pitch::getFrequencyForPitch(0);
+            max = Pitch::getFrequencyForPitch(127);
+        } else if (shouldConvertMIDIToHz()) {
+            min = Pitch::getFrequencyForPitch(lrintf(min));
+            max = Pitch::getFrequencyForPitch(lrintf(max + 1));
+        }
+
+        if (m_verticalScale == LogScale || m_verticalScale == MIDIRangeScale) {
+            min = (min < 0.0) ? -log10(-min) : (min == 0.0) ? 0.0 : log10(min);
+            max = (max < 0.0) ? -log10(-max) : (max == 0.0) ? 0.0 : log10(max);
+            log = true;
+        }
     }
 
-    if (max < min) max = min;
-    max = max + 1.0;
+    if (max == min) max = min + 1.0;
+}
 
-    return int(h - ((value - min) * h) / (max - min)) - 1;
+int
+NoteLayer::getYForValue(View *v, float val) const
+{
+    float min = 0.0, max = 0.0;
+    bool logarithmic = false;
+    int h = v->height();
+
+    getScaleExtents(v, min, max, logarithmic);
+
+//    std::cerr << "NoteLayer[" << this << "]::getYForValue(" << val << "): min = " << min << ", max = " << max << ", log = " << logarithmic << std::endl;
+
+    if (shouldConvertMIDIToHz()) {
+        val = Pitch::getFrequencyForPitch(lrintf(val),
+                                          lrintf((val - lrintf(val)) * 100));
+//        std::cerr << "shouldConvertMIDIToHz true, val now = " << val << std::endl;
+    }
+
+    if (logarithmic) {
+        val = (val < 0.0) ? -log10(-val) : (val == 0.0) ? 0.0 : log10(val);
+//        std::cerr << "logarithmic true, val now = " << val << std::endl;
+    }
+
+    int y = int(h - ((val - min) * h) / (max - min)) - 1;
+//    std::cerr << "y = " << y << std::endl;
+    return y;
 }
 
 float
 NoteLayer::getValueForY(View *v, int y) const
 {
-    //!!!
-
-    float min = m_model->getValueMinimum();
-    float max = m_model->getValueMaximum();
-    if (max == min) max = min + 1.0;
-
+    float min = 0.0, max = 0.0;
+    bool logarithmic = false;
     int h = v->height();
 
-    return min + (float(h - y) * float(max - min)) / h;
+    getScaleExtents(v, min, max, logarithmic);
+
+    float val = min + (float(h - y) * float(max - min)) / h;
+
+    if (logarithmic) {
+        val = pow(10, val);
+    }
+
+    if (shouldConvertMIDIToHz()) {
+        val = Pitch::getPitchForFrequency(val);
+    }
+
+    return val;
 }
 
 void
@@ -538,7 +641,7 @@
 void
 NoteLayer::drawStart(View *v, QMouseEvent *e)
 {
-    std::cerr << "NoteLayer::drawStart(" << e->x() << "," << e->y() << ")" << std::endl;
+//    std::cerr << "NoteLayer::drawStart(" << e->x() << "," << e->y() << ")" << std::endl;
 
     if (!m_model) return;
 
@@ -562,7 +665,7 @@
 void
 NoteLayer::drawDrag(View *v, QMouseEvent *e)
 {
-    std::cerr << "NoteLayer::drawDrag(" << e->x() << "," << e->y() << ")" << std::endl;
+//    std::cerr << "NoteLayer::drawDrag(" << e->x() << "," << e->y() << ")" << std::endl;
 
     if (!m_model || !m_editing) return;
 
@@ -570,18 +673,28 @@
     if (frame < 0) frame = 0;
     frame = frame / m_model->getResolution() * m_model->getResolution();
 
-    float value = getValueForY(v, e->y());
+    float newValue = getValueForY(v, e->y());
+
+    long newFrame = m_editingPoint.frame;
+    long newDuration = frame - newFrame;
+    if (newDuration < 0) {
+        newFrame = frame;
+        newDuration = -newDuration;
+    } else if (newDuration == 0) {
+        newDuration = 1;
+    }
 
     m_editingCommand->deletePoint(m_editingPoint);
-    m_editingPoint.frame = frame;
-    m_editingPoint.value = value;
+    m_editingPoint.frame = newFrame;
+    m_editingPoint.value = newValue;
+    m_editingPoint.duration = newDuration;
     m_editingCommand->addPoint(m_editingPoint);
 }
 
 void
 NoteLayer::drawEnd(View *v, QMouseEvent *e)
 {
-    std::cerr << "NoteLayer::drawEnd(" << e->x() << "," << e->y() << ")" << std::endl;
+//    std::cerr << "NoteLayer::drawEnd(" << e->x() << "," << e->y() << ")" << std::endl;
     if (!m_model || !m_editing) return;
     m_editingCommand->finish();
     m_editingCommand = 0;
@@ -591,7 +704,7 @@
 void
 NoteLayer::editStart(View *v, QMouseEvent *e)
 {
-    std::cerr << "NoteLayer::editStart(" << e->x() << "," << e->y() << ")" << std::endl;
+//    std::cerr << "NoteLayer::editStart(" << e->x() << "," << e->y() << ")" << std::endl;
 
     if (!m_model) return;
 
@@ -612,7 +725,7 @@
 void
 NoteLayer::editDrag(View *v, QMouseEvent *e)
 {
-    std::cerr << "NoteLayer::editDrag(" << e->x() << "," << e->y() << ")" << std::endl;
+//    std::cerr << "NoteLayer::editDrag(" << e->x() << "," << e->y() << ")" << std::endl;
 
     if (!m_model || !m_editing) return;
 
@@ -636,7 +749,7 @@
 void
 NoteLayer::editEnd(View *v, QMouseEvent *e)
 {
-    std::cerr << "NoteLayer::editEnd(" << e->x() << "," << e->y() << ")" << std::endl;
+//    std::cerr << "NoteLayer::editEnd(" << e->x() << "," << e->y() << ")" << std::endl;
     if (!m_model || !m_editing) return;
 
     if (m_editingCommand) {
--- a/layer/NoteLayer.h	Thu May 11 15:02:14 2006 +0000
+++ b/layer/NoteLayer.h	Fri May 12 14:40:43 2006 +0000
@@ -73,9 +73,10 @@
     QColor getBaseColour() const { return m_colour; }
 
     enum VerticalScale {
-	MinMaxRangeScale,
-	MIDIRangeScale,
-	FrequencyScale
+        AutoAlignScale,
+        LinearScale,
+        LogScale,
+        MIDIRangeScale
     };
 
     void setVerticalScale(VerticalScale scale);
@@ -87,7 +88,10 @@
 
     virtual int getCompletion() const { return m_model->getCompletion(); }
 
-    virtual bool getValueExtents(float &min, float &max, QString &unit) const;
+    virtual bool getValueExtents(float &min, float &max,
+                                 bool &log, QString &unit) const;
+
+    virtual bool getDisplayExtents(float &min, float &max) const;
 
     virtual QString toXmlString(QString indent = "",
 				QString extraAttributes = "") const;
@@ -95,8 +99,10 @@
     void setProperties(const QXmlAttributes &attributes);
 
 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;
+    bool shouldConvertMIDIToHz() const;
 
     NoteModel::PointList getLocalPoints(View *v, int) const;
 
--- a/layer/SpectrogramLayer.cpp	Thu May 11 15:02:14 2006 +0000
+++ b/layer/SpectrogramLayer.cpp	Fri May 12 14:40:43 2006 +0000
@@ -34,7 +34,7 @@
 #include <cassert>
 #include <cmath>
 
-#define DEBUG_SPECTROGRAM_REPAINT 1
+//#define DEBUG_SPECTROGRAM_REPAINT 1
 
 static double mod(double x, double y)
 {
@@ -111,7 +111,7 @@
 void
 SpectrogramLayer::setModel(const DenseTimeValueModel *model)
 {
-    std::cerr << "SpectrogramLayer(" << this << "): setModel(" << model << ")" << std::endl;
+//    std::cerr << "SpectrogramLayer(" << this << "): setModel(" << model << ")" << std::endl;
 
     m_mutex.lock();
     m_cacheInvalid = true;
@@ -637,8 +637,8 @@
 void
 SpectrogramLayer::setGain(float gain)
 {
-    std::cerr << "SpectrogramLayer::setGain(" << gain << ") (my gain is now "
-	      << m_gain << ")" << std::endl;
+//    std::cerr << "SpectrogramLayer::setGain(" << gain << ") (my gain is now "
+//	      << m_gain << ")" << std::endl;
 
     if (m_gain == gain) return;
 
@@ -1292,18 +1292,18 @@
 	    m_fillExtent = 0;
 	    m_fillCompletion = 0;
 
-	    std::cerr << "SpectrogramLayer::CacheFillThread::run: model is ready" << std::endl;
+	//    std::cerr << "SpectrogramLayer::CacheFillThread::run: model is ready" << std::endl;
 
 	    size_t start = m_layer.m_model->getStartFrame();
 	    size_t end = m_layer.m_model->getEndFrame();
 
-	    std::cerr << "start = " << start << ", end = " << end << std::endl;
+	//    std::cerr << "start = " << start << ", end = " << end << std::endl;
 
 	    WindowType windowType = m_layer.m_windowType;
 	    size_t windowSize = m_layer.m_windowSize;
 	    size_t windowIncrement = m_layer.getWindowIncrement();
 
-            std::cerr << "\nWINDOW INCREMENT: " << windowIncrement << " (for hop level " << m_layer.m_windowHopLevel << ")\n" << std::endl;
+        //    std::cerr << "\nWINDOW INCREMENT: " << windowIncrement << " (for hop level " << m_layer.m_windowHopLevel << ")\n" << std::endl;
 
 	    size_t visibleStart = m_layer.m_candidateFillStartFrame;
 	    visibleStart = (visibleStart / windowIncrement) * windowIncrement;
@@ -2174,15 +2174,25 @@
 }
 
 bool
-SpectrogramLayer::getValueExtents(float &min, float &max, QString &unit) const
+SpectrogramLayer::getValueExtents(float &min, float &max,
+                                  bool &logarithmic, QString &unit) const
 {
     min = getEffectiveMinFrequency();
     max = getEffectiveMaxFrequency();
+    logarithmic = (m_frequencyScale == LogFrequencyScale);
     unit = "Hz";
     return true;
 }
 
 bool
+SpectrogramLayer::getDisplayExtents(float &min, float &max) const
+{
+    min = getEffectiveMinFrequency();
+    max = getEffectiveMaxFrequency();
+    return true;
+}    
+
+bool
 SpectrogramLayer::snapToFeatureFrame(View *v, int &frame,
 				     size_t &resolution,
 				     SnapType snap) const
--- a/layer/SpectrogramLayer.h	Thu May 11 15:02:14 2006 +0000
+++ b/layer/SpectrogramLayer.h	Fri May 12 14:40:43 2006 +0000
@@ -186,7 +186,10 @@
 
     virtual int getCompletion() const;
 
-    virtual bool getValueExtents(float &min, float &max, QString &unit) const;
+    virtual bool getValueExtents(float &min, float &max,
+                                 bool &logarithmic, QString &unit) const;
+
+    virtual bool getDisplayExtents(float &min, float &max) const;
 
     virtual QString toXmlString(QString indent = "",
 				QString extraAttributes = "") const;
--- a/layer/TextLayer.cpp	Thu May 11 15:02:14 2006 +0000
+++ b/layer/TextLayer.cpp	Fri May 12 14:40:43 2006 +0000
@@ -143,7 +143,8 @@
 }
 
 bool
-TextLayer::getValueExtents(float &min, float &max, QString &unit) const
+TextLayer::getValueExtents(float &min, float &max,
+                           bool &logarithmic, QString &unit) const
 {
     return false;
 }
--- a/layer/TextLayer.h	Thu May 11 15:02:14 2006 +0000
+++ b/layer/TextLayer.h	Fri May 12 14:40:43 2006 +0000
@@ -78,7 +78,8 @@
 
     virtual int getCompletion() const { return m_model->getCompletion(); }
 
-    virtual bool getValueExtents(float &min, float &max, QString &unit) const;
+    virtual bool getValueExtents(float &min, float &max,
+                                 bool &logarithmic, QString &unit) const;
 
     virtual QString toXmlString(QString indent = "",
 				QString extraAttributes = "") const;
--- a/layer/TimeInstantLayer.h	Thu May 11 15:02:14 2006 +0000
+++ b/layer/TimeInstantLayer.h	Fri May 12 14:40:43 2006 +0000
@@ -88,7 +88,8 @@
 
     virtual bool needsTextLabelHeight() const { return m_model->hasTextLabels(); }
 
-    virtual bool getValueExtents(float &min, float &max, QString &unit) const {
+    virtual bool getValueExtents(float &min, float &max,
+                                 bool &log, QString &unit) const {
         return false;
     }
 
--- a/layer/TimeRulerLayer.h	Thu May 11 15:02:14 2006 +0000
+++ b/layer/TimeRulerLayer.h	Fri May 12 14:40:43 2006 +0000
@@ -53,7 +53,8 @@
 					  int value) const;
     virtual void setProperty(const PropertyName &, int value);
 
-    virtual bool getValueExtents(float &min, float &max, QString &unit) const {
+    virtual bool getValueExtents(float &min, float &max,
+                                 bool &log, QString &unit) const {
         return false;
     }
 
--- a/layer/TimeValueLayer.cpp	Thu May 11 15:02:14 2006 +0000
+++ b/layer/TimeValueLayer.cpp	Fri May 12 14:40:43 2006 +0000
@@ -42,7 +42,7 @@
     m_editingCommand(0),
     m_colour(Qt::darkGreen),
     m_plotStyle(PlotConnectedPoints),
-    m_verticalScale(LinearScale)
+    m_verticalScale(AutoAlignScale)
 {
     
 }
@@ -60,7 +60,7 @@
     connect(m_model, SIGNAL(completionChanged()),
 	    this, SIGNAL(modelCompletionChanged()));
 
-    std::cerr << "TimeValueLayer::setModel(" << model << ")" << std::endl;
+//    std::cerr << "TimeValueLayer::setModel(" << model << ")" << std::endl;
 
     emit modelReplaced();
 }
@@ -169,10 +169,10 @@
     } else if (name == "Vertical Scale") {
 	switch (value) {
 	default:
-	case 0: return tr("Linear Scale");
-	case 1: return tr("Log Scale");
-	case 2: return tr("+/-1 Scale");
-	case 3: return tr("Frequency Scale");
+	case 0: return tr("Auto-Align");
+	case 1: return tr("Linear Scale");
+	case 2: return tr("Log Scale");
+	case 3: return tr("+/-1 Scale");
 	}
     }
     return tr("<unknown>");
@@ -242,14 +242,27 @@
 }
 
 bool
-TimeValueLayer::getValueExtents(float &min, float &max, QString &unit) const
+TimeValueLayer::getValueExtents(float &min, float &max,
+                                bool &logarithmic, QString &unit) const
 {
+    if (!m_model) return false;
     min = m_model->getValueMinimum();
     max = m_model->getValueMaximum();
+    logarithmic = (m_verticalScale == LogScale);
     unit = m_model->getScaleUnits();
     return true;
 }
 
+bool
+TimeValueLayer::getDisplayExtents(float &min, float &max) const
+{
+    if (!m_model || m_verticalScale == AutoAlignScale) return false;
+
+    min = m_model->getValueMinimum();
+    max = m_model->getValueMaximum();
+    return true;
+}
+
 SparseTimeValueModel::PointList
 TimeValueLayer::getLocalPoints(View *v, int x) const
 {
@@ -315,15 +328,19 @@
     RealTime rt = RealTime::frame2RealTime(useFrame, m_model->getSampleRate());
     
     QString text;
+    QString unit = m_model->getScaleUnits();
+    if (unit != "") unit = " " + unit;
 
     if (points.begin()->label == "") {
-	text = QString(tr("Time:\t%1\nValue:\t%2\nNo label"))
-	    .arg(rt.toText(true).c_str())
-	    .arg(points.begin()->value);
-    } else {
-	text = QString(tr("Time:\t%1\nValue:\t%2\nLabel:\t%3"))
+	text = QString(tr("Time:\t%1\nValue:\t%2%3\nNo label"))
 	    .arg(rt.toText(true).c_str())
 	    .arg(points.begin()->value)
+            .arg(unit);
+    } else {
+	text = QString(tr("Time:\t%1\nValue:\t%2%3\nLabel:\t%4"))
+	    .arg(rt.toText(true).c_str())
+	    .arg(points.begin()->value)
+            .arg(unit)
 	    .arg(points.begin()->label);
     }
 
@@ -404,81 +421,89 @@
     return found;
 }
 
+void
+TimeValueLayer::getScaleExtents(View *v, float &min, float &max, bool &log) const
+{
+    min = 0.0;
+    max = 0.0;
+    log = false;
+
+    if (m_verticalScale == AutoAlignScale) {
+
+        if (!v->getValueExtents(m_model->getScaleUnits(), min, max, log)) {
+            min = m_model->getValueMinimum();
+            max = m_model->getValueMaximum();
+        } else if (log) {
+            min = (min < 0.0) ? -log10(-min) : (min == 0.0) ? 0.0 : log10(min);
+            max = (max < 0.0) ? -log10(-max) : (max == 0.0) ? 0.0 : log10(max);
+        }
+
+    } else if (m_verticalScale == PlusMinusOneScale) {
+
+        min = -1.0;
+        max = 1.0;
+
+    } else {
+
+        min = m_model->getValueMinimum();
+        max = m_model->getValueMaximum();
+
+        if (m_verticalScale == LogScale) {
+            min = (min < 0.0) ? -log10(-min) : (min == 0.0) ? 0.0 : log10(min);
+            max = (max < 0.0) ? -log10(-max) : (max == 0.0) ? 0.0 : log10(max);
+            log = true;
+        }
+    }
+
+    if (max == min) max = min + 1.0;
+}
+
 int
 TimeValueLayer::getYForValue(View *v, float val) const
 {
     float min = 0.0, max = 0.0;
+    bool logarithmic = false;
     int h = v->height();
 
-    if (!v->getValueExtents(m_model->getScaleUnits(), min, max)) {
-        min = m_model->getValueMinimum();
-        max = m_model->getValueMaximum();
+    getScaleExtents(v, min, max, logarithmic);
+
+//    std::cerr << "getYForValue(" << val << "): min " << min << ", max "
+//              << max << ", log " << logarithmic << std::endl;
+
+    if (logarithmic) {
+        val = (val < 0.0) ? -log10(-val) : (val == 0.0) ? 0.0 : log10(val);
     }
 
-    if (max == min) max = min + 1.0;
-
-/*!!!
-    float min = m_model->getValueMinimum();
-    float max = m_model->getValueMaximum();
-    if (max == min) max = min + 1.0;
-
-    int h = v->height();
-
-    if (m_verticalScale == FrequencyScale || m_verticalScale == LogScale) {
-        
-        if (m_verticalScale == FrequencyScale) {
-            // If we have a spectrogram layer on the same view as us, align
-            // ourselves with it...
-            for (int i = 0; i < v->getLayerCount(); ++i) {
-                SpectrogramLayer *spectrogram = dynamic_cast<SpectrogramLayer *>
-                    (v->getLayer(i));
-                if (spectrogram) {
-                    return spectrogram->getYForFrequency(v, val);
-                }
-            }
-        }
-
-        min = (min < 0.0) ? -log10(-min) : (min == 0.0) ? 0.0 : log10(min);
-        max = (max < 0.0) ? -log10(-max) : (max == 0.0) ? 0.0 : log10(max);
-        val = (val < 0.0) ? -log10(-val) : (val == 0.0) ? 0.0 : log10(val);
-
-    } else if (m_verticalScale == PlusMinusOneScale) {
-        min = -1.0;
-        max = 1.0;
-    }
-*/
-
     return int(h - ((val - min) * h) / (max - min));
 }
 
 float
 TimeValueLayer::getValueForY(View *v, int y) const
 {
-    //!!!
-
-    float min = m_model->getValueMinimum();
-    float max = m_model->getValueMaximum();
-    if (max == min) max = min + 1.0;
-
+    float min = 0.0, max = 0.0;
+    bool logarithmic = false;
     int h = v->height();
 
-    return min + (float(h - y) * float(max - min)) / h;
+    getScaleExtents(v, min, max, logarithmic);
+
+    float val = min + (float(h - y) * float(max - min)) / h;
+
+    if (logarithmic) {
+        val = pow(10, val);
+    }
+
+    return val;
 }
 
 QColor
-TimeValueLayer::getColourForValue(float val) const
+TimeValueLayer::getColourForValue(View *v, float val) const
 {
-    float min = m_model->getValueMinimum();
-    float max = m_model->getValueMaximum();
-    if (max == min) max = min + 1.0;
+    float min, max;
+    bool log;
+    getScaleExtents(v, min, max, log);
 
-    if (m_verticalScale == FrequencyScale || m_verticalScale == LogScale) {
-        min = (min < 0.0) ? -log10(-min) : (min == 0.0) ? 0.0 : log10(min);
-        max = (max < 0.0) ? -log10(-max) : (max == 0.0) ? 0.0 : log10(max);
+    if (log) {
         val = (val < 0.0) ? -log10(-val) : (val == 0.0) ? 0.0 : log10(val);
-    } else if (m_verticalScale == PlusMinusOneScale) {
-        min = -1.0;
-        max = 1.0;
     }
 
     int iv = ((val - min) / (max - min)) * 255.999;
@@ -581,7 +606,7 @@
 	paint.setPen(m_colour);
 
 	if (m_plotStyle == PlotSegmentation) {
-            paint.setBrush(getColourForValue(p.value));
+            paint.setBrush(getColourForValue(v, p.value));
 	    labelY = v->height();
 	} else if (m_plotStyle == PlotLines ||
 		   m_plotStyle == PlotCurve) {
@@ -744,7 +769,7 @@
         paint.save();
         for (int y = 0; y < boxh; ++y) {
             float val = ((boxh - y) * (max - min)) / boxh + min;
-            paint.setPen(getColourForValue(val));
+            paint.setPen(getColourForValue(v, val));
             paint.drawLine(boxx + 1, y + boxy + 1, boxx + boxw, y + boxy + 1);
         }
         paint.restore();
@@ -789,7 +814,7 @@
 void
 TimeValueLayer::drawStart(View *v, QMouseEvent *e)
 {
-    std::cerr << "TimeValueLayer::drawStart(" << e->x() << "," << e->y() << ")" << std::endl;
+//    std::cerr << "TimeValueLayer::drawStart(" << e->x() << "," << e->y() << ")" << std::endl;
 
     if (!m_model) return;
 
@@ -807,7 +832,7 @@
         for (SparseTimeValueModel::PointList::iterator i = points.begin();
              i != points.end(); ++i) {
             if (((i->frame / resolution) * resolution) != frame) {
-                std::cerr << "ignoring out-of-range frame at " << i->frame << std::endl;
+//                std::cerr << "ignoring out-of-range frame at " << i->frame << std::endl;
                 continue;
             }
             m_editingPoint = *i;
@@ -835,7 +860,7 @@
 void
 TimeValueLayer::drawDrag(View *v, QMouseEvent *e)
 {
-    std::cerr << "TimeValueLayer::drawDrag(" << e->x() << "," << e->y() << ")" << std::endl;
+//    std::cerr << "TimeValueLayer::drawDrag(" << e->x() << "," << e->y() << ")" << std::endl;
 
     if (!m_model || !m_editing) return;
 
@@ -848,7 +873,7 @@
 
     SparseTimeValueModel::PointList points = getLocalPoints(v, e->x());
 
-    std::cerr << points.size() << " points" << std::endl;
+//    std::cerr << points.size() << " points" << std::endl;
 
     bool havePoint = false;
 
@@ -857,14 +882,14 @@
              i != points.end(); ++i) {
             if (i->frame == m_editingPoint.frame &&
                 i->value == m_editingPoint.value) {
-                std::cerr << "ignoring current editing point at " << i->frame << ", " << i->value << std::endl;
+            //    std::cerr << "ignoring current editing point at " << i->frame << ", " << i->value << std::endl;
                 continue;
             }
             if (((i->frame / resolution) * resolution) != frame) {
-                std::cerr << "ignoring out-of-range frame at " << i->frame << std::endl;
+            //    std::cerr << "ignoring out-of-range frame at " << i->frame << std::endl;
                 continue;
             }
-            std::cerr << "adjusting to new point at " << i->frame << ", " << i->value << std::endl;
+        //    std::cerr << "adjusting to new point at " << i->frame << ", " << i->value << std::endl;
             m_editingPoint = *i;
             m_originalPoint = m_editingPoint;
             m_editingCommand->deletePoint(m_editingPoint);
@@ -887,7 +912,7 @@
 void
 TimeValueLayer::drawEnd(View *v, QMouseEvent *e)
 {
-    std::cerr << "TimeValueLayer::drawEnd(" << e->x() << "," << e->y() << ")" << std::endl;
+//    std::cerr << "TimeValueLayer::drawEnd(" << e->x() << "," << e->y() << ")" << std::endl;
     if (!m_model || !m_editing) return;
     m_editingCommand->finish();
     m_editingCommand = 0;
@@ -897,7 +922,7 @@
 void
 TimeValueLayer::editStart(View *v, QMouseEvent *e)
 {
-    std::cerr << "TimeValueLayer::editStart(" << e->x() << "," << e->y() << ")" << std::endl;
+//    std::cerr << "TimeValueLayer::editStart(" << e->x() << "," << e->y() << ")" << std::endl;
 
     if (!m_model) return;
 
@@ -918,7 +943,7 @@
 void
 TimeValueLayer::editDrag(View *v, QMouseEvent *e)
 {
-    std::cerr << "TimeValueLayer::editDrag(" << e->x() << "," << e->y() << ")" << std::endl;
+//    std::cerr << "TimeValueLayer::editDrag(" << e->x() << "," << e->y() << ")" << std::endl;
 
     if (!m_model || !m_editing) return;
 
@@ -942,7 +967,7 @@
 void
 TimeValueLayer::editEnd(View *v, QMouseEvent *e)
 {
-    std::cerr << "TimeValueLayer::editEnd(" << e->x() << "," << e->y() << ")" << std::endl;
+//    std::cerr << "TimeValueLayer::editEnd(" << e->x() << "," << e->y() << ")" << std::endl;
     if (!m_model || !m_editing) return;
 
     if (m_editingCommand) {
--- a/layer/TimeValueLayer.h	Thu May 11 15:02:14 2006 +0000
+++ b/layer/TimeValueLayer.h	Fri May 12 14:40:43 2006 +0000
@@ -88,10 +88,10 @@
     PlotStyle getPlotStyle() const { return m_plotStyle; }
 
     enum VerticalScale {
+        AutoAlignScale,
         LinearScale,
         LogScale,
-        PlusMinusOneScale,
-        FrequencyScale
+        PlusMinusOneScale
     };
     
     void setVerticalScale(VerticalScale scale);
@@ -107,7 +107,10 @@
         return m_plotStyle == PlotSegmentation && m_model->hasTextLabels();
     }
 
-    virtual bool getValueExtents(float &min, float &max, QString &unit) const;
+    virtual bool getValueExtents(float &min, float &max,
+                                 bool &logarithmic, QString &unit) const;
+
+    virtual bool getDisplayExtents(float &min, float &max) const;
 
     virtual QString toXmlString(QString indent = "",
 				QString extraAttributes = "") const;
@@ -115,10 +118,10 @@
     void setProperties(const QXmlAttributes &attributes);
 
 protected:
+    void getScaleExtents(View *, float &min, float &max, bool &log) const;
     int getYForValue(View *, float value) const;
     float getValueForY(View *, int y) const;
-
-    QColor getColourForValue(float value) const;
+    QColor getColourForValue(View *v, float value) const;
 
     SparseTimeValueModel::PointList getLocalPoints(View *v, int) const;
 
--- a/layer/WaveformLayer.cpp	Thu May 11 15:02:14 2006 +0000
+++ b/layer/WaveformLayer.cpp	Fri May 12 14:40:43 2006 +0000
@@ -312,7 +312,7 @@
 void
 WaveformLayer::setChannel(int channel)
 {
-    std::cerr << "WaveformLayer::setChannel(" << channel << ")" << std::endl;
+//    std::cerr << "WaveformLayer::setChannel(" << channel << ")" << std::endl;
 
     if (m_channel == channel) return;
     m_channel = channel;
@@ -348,7 +348,8 @@
 }
 
 bool
-WaveformLayer::getValueExtents(float &min, float &max, QString &unit) const
+WaveformLayer::getValueExtents(float &min, float &max,
+                               bool &log, QString &unit) const
 {
     if (m_scale == LinearScale) {
         min = 0.0;
--- a/layer/WaveformLayer.h	Thu May 11 15:02:14 2006 +0000
+++ b/layer/WaveformLayer.h	Fri May 12 14:40:43 2006 +0000
@@ -173,7 +173,8 @@
 
     virtual int getCompletion() const;
 
-    virtual bool getValueExtents(float &min, float &max, QString &unit) const;
+    virtual bool getValueExtents(float &min, float &max,
+                                 bool &log, QString &unit) const;
 
     virtual QString toXmlString(QString indent = "",
 				QString extraAttributes = "") const;
--- a/widgets/ItemEditDialog.cpp	Thu May 11 15:02:14 2006 +0000
+++ b/widgets/ItemEditDialog.cpp	Fri May 12 14:40:43 2006 +0000
@@ -118,7 +118,7 @@
         m_realDurationUSecsSpinBox->setMaximum(999999);
         m_realDurationUSecsSpinBox->setSuffix(tr(" usec"));
         m_realDurationUSecsSpinBox->setSingleStep(singleStep);
-        subgrid->addWidget(m_realDurationUSecsSpinBox, subrow, 3);
+        subgrid->addWidget(m_realDurationUSecsSpinBox, subrow, 2);
         connect(m_realDurationUSecsSpinBox, SIGNAL(valueChanged(int)),
                 this, SLOT(realDurationUSecsChanged(int)));
 
--- a/widgets/Pane.cpp	Thu May 11 15:02:14 2006 +0000
+++ b/widgets/Pane.cpp	Fri May 12 14:40:43 2006 +0000
@@ -839,7 +839,7 @@
         return;
     }
 
-    std::cerr << "mouseDoubleClickEvent" << std::endl;
+//    std::cerr << "mouseDoubleClickEvent" << std::endl;
 
     m_clickPos = e->pos();
     m_clickedInRange = true;
@@ -849,7 +849,8 @@
     ViewManager::ToolMode mode = ViewManager::NavigateMode;
     if (m_manager) mode = m_manager->getToolMode();
 
-    if (mode == ViewManager::EditMode) {
+    if (mode == ViewManager::NavigateMode ||
+        mode == ViewManager::EditMode) {
 
 	Layer *layer = getSelectedLayer();
 	if (layer && layer->isLayerEditable()) {
@@ -1013,7 +1014,7 @@
 Pane::toolModeChanged()
 {
     ViewManager::ToolMode mode = m_manager->getToolMode();
-    std::cerr << "Pane::toolModeChanged(" << mode << ")" << std::endl;
+//    std::cerr << "Pane::toolModeChanged(" << mode << ")" << std::endl;
 
     switch (mode) {