changeset 287:cd2492c5fe45

* Add SingleColourLayer to manage colours for layers that have a single predominant colour (i.e. most of them).
author Chris Cannam
date Thu, 12 Jul 2007 16:14:59 +0000
parents 7554ae119882
children e27f546f83ad
files layer/Colour3DPlotLayer.cpp layer/Colour3DPlotLayer.h layer/Layer.h layer/NoteLayer.cpp layer/NoteLayer.h layer/SingleColourLayer.cpp layer/SingleColourLayer.h layer/SliceLayer.cpp layer/SliceLayer.h layer/SpectrogramLayer.cpp layer/SpectrogramLayer.h layer/SpectrumLayer.cpp layer/TextLayer.cpp layer/TextLayer.h layer/TimeInstantLayer.cpp layer/TimeInstantLayer.h layer/TimeRulerLayer.cpp layer/TimeRulerLayer.h layer/TimeValueLayer.cpp layer/TimeValueLayer.h layer/WaveformLayer.cpp layer/WaveformLayer.h layer/layer.pro view/Overview.cpp view/Pane.cpp view/View.cpp view/View.h widgets/AudioDial.cpp widgets/PropertyBox.cpp
diffstat 29 files changed, 706 insertions(+), 761 deletions(-) [+]
line wrap: on
line diff
--- a/layer/Colour3DPlotLayer.cpp	Wed Jul 11 20:46:37 2007 +0000
+++ b/layer/Colour3DPlotLayer.cpp	Thu Jul 12 16:14:59 2007 +0000
@@ -343,7 +343,7 @@
     int ch = h - 20;
     if (ch > 20 && m_cache) {
 
-        paint.setPen(Qt::black);
+        paint.setPen(v->getForeground());
         paint.drawRect(4, 10, cw - 8, ch - 19);
 
         for (int y = 0; y < ch - 20; ++y) {
@@ -354,7 +354,7 @@
         }
     }
 
-    paint.setPen(Qt::black);
+    paint.setPen(v->getForeground());
 
     int count = v->height() / paint.fontMetrics().height();
     int step = m_model->getHeight() / count;
@@ -643,7 +643,7 @@
 
 	    if (illuminate) {
 		if (r.contains(illuminatePos)) {
-		    paint.setPen(Qt::black);//!!!
+		    paint.setPen(v->getForeground());
 		}
 	    }
             
@@ -660,7 +660,7 @@
 		    float value = m_model->getValueAt(scx, sy);
 		    sprintf(labelbuf, "%06f", value);
 		    QString text(labelbuf);
-		    paint.setPen(Qt::white);
+		    paint.setPen(v->getBackground());
 		    paint.drawText(rx0 + 2,
 				   ry0 - h / sh - 1 + 2 + paint.fontMetrics().ascent(),
 				   text);
--- a/layer/Colour3DPlotLayer.h	Wed Jul 11 20:46:37 2007 +0000
+++ b/layer/Colour3DPlotLayer.h	Thu Jul 12 16:14:59 2007 +0000
@@ -62,7 +62,9 @@
 
     virtual bool isLayerScrollable(const View *v) const;
 
-    virtual bool isLayerColourSignificant() const { return true; }
+    virtual ColourSignificance getLayerColourSignificance() const {
+        return ColourHasMeaningfulValue;
+    }
 
     void setModel(const DenseThreeDimensionalModel *model);
 
--- a/layer/Layer.h	Wed Jul 11 20:46:37 2007 +0000
+++ b/layer/Layer.h	Thu Jul 12 16:14:59 2007 +0000
@@ -229,15 +229,33 @@
      */
     virtual bool isLayerOpaque() const { return false; }
 
+    enum ColourSignificance {
+        ColourAbsent,
+        ColourIrrelevant,
+        ColourDistinguishes,
+        ColourAndBackgroundSignificant,
+        ColourHasMeaningfulValue
+    };
+
     /**
-     * This should return true if the layer uses colours to indicate
-     * meaningful information (as opposed to just using a single
-     * colour of the user's choice).  If this is the case, the view
-     * will show selections using unfilled rectangles instead of
-     * translucent filled rectangles, so as not to disturb the colours
-     * underneath.
+     * This should return the degree of meaning associated with colour
+     * in this layer.
+     *
+     * If ColourAbsent, the layer does not use colour.  If
+     * ColourIrrelevant, the layer is coloured and the colour may be
+     * set by the user, but it doesn't really matter what the colour
+     * is (for example, in a time ruler layer).  If
+     * ColourDistinguishes, then the colour is used to distinguish
+     * this layer from other similar layers (e.g. for data layers).
+     * If ColourAndBackgroundSignificant, then the layer should be
+     * given greater weight than ColourDistinguishes layers when
+     * choosing a background colour (e.g. for waveforms).  If
+     * ColourHasMeaningfulValue, colours are actually meaningful --
+     * the view will then show selections using unfilled rectangles
+     * instead of translucent filled rectangles, so as not to disturb
+     * the colours underneath.
      */
-    virtual bool isLayerColourSignificant() const { return false; }
+    virtual ColourSignificance getLayerColourSignificance() const = 0;
 
     /**
      * This should return true if the layer can be edited by the user.
--- a/layer/NoteLayer.cpp	Wed Jul 11 20:46:37 2007 +0000
+++ b/layer/NoteLayer.cpp	Thu Jul 12 16:14:59 2007 +0000
@@ -20,6 +20,7 @@
 #include "base/Profiler.h"
 #include "base/Pitch.h"
 #include "base/LogRange.h"
+#include "base/ColourDatabase.h"
 #include "view/View.h"
 
 #include "data/model/NoteModel.h"
@@ -36,13 +37,12 @@
 #include <cmath>
 
 NoteLayer::NoteLayer() :
-    Layer(),
+    SingleColourLayer(),
     m_model(0),
     m_editing(false),
     m_originalPoint(0, 0.0, 0, tr("New Point")),
     m_editingPoint(0, 0.0, 0, tr("New Point")),
     m_editingCommand(0),
-    m_colour(Qt::black),
     m_verticalScale(AutoAlignScale)
 {
     
@@ -69,8 +69,7 @@
 Layer::PropertyList
 NoteLayer::getProperties() const
 {
-    PropertyList list;
-    list.push_back("Colour");
+    PropertyList list = SingleColourLayer::getProperties();
     list.push_back("Vertical Scale");
     list.push_back("Scale Units");
     return list;
@@ -79,17 +78,17 @@
 QString
 NoteLayer::getPropertyLabel(const PropertyName &name) const
 {
-    if (name == "Colour") return tr("Colour");
     if (name == "Vertical Scale") return tr("Vertical Scale");
     if (name == "Scale Units") return tr("Scale Units");
-    return "";
+    return SingleColourLayer::getPropertyLabel(name);
 }
 
 Layer::PropertyType
 NoteLayer::getPropertyType(const PropertyName &name) const
 {
     if (name == "Scale Units") return UnitsProperty;
-    return ValueProperty;
+    if (name == "Vertical Scale") return ValueProperty;
+    return SingleColourLayer::getPropertyType(name);
 }
 
 QString
@@ -98,31 +97,16 @@
     if (name == "Vertical Scale" || name == "Scale Units") {
         return tr("Scale");
     }
-    return QString();
+    return SingleColourLayer::getPropertyGroupName(name);
 }
 
 int
 NoteLayer::getPropertyRangeAndValue(const PropertyName &name,
                                     int *min, int *max, int *deflt) const
 {
-    //!!! factor this colour handling stuff out into a colour manager class
-
     int val = 0;
 
-    if (name == "Colour") {
-
-	if (min) *min = 0;
-	if (max) *max = 5;
-        if (deflt) *deflt = 0;
-
-	if (m_colour == Qt::black) val = 0;
-	else if (m_colour == Qt::darkRed) val = 1;
-	else if (m_colour == Qt::darkBlue) val = 2;
-	else if (m_colour == Qt::darkGreen) val = 3;
-	else if (m_colour == QColor(200, 50, 255)) val = 4;
-	else if (m_colour == QColor(255, 150, 50)) val = 5;
-
-    } else if (name == "Vertical Scale") {
+    if (name == "Vertical Scale") {
 	
 	if (min) *min = 0;
 	if (max) *max = 3;
@@ -140,7 +124,7 @@
 
     } else {
 
-	val = Layer::getPropertyRangeAndValue(name, min, max, deflt);
+	val = SingleColourLayer::getPropertyRangeAndValue(name, min, max, deflt);
     }
 
     return val;
@@ -148,19 +132,9 @@
 
 QString
 NoteLayer::getPropertyValueLabel(const PropertyName &name,
-				    int value) const
+                                 int value) const
 {
-    if (name == "Colour") {
-	switch (value) {
-	default:
-	case 0: return tr("Black");
-	case 1: return tr("Red");
-	case 2: return tr("Blue");
-	case 3: return tr("Green");
-	case 4: return tr("Purple");
-	case 5: return tr("Orange");
-	}
-    } else if (name == "Vertical Scale") {
+    if (name == "Vertical Scale") {
 	switch (value) {
 	default:
 	case 0: return tr("Auto-Align");
@@ -169,23 +143,13 @@
 	case 3: return tr("MIDI Notes");
 	}
     }
-    return tr("<unknown>");
+    return SingleColourLayer::getPropertyValueLabel(name, value);
 }
 
 void
 NoteLayer::setProperty(const PropertyName &name, int value)
 {
-    if (name == "Colour") {
-	switch (value) {
-	default:
-	case 0:	setBaseColour(Qt::black); break;
-	case 1: setBaseColour(Qt::darkRed); break;
-	case 2: setBaseColour(Qt::darkBlue); break;
-	case 3: setBaseColour(Qt::darkGreen); break;
-	case 4: setBaseColour(QColor(200, 50, 255)); break;
-	case 5: setBaseColour(QColor(255, 150, 50)); break;
-	}
-    } else if (name == "Vertical Scale") {
+    if (name == "Vertical Scale") {
 	setVerticalScale(VerticalScale(value));
     } else if (name == "Scale Units") {
         if (m_model) {
@@ -193,18 +157,12 @@
                 (UnitDatabase::getInstance()->getUnitById(value));
             emit modelChanged();
         }
+    } else {
+        return SingleColourLayer::setProperty(name, value);
     }
 }
 
 void
-NoteLayer::setBaseColour(QColor colour)
-{
-    if (m_colour == colour) return;
-    m_colour = colour;
-    emit layerParametersChanged();
-}
-
-void
 NoteLayer::setVerticalScale(VerticalScale scale)
 {
     if (m_verticalScale == scale) return;
@@ -592,9 +550,9 @@
     NoteModel::PointList points(m_model->getPoints(frame0, frame1));
     if (points.empty()) return;
 
-    paint.setPen(m_colour);
+    paint.setPen(getBaseQColor());
 
-    QColor brushColour(m_colour);
+    QColor brushColour(getBaseQColor());
     brushColour.setAlpha(80);
 
 //    std::cerr << "NoteLayer::paint: resolution is "
@@ -632,13 +590,13 @@
 	}
 
 	if (w < 1) w = 1;
-	paint.setPen(m_colour);
+	paint.setPen(getBaseQColor());
 	paint.setBrush(brushColour);
 
 	if (illuminateFrame == p.frame) {
 	    if (localPos.y() >= y - h && localPos.y() < y) {
-		paint.setPen(Qt::black);//!!!
-		paint.setBrush(Qt::black);//!!!
+		paint.setPen(v->getForeground());
+		paint.setBrush(v->getForeground());
 	    }
 	}
 	
@@ -982,24 +940,26 @@
     return true;
 }
 
+int
+NoteLayer::getDefaultColourHint(bool darkbg, bool &impose)
+{
+    impose = false;
+    return ColourDatabase::getInstance()->getColourIndex
+        (QString(darkbg ? "White" : "Black"));
+}
+
 QString
 NoteLayer::toXmlString(QString indent, QString extraAttributes) const
 {
-    return Layer::toXmlString(indent, extraAttributes +
-			      QString(" colour=\"%1\" verticalScale=\"%2\"")
-			      .arg(encodeColour(m_colour)).arg(m_verticalScale));
+    return SingleColourLayer::toXmlString(indent, extraAttributes +
+                                          QString(" verticalScale=\"%1\"")
+                                          .arg(m_verticalScale));
 }
 
 void
 NoteLayer::setProperties(const QXmlAttributes &attributes)
 {
-    QString colourSpec = attributes.value("colour");
-    if (colourSpec != "") {
-	QColor colour(colourSpec);
-	if (colour.isValid()) {
-	    setBaseColour(QColor(colourSpec));
-	}
-    }
+    SingleColourLayer::setProperties(attributes);
 
     bool ok;
     VerticalScale scale = (VerticalScale)
--- a/layer/NoteLayer.h	Wed Jul 11 20:46:37 2007 +0000
+++ b/layer/NoteLayer.h	Thu Jul 12 16:14:59 2007 +0000
@@ -16,7 +16,7 @@
 #ifndef _NOTE_LAYER_H_
 #define _NOTE_LAYER_H_
 
-#include "Layer.h"
+#include "SingleColourLayer.h"
 #include "data/model/NoteModel.h"
 
 #include <QObject>
@@ -25,7 +25,7 @@
 class View;
 class QPainter;
 
-class NoteLayer : public Layer
+class NoteLayer : public SingleColourLayer
 {
     Q_OBJECT
 
@@ -71,9 +71,6 @@
 					  int value) const;
     virtual void setProperty(const PropertyName &, int value);
 
-    void setBaseColour(QColor);
-    QColor getBaseColour() const { return m_colour; }
-
     enum VerticalScale {
         AutoAlignScale,
         LinearScale,
@@ -106,6 +103,8 @@
     float getValueForY(View *v, int y) const;
     bool shouldConvertMIDIToHz() const;
 
+    virtual int getDefaultColourHint(bool dark, bool &impose);
+
     NoteModel::PointList getLocalPoints(View *v, int) const;
 
     NoteModel *m_model;
@@ -113,7 +112,6 @@
     NoteModel::Point m_originalPoint;
     NoteModel::Point m_editingPoint;
     NoteModel::EditCommand *m_editingCommand;
-    QColor m_colour;
     VerticalScale m_verticalScale;
 };
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/layer/SingleColourLayer.cpp	Thu Jul 12 16:14:59 2007 +0000
@@ -0,0 +1,243 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
+
+/*
+    Sonic Visualiser
+    An audio file viewer and annotation editor.
+    Centre for Digital Music, Queen Mary, University of London.
+    This file copyright 2007 QMUL.
+    
+    This program is free software; you can redistribute it and/or
+    modify it under the terms of the GNU General Public License as
+    published by the Free Software Foundation; either version 2 of the
+    License, or (at your option) any later version.  See the file
+    COPYING included with this distribution for more information.
+*/
+
+#include "SingleColourLayer.h"
+#include "base/ColourDatabase.h"
+#include "view/View.h"
+
+#include <iostream>
+
+#include <QApplication>
+
+SingleColourLayer::ColourIndexPool 
+SingleColourLayer::m_usedColourIndices;
+
+SingleColourLayer::SingleColourLayer() :
+    m_colour(0)
+{
+    setDefaultColourFor(0);
+}
+
+bool
+SingleColourLayer::hasLightBackground() const
+{
+    bool dark = ColourDatabase::getInstance()->useDarkBackground(m_colour);
+    return !dark;
+}
+
+Layer::PropertyList
+SingleColourLayer::getProperties() const
+{
+    PropertyList list = Layer::getProperties();
+    list.push_back("Colour");
+    return list;
+}
+
+QString
+SingleColourLayer::getPropertyLabel(const PropertyName &name) const
+{
+    if (name == "Colour") return tr("Colour");
+    return "";
+}
+
+Layer::PropertyType
+SingleColourLayer::getPropertyType(const PropertyName &name) const
+{
+    if (name == "Colour") return ColourProperty;
+    return InvalidProperty;
+}
+
+QString
+SingleColourLayer::getPropertyGroupName(const PropertyName &) const
+{
+    return QString();
+}
+
+int
+SingleColourLayer::getPropertyRangeAndValue(const PropertyName &name,
+                                        int *min, int *max, int *deflt) const
+{
+    int val = 0;
+
+    int garbage0, garbage1, garbage2;
+    if (!min) min = &garbage0;
+    if (!max) max = &garbage1;
+    if (!deflt) deflt = &garbage2;
+
+    if (name == "Colour") {
+
+        ColourDatabase::getInstance()->getColourPropertyRange(min, max);
+        *deflt = 0; //!!!
+
+        val = m_colour;
+
+    } else {
+	val = Layer::getPropertyRangeAndValue(name, min, max, deflt);
+    }
+
+    return val;
+}
+
+QString
+SingleColourLayer::getPropertyValueLabel(const PropertyName &name,
+				    int value) const
+{
+    if (name == "Colour") {
+        return Layer::getPropertyValueLabel(name, value);
+    }
+    return tr("<unknown>");
+}
+
+RangeMapper *
+SingleColourLayer::getNewPropertyRangeMapper(const PropertyName &) const
+{
+    return 0;
+}
+
+void
+SingleColourLayer::setProperty(const PropertyName &name, int value)
+{
+    if (name == "Colour") {
+        setBaseColour(value);
+    }
+}
+
+void
+SingleColourLayer::setDefaultColourFor(View *v)
+{
+    bool dark = false;
+    if (v) {
+        ColourIndexPool::iterator i = m_usedColourIndices.find(m_colour);
+        if (i != m_usedColourIndices.end()) m_usedColourIndices.erase(i);
+        dark = !v->hasLightBackground();
+    } else {
+        QColor bg = QApplication::palette().color(QPalette::Window);
+        if (bg.red() + bg.green() + bg.blue() < 384) dark = true;
+    }
+
+    m_colour = -1;
+    ColourDatabase *cdb = ColourDatabase::getInstance();
+
+    int hint = -1;
+    bool impose = false;
+    if (v) {
+        // We don't want to call this if !v because that probably
+        // means we're being called from the constructor, and this is
+        // a virtual function
+        hint = getDefaultColourHint(dark, impose);
+        std::cerr << "hint = " << hint << ", impose = " << impose << std::endl;
+    }
+
+    if (hint >= 0 && impose) {
+        m_colour = hint;
+        m_usedColourIndices.insert(m_colour);
+        return;
+    }
+
+    for (int i = 0; i < cdb->getColourCount(); ++i) {
+        int index = i;
+        if (hint > 0) index = (index + hint) % cdb->getColourCount();
+        if (cdb->useDarkBackground(index) != dark) continue;
+        if (m_colour < 0) m_colour = index;
+        if (m_usedColourIndices.find(index) == m_usedColourIndices.end()) {
+            m_colour = index;
+            break;
+        }
+    }
+
+    if (m_colour < 0) m_colour = 0;
+    m_usedColourIndices.insert(m_colour);
+}
+
+void
+SingleColourLayer::setBaseColour(int colour)
+{
+    if (m_colour == colour) return;
+    ColourIndexPool::iterator i = m_usedColourIndices.find(m_colour);
+    if (i != m_usedColourIndices.end()) m_usedColourIndices.erase(i);
+    m_colour = colour;
+    m_usedColourIndices.insert(m_colour);
+    flagBaseColourChanged();
+    emit layerParametersChanged();
+}
+
+int
+SingleColourLayer::getBaseColour() const
+{
+    return m_colour;
+}
+
+QColor
+SingleColourLayer::getBaseQColor() const
+{
+    return ColourDatabase::getInstance()->getColour(m_colour);
+}
+
+QColor
+SingleColourLayer::getBackgroundQColor(View *v) const
+{
+    return v->getBackground();
+}
+
+QColor
+SingleColourLayer::getForegroundQColor(View *v) const
+{
+    return v->getForeground();
+}
+
+std::vector<QColor>
+SingleColourLayer::getPartialShades(View *v) const
+{
+    std::vector<QColor> s;
+    QColor base = getBaseQColor();
+    QColor bg = getBackgroundQColor(v);
+    for (int i = 0; i < 3; ++i) {
+        int red = base.red() + ((bg.red() - base.red()) * (i + 1)) / 4;
+        int green = base.green() + ((bg.green() - base.green()) * (i + 1)) / 4;
+        int blue = base.blue() + ((bg.blue() - base.blue()) * (i + 1)) / 4;
+        s.push_back(QColor(red, green, blue));
+    }
+    return s;
+}
+
+QString
+SingleColourLayer::toXmlString(QString indent, QString extraAttributes) const
+{
+    QString s;
+    
+    QString colourName, colourSpec, darkbg;
+    ColourDatabase::getInstance()->getStringValues
+        (m_colour, colourName, colourSpec, darkbg);
+
+    s += QString("colourName=\"%1\" "
+                 "colour=\"%2\" "
+                 "darkBackground=\"%3\" ")
+	.arg(colourName)
+        .arg(colourSpec)
+        .arg(darkbg);
+
+    return Layer::toXmlString(indent, extraAttributes + " " + s);
+}
+
+void
+SingleColourLayer::setProperties(const QXmlAttributes &attributes)
+{
+    QString colourName = attributes.value("colourName");
+    QString colourSpec = attributes.value("colour");
+    QString darkbg = attributes.value("darkBackground");
+    m_colour = ColourDatabase::getInstance()->putStringValues
+        (colourName, colourSpec, darkbg);
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/layer/SingleColourLayer.h	Thu Jul 12 16:14:59 2007 +0000
@@ -0,0 +1,71 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
+
+/*
+    Sonic Visualiser
+    An audio file viewer and annotation editor.
+    Centre for Digital Music, Queen Mary, University of London.
+    This file copyright 2007 QMUL.
+    
+    This program is free software; you can redistribute it and/or
+    modify it under the terms of the GNU General Public License as
+    published by the Free Software Foundation; either version 2 of the
+    License, or (at your option) any later version.  See the file
+    COPYING included with this distribution for more information.
+*/
+
+#ifndef _SINGLE_COLOUR_LAYER_H_
+#define _SINGLE_COLOUR_LAYER_H_
+
+#include "Layer.h"
+#include <QColor>
+#include <vector>
+
+class SingleColourLayer : public Layer
+{
+public:
+    virtual void setBaseColour(int);
+    virtual int getBaseColour() const;
+
+    virtual bool hasLightBackground() const;
+
+    virtual ColourSignificance getLayerColourSignificance() const {
+        return ColourDistinguishes;
+    }
+
+    virtual PropertyList getProperties() const;
+    virtual QString getPropertyLabel(const PropertyName &) const;
+    virtual PropertyType getPropertyType(const PropertyName &) const;
+    virtual QString getPropertyGroupName(const PropertyName &) const;
+    virtual int getPropertyRangeAndValue(const PropertyName &,
+                                         int *min, int *max, int *deflt) const;
+    virtual QString getPropertyValueLabel(const PropertyName &,
+					  int value) const;
+    virtual RangeMapper *getNewPropertyRangeMapper(const PropertyName &) const;
+    virtual void setProperty(const PropertyName &, int value);
+
+    virtual QString toXmlString(QString indent = "",
+				QString extraAttributes = "") const;
+
+    virtual void setProperties(const QXmlAttributes &attributes);
+
+    virtual void setDefaultColourFor(View *v);
+
+protected:
+    SingleColourLayer();
+
+    virtual QColor getBaseQColor() const;
+    virtual QColor getBackgroundQColor(View *v) const;
+    virtual QColor getForegroundQColor(View *v) const;
+    std::vector<QColor> getPartialShades(View *v) const;
+
+    virtual void flagBaseColourChanged() { }
+    virtual int getDefaultColourHint(bool /* darkBackground */,
+                                     bool & /* impose */) { return -1; }
+
+    typedef std::multiset<int> ColourIndexPool;
+    static ColourIndexPool m_usedColourIndices;
+
+    int m_colour;
+};
+
+#endif
--- a/layer/SliceLayer.cpp	Wed Jul 11 20:46:37 2007 +0000
+++ b/layer/SliceLayer.cpp	Thu Jul 12 16:14:59 2007 +0000
@@ -21,6 +21,7 @@
 #include "base/RangeMapper.h"
 #include "base/RealTime.h"
 #include "base/ColourMapper.h"
+#include "base/ColourDatabase.h"
 
 #include "PaintAssistant.h"
 
@@ -29,7 +30,6 @@
 
 SliceLayer::SliceLayer() :
     m_sliceableModel(0),
-    m_colour(Qt::darkBlue),
     m_colourMap(0),
     m_energyScale(dBScale),
     m_samplingMode(SampleMean),
@@ -330,7 +330,7 @@
         }
     }
 
-    paint.setPen(m_colour);
+    paint.setPen(getBaseQColor());
 
     int xorigin = getVerticalScaleWidth(v, paint) + 1;
     int w = v->width() - xorigin - 1;
@@ -549,8 +549,7 @@
 Layer::PropertyList
 SliceLayer::getProperties() const
 {
-    PropertyList list;
-    list.push_back("Colour");
+    PropertyList list = SingleColourLayer::getProperties();
     list.push_back("Plot Type");
 //    list.push_back("Sampling Mode");
     list.push_back("Scale");
@@ -565,7 +564,6 @@
 QString
 SliceLayer::getPropertyLabel(const PropertyName &name) const
 {
-    if (name == "Colour") return tr("Colour");
     if (name == "Plot Type") return tr("Plot Type");
     if (name == "Energy Scale") return tr("Scale");
     if (name == "Normalize") return tr("Normalize");
@@ -573,7 +571,7 @@
     if (name == "Gain") return tr("Gain");
     if (name == "Sampling Mode") return tr("Sampling Mode");
     if (name == "Bin Scale") return tr("Plot X Scale");
-    return "";
+    return SingleColourLayer::getPropertyLabel(name);
 }
 
 Layer::PropertyType
@@ -582,7 +580,12 @@
     if (name == "Gain") return RangeProperty;
     if (name == "Normalize") return ToggleProperty;
     if (name == "Threshold") return RangeProperty;
-    return ValueProperty;
+    if (name == "Plot Type") return ValueProperty;
+    if (name == "Energy Scale") return ValueProperty;
+    if (name == "Sampling Mode") return ValueProperty;
+    if (name == "Bin Scale") return ValueProperty;
+    if (name == "Colour" && m_plotStyle == PlotFilledBlocks) return ValueProperty;
+    return SingleColourLayer::getPropertyType(name);
 }
 
 QString
@@ -595,7 +598,7 @@
         name == "Gain") return tr("Scale");
     if (name == "Plot Type" ||
         name == "Bin Scale") return tr("Plot Type");
-    return QString();
+    return SingleColourLayer::getPropertyGroupName(name);
 }
 
 int
@@ -639,29 +642,13 @@
 	val = (m_normalize ? 1 : 0);
         *deflt = 0;
 
-    } else if (name == "Colour") {
-
-        if (m_plotStyle == PlotFilledBlocks) {
+    } else if (name == "Colour" && m_plotStyle == PlotFilledBlocks) {
             
-            *min = 0;
-            *max = ColourMapper::getColourMapCount() - 1;
-            *deflt = 0;
-
-            val = m_colourMap;
-
-        } else {
-
-            *min = 0;
-            *max = 5;
-            *deflt = 0;
-
-            if (m_colour == Qt::black) val = 0;
-            else if (m_colour == Qt::darkRed) val = 1;
-            else if (m_colour == Qt::darkBlue) val = 2;
-            else if (m_colour == Qt::darkGreen) val = 3;
-            else if (m_colour == QColor(200, 50, 255)) val = 4;
-            else if (m_colour == QColor(255, 150, 50)) val = 5;
-        }
+        *min = 0;
+        *max = ColourMapper::getColourMapCount() - 1;
+        *deflt = 0;
+        
+        val = m_colourMap;
 
     } else if (name == "Scale") {
 
@@ -697,7 +684,7 @@
         val = (int)m_binScale;
 
     } else {
-	val = Layer::getPropertyRangeAndValue(name, min, max, deflt);
+	val = SingleColourLayer::getPropertyRangeAndValue(name, min, max, deflt);
     }
 
     return val;
@@ -707,20 +694,8 @@
 SliceLayer::getPropertyValueLabel(const PropertyName &name,
 				    int value) const
 {
-    if (name == "Colour") {
-        if (m_plotStyle == PlotFilledBlocks) {
-            return ColourMapper::getColourMapName(value);
-        } else {
-            switch (value) {
-            default:
-            case 0: return tr("Black");
-            case 1: return tr("Red");
-            case 2: return tr("Blue");
-            case 3: return tr("Green");
-            case 4: return tr("Purple");
-            case 5: return tr("Orange");
-            }
-	}
+    if (name == "Colour" && m_plotStyle == PlotFilledBlocks) {
+        return ColourMapper::getColourMapName(value);
     }
     if (name == "Scale") {
 	switch (value) {
@@ -755,7 +730,7 @@
 	case 2: return tr("Rev Log Bins");
 	}
     }
-    return tr("<unknown>");
+    return SingleColourLayer::getPropertyValueLabel(name, value);
 }
 
 RangeMapper *
@@ -767,7 +742,7 @@
     if (name == "Threshold") {
         return new LinearRangeMapper(-80, 0, -80, 0, tr("dB"));
     }
-    return 0;
+    return SingleColourLayer::getNewPropertyRangeMapper(name);
 }
 
 void
@@ -778,20 +753,8 @@
     } else if (name == "Threshold") {
 	if (value == -80) setThreshold(0.0);
 	else setThreshold(AudioLevel::dB_to_multiplier(value));
-    } else if (name == "Colour") {
-        if (m_plotStyle == PlotFilledBlocks) {
-            setFillColourMap(value);
-        } else {
-            switch (value) {
-            default:
-            case 0: setBaseColour(Qt::black); break;
-            case 1: setBaseColour(Qt::darkRed); break;
-            case 2: setBaseColour(Qt::darkBlue); break;
-            case 3: setBaseColour(Qt::darkGreen); break;
-            case 4: setBaseColour(QColor(200, 50, 255)); break;
-            case 5: setBaseColour(QColor(255, 150, 50)); break;
-            }
-	}
+    } else if (name == "Colour" && m_plotStyle == PlotFilledBlocks) {
+        setFillColourMap(value);
     } else if (name == "Scale") {
 	switch (value) {
 	default:
@@ -817,18 +780,12 @@
 	}
     } else if (name == "Normalize") {
 	setNormalize(value ? true : false);
+    } else {
+        SingleColourLayer::setProperty(name, value);
     }
 }
 
 void
-SliceLayer::setBaseColour(QColor colour)
-{
-    if (m_colour == colour) return;
-    m_colour = colour;
-    emit layerParametersChanged();
-}
-
-void
 SliceLayer::setFillColourMap(int map)
 {
     if (m_colourMap == map) return;
@@ -905,25 +862,31 @@
     return db;
 }
 
+int
+SliceLayer::getDefaultColourHint(bool darkbg, bool &impose)
+{
+    impose = false;
+    return ColourDatabase::getInstance()->getColourIndex
+        (QString(darkbg ? "Bright Blue" : "Blue"));
+}
+
 QString
 SliceLayer::toXmlString(QString indent, QString extraAttributes) const
 {
     QString s;
     
-    s += QString("colour=\"%1\" "
-                 "colourScheme=\"%2\" "
-		 "energyScale=\"%3\" "
-                 "samplingMode=\"%4\" "
-                 "gain=\"%5\" "
-                 "normalize=\"%6\"")
-	.arg(encodeColour(m_colour))
+    s += QString("colourScheme=\"%1\" "
+		 "energyScale=\"%2\" "
+                 "samplingMode=\"%3\" "
+                 "gain=\"%4\" "
+                 "normalize=\"%5\"")
         .arg(m_colourMap)
 	.arg(m_energyScale)
         .arg(m_samplingMode)
         .arg(m_gain)
         .arg(m_normalize ? "true" : "false");
 
-    return Layer::toXmlString(indent, extraAttributes + " " + s);
+    return SingleColourLayer::toXmlString(indent, extraAttributes + " " + s);
 }
 
 void
@@ -931,13 +894,7 @@
 {
     bool ok = false;
 
-    QString colourSpec = attributes.value("colour");
-    if (colourSpec != "") {
-	QColor colour(colourSpec);
-	if (colour.isValid()) {
-	    setBaseColour(QColor(colourSpec));
-	}
-    }
+    SingleColourLayer::setProperties(attributes);
 
     EnergyScale scale = (EnergyScale)
 	attributes.value("energyScale").toInt(&ok);
--- a/layer/SliceLayer.h	Wed Jul 11 20:46:37 2007 +0000
+++ b/layer/SliceLayer.h	Thu Jul 12 16:14:59 2007 +0000
@@ -17,7 +17,7 @@
 #ifndef _SLICE_LAYER_H_
 #define _SLICE_LAYER_H_
 
-#include "Layer.h"
+#include "SingleColourLayer.h"
 
 #include "base/Window.h"
 
@@ -25,7 +25,7 @@
 
 #include <QColor>
 
-class SliceLayer : public Layer
+class SliceLayer : public SingleColourLayer
 {
     Q_OBJECT
 
@@ -46,6 +46,10 @@
     virtual int getVerticalScaleWidth(View *v, QPainter &) const;
     virtual void paintVerticalScale(View *v, QPainter &paint, QRect rect) const;
 
+    virtual ColourSignificance getLayerColourSignificance() const {
+        return ColourAndBackgroundSignificant;
+    }
+
     virtual PropertyList getProperties() const;
     virtual QString getPropertyLabel(const PropertyName &) const;
     virtual PropertyType getPropertyType(const PropertyName &) const;
@@ -73,9 +77,6 @@
 
     enum BinScale { LinearBins, LogBins, InvertedLogBins };
 
-    void setBaseColour(QColor);
-    QColor getBaseColour() const { return m_colour; }
-
     void setFillColourMap(int);
     int getFillColourMap() const { return m_colourMap; }
 
@@ -127,8 +128,9 @@
 
     virtual float getThresholdDb() const;
 
+    virtual int getDefaultColourHint(bool dark, bool &impose);
+
     const DenseThreeDimensionalModel *m_sliceableModel;
-    QColor                            m_colour;
     int                               m_colourMap;
     EnergyScale                       m_energyScale;
     SamplingMode                      m_samplingMode;
--- a/layer/SpectrogramLayer.cpp	Wed Jul 11 20:46:37 2007 +0000
+++ b/layer/SpectrogramLayer.cpp	Thu Jul 12 16:14:59 2007 +0000
@@ -1021,7 +1021,7 @@
 bool
 SpectrogramLayer::hasLightBackground() const 
 {
-    return (m_colourMap == (int)ColourMapper::BlackOnWhite);
+    return ColourMapper(m_colourMap, 1.f, 255.f).hasLightBackground();
 }
 
 void
@@ -2334,7 +2334,7 @@
 //        std::cerr << "SpectrogramLayer: illuminate "
 //                  << x0 << "," << y1 << " -> " << x1 << "," << y0 << std::endl;
         
-        paint.setPen(Qt::white);
+        paint.setPen(v->getForeground());
 
         //!!! should we be using paintCrosshairs for this?
 
@@ -2836,11 +2836,11 @@
                          idb % 10 == 0) ||
                         (abs(y - lasty) > paint.fontMetrics().ascent() && 
                          idb % 5 == 0))) {
-                paint.setPen(Qt::black);
+                paint.setPen(v->getBackground());
                 QString text = QString("%1").arg(idb);
                 paint.drawText(3 + cw - cbw - paint.fontMetrics().width(text),
                                y + toff + textHeight/2, text);
-                paint.setPen(Qt::white);
+                paint.setPen(v->getForeground());
                 paint.drawLine(5 + cw - cbw, y, 8 + cw - cbw, y);
                 lasty = y;
                 lastdb = idb;
--- a/layer/SpectrogramLayer.h	Wed Jul 11 20:46:37 2007 +0000
+++ b/layer/SpectrogramLayer.h	Thu Jul 12 16:14:59 2007 +0000
@@ -188,7 +188,10 @@
     }
 
     virtual bool isLayerOpaque() const { return true; }
-    virtual bool isLayerColourSignificant() const { return true; }
+
+    virtual ColourSignificance getLayerColourSignificance() const {
+        return ColourHasMeaningfulValue;
+    }
 
     float getYForFrequency(const View *v, float frequency) const;
     float getFrequencyForY(const View *v, int y) const;
--- a/layer/SpectrumLayer.cpp	Wed Jul 11 20:46:37 2007 +0000
+++ b/layer/SpectrumLayer.cpp	Thu Jul 12 16:14:59 2007 +0000
@@ -644,7 +644,9 @@
     int w = v->width() - xorigin - 1;
 
     int pkh = 0;
-    if (m_binScale == LogBins) pkh = 10;
+//!!!    if (m_binScale == LogBins) {
+        pkh = 10;
+//!!!    }
 
     if (fft && m_showPeaks) {
 
@@ -707,9 +709,9 @@
     //relevant to Colour3DPlotLayer with unit Hz, but that's a bigger
     //proposition.
 
-    if (m_binScale == LogBins) {
+//    if (m_binScale == LogBins) {
 
-        int pkh = 10;
+//        int pkh = 10;
         int h = v->height();
 
         // piano keyboard
@@ -779,7 +781,7 @@
             ppx = px;
 	    px = x;
 	}
-    }
+//    }
 }
 
 void
--- a/layer/TextLayer.cpp	Wed Jul 11 20:46:37 2007 +0000
+++ b/layer/TextLayer.cpp	Thu Jul 12 16:14:59 2007 +0000
@@ -18,6 +18,7 @@
 #include "data/model/Model.h"
 #include "base/RealTime.h"
 #include "base/Profiler.h"
+#include "base/ColourDatabase.h"
 #include "view/View.h"
 
 #include "data/model/TextModel.h"
@@ -30,13 +31,12 @@
 #include <cmath>
 
 TextLayer::TextLayer() :
-    Layer(),
+    SingleColourLayer(),
     m_model(0),
     m_editing(false),
     m_originalPoint(0, 0.0, tr("Empty Label")),
     m_editingPoint(0, 0.0, tr("Empty Label")),
-    m_editingCommand(0),
-    m_colour(255, 150, 50) // orange
+    m_editingCommand(0)
 {
     
 }
@@ -62,85 +62,40 @@
 Layer::PropertyList
 TextLayer::getProperties() const
 {
-    PropertyList list;
-    list.push_back("Colour");
+    PropertyList list = SingleColourLayer::getProperties();
     return list;
 }
 
 QString
 TextLayer::getPropertyLabel(const PropertyName &name) const
 {
-    if (name == "Colour") return tr("Colour");
-    return "";
+    return SingleColourLayer::getPropertyLabel(name);
 }
 
 Layer::PropertyType
-TextLayer::getPropertyType(const PropertyName &) const
+TextLayer::getPropertyType(const PropertyName &name) const
 {
-    return ValueProperty;
+    return SingleColourLayer::getPropertyType(name);
 }
 
 int
 TextLayer::getPropertyRangeAndValue(const PropertyName &name,
 				    int *min, int *max, int *deflt) const
 {
-    //!!! factor this colour handling stuff out into a colour manager class
-
-    int val = 0;
-
-    if (name == "Colour") {
-
-	if (min) *min = 0;
-	if (max) *max = 5;
-        if (deflt) *deflt = 0;
-
-	if (m_colour == Qt::black) val = 0;
-	else if (m_colour == Qt::darkRed) val = 1;
-	else if (m_colour == Qt::darkBlue) val = 2;
-	else if (m_colour == Qt::darkGreen) val = 3;
-	else if (m_colour == QColor(200, 50, 255)) val = 4;
-	else if (m_colour == QColor(255, 150, 50)) val = 5;
-
-    } else {
-	
-	val = Layer::getPropertyRangeAndValue(name, min, max, deflt);
-    }
-
-    return val;
+    return SingleColourLayer::getPropertyRangeAndValue(name, min, max, deflt);
 }
 
 QString
 TextLayer::getPropertyValueLabel(const PropertyName &name,
 				 int value) const
 {
-    if (name == "Colour") {
-	switch (value) {
-	default:
-	case 0: return tr("Black");
-	case 1: return tr("Red");
-	case 2: return tr("Blue");
-	case 3: return tr("Green");
-	case 4: return tr("Purple");
-	case 5: return tr("Orange");
-	}
-    }
-    return tr("<unknown>");
+    return SingleColourLayer::getPropertyValueLabel(name, value);
 }
 
 void
 TextLayer::setProperty(const PropertyName &name, int value)
 {
-    if (name == "Colour") {
-	switch (value) {
-	default:
-	case 0:	setBaseColour(Qt::black); break;
-	case 1: setBaseColour(Qt::darkRed); break;
-	case 2: setBaseColour(Qt::darkBlue); break;
-	case 3: setBaseColour(Qt::darkGreen); break;
-	case 4: setBaseColour(QColor(200, 50, 255)); break;
-	case 5: setBaseColour(QColor(255, 150, 50)); break;
-	}
-    }
+    SingleColourLayer::setProperty(name, value);
 }
 
 bool
@@ -149,14 +104,6 @@
     return false;
 }
 
-void
-TextLayer::setBaseColour(QColor colour)
-{
-    if (m_colour == colour) return;
-    m_colour = colour;
-    emit layerParametersChanged();
-}
-
 bool
 TextLayer::isLayerScrollable(const View *v) const
 {
@@ -350,18 +297,14 @@
     TextModel::PointList points(m_model->getPoints(frame0, frame1));
     if (points.empty()) return;
 
-    QColor brushColour(m_colour);
+    QColor brushColour(getBaseQColor());
 
     int h, s, val;
     brushColour.getHsv(&h, &s, &val);
     brushColour.setHsv(h, s, 255, 100);
 
     QColor penColour;
-    if (v->hasLightBackground()) {
-	penColour = Qt::black;
-    } else {
-	penColour = Qt::white;
-    }
+    penColour = v->getForeground();
 
 //    std::cerr << "TextLayer::paint: resolution is "
 //	      << m_model->getResolution() << " frames" << std::endl;
@@ -391,11 +334,7 @@
 
 	if (illuminateFrame == p.frame) {
 	    paint.setBrush(penColour);
-	    if (v->hasLightBackground()) {
-		paint.setPen(Qt::white);
-	    } else {
-		paint.setPen(Qt::black);
-	    }
+            paint.setPen(v->getBackground());
 	} else {
 	    paint.setPen(penColour);
 	    paint.setBrush(brushColour);
@@ -756,23 +695,23 @@
     return true;
 }
 
+int
+TextLayer::getDefaultColourHint(bool darkbg, bool &impose)
+{
+    impose = false;
+    return ColourDatabase::getInstance()->getColourIndex
+        (QString(darkbg ? "Bright Orange" : "Orange"));
+}
+
 QString
 TextLayer::toXmlString(QString indent, QString extraAttributes) const
 {
-    return Layer::toXmlString(indent, extraAttributes +
-			      QString(" colour=\"%1\"")
-			      .arg(encodeColour(m_colour)));
+    return SingleColourLayer::toXmlString(indent, extraAttributes);
 }
 
 void
 TextLayer::setProperties(const QXmlAttributes &attributes)
 {
-    QString colourSpec = attributes.value("colour");
-    if (colourSpec != "") {
-	QColor colour(colourSpec);
-	if (colour.isValid()) {
-	    setBaseColour(QColor(colourSpec));
-	}
-    }
+    SingleColourLayer::setProperties(attributes);
 }
 
--- a/layer/TextLayer.h	Wed Jul 11 20:46:37 2007 +0000
+++ b/layer/TextLayer.h	Thu Jul 12 16:14:59 2007 +0000
@@ -16,7 +16,7 @@
 #ifndef _TEXT_LAYER_H_
 #define _TEXT_LAYER_H_
 
-#include "Layer.h"
+#include "SingleColourLayer.h"
 #include "data/model/TextModel.h"
 
 #include <QObject>
@@ -25,7 +25,7 @@
 class View;
 class QPainter;
 
-class TextLayer : public Layer
+class TextLayer : public SingleColourLayer
 {
     Q_OBJECT
 
@@ -70,9 +70,6 @@
 					  int value) const;
     virtual void setProperty(const PropertyName &, int value);
 
-    void setBaseColour(QColor);
-    QColor getBaseColour() const { return m_colour; }
-
     virtual bool isLayerScrollable(const View *v) const;
 
     virtual bool isLayerEditable() const { return true; }
@@ -91,6 +88,8 @@
     int getYForHeight(View *v, float height) const;
     float getHeightForY(View *v, int y) const;
 
+    virtual int getDefaultColourHint(bool dark, bool &impose);
+
     TextModel::PointList getLocalPoints(View *v, int x, int y) const;
 
     TextModel *m_model;
@@ -99,7 +98,6 @@
     TextModel::Point m_originalPoint;
     TextModel::Point m_editingPoint;
     TextModel::EditCommand *m_editingCommand;
-    QColor m_colour;
 };
 
 #endif
--- a/layer/TimeInstantLayer.cpp	Wed Jul 11 20:46:37 2007 +0000
+++ b/layer/TimeInstantLayer.cpp	Thu Jul 12 16:14:59 2007 +0000
@@ -20,6 +20,7 @@
 #include "view/View.h"
 #include "base/Profiler.h"
 #include "base/Clipboard.h"
+#include "base/ColourDatabase.h"
 
 #include "data/model/SparseOneDimensionalModel.h"
 
@@ -32,12 +33,11 @@
 #include <cmath>
 
 TimeInstantLayer::TimeInstantLayer() :
-    Layer(),
+    SingleColourLayer(),
     m_model(0),
     m_editing(false),
     m_editingPoint(0, tr("New Point")),
     m_editingCommand(0),
-    m_colour(QColor(200, 50, 255)),
     m_plotStyle(PlotInstants)
 {
     
@@ -64,8 +64,7 @@
 Layer::PropertyList
 TimeInstantLayer::getProperties() const
 {
-    PropertyList list;
-    list.push_back("Colour");
+    PropertyList list = SingleColourLayer::getProperties();
     list.push_back("Plot Type");
     return list;
 }
@@ -73,15 +72,15 @@
 QString
 TimeInstantLayer::getPropertyLabel(const PropertyName &name) const
 {
-    if (name == "Colour") return tr("Colour");
     if (name == "Plot Type") return tr("Plot Type");
-    return "";
+    return SingleColourLayer::getPropertyLabel(name);
 }
 
 Layer::PropertyType
-TimeInstantLayer::getPropertyType(const PropertyName &) const
+TimeInstantLayer::getPropertyType(const PropertyName &name) const
 {
-    return ValueProperty;
+    if (name == "Plot Type") return ValueProperty;
+    return SingleColourLayer::getPropertyType(name);
 }
 
 int
@@ -90,20 +89,7 @@
 {
     int val = 0;
 
-    if (name == "Colour") {
-
-	if (min) *min = 0;
-	if (max) *max = 5;
-        if (deflt) *deflt = 0;
-
-	if (m_colour == Qt::black) val = 0;
-	else if (m_colour == Qt::darkRed) val = 1;
-	else if (m_colour == Qt::darkBlue) val = 2;
-	else if (m_colour == Qt::darkGreen) val = 3;
-	else if (m_colour == QColor(200, 50, 255)) val = 4;
-	else if (m_colour == QColor(255, 150, 50)) val = 5;
-
-    } else if (name == "Plot Type") {
+    if (name == "Plot Type") {
 	
 	if (min) *min = 0;
 	if (max) *max = 1;
@@ -113,7 +99,7 @@
 
     } else {
 	
-	val = Layer::getPropertyRangeAndValue(name, min, max, deflt);
+	val = SingleColourLayer::getPropertyRangeAndValue(name, min, max, deflt);
     }
 
     return val;
@@ -121,55 +107,29 @@
 
 QString
 TimeInstantLayer::getPropertyValueLabel(const PropertyName &name,
-				    int value) const
+                                        int value) const
 {
-    if (name == "Colour") {
-	switch (value) {
-	default:
-	case 0: return tr("Black");
-	case 1: return tr("Red");
-	case 2: return tr("Blue");
-	case 3: return tr("Green");
-	case 4: return tr("Purple");
-	case 5: return tr("Orange");
-	}
-    } else if (name == "Plot Type") {
+    if (name == "Plot Type") {
 	switch (value) {
 	default:
 	case 0: return tr("Instants");
 	case 1: return tr("Segmentation");
 	}
     }
-    return tr("<unknown>");
+    return SingleColourLayer::getPropertyValueLabel(name, value);
 }
 
 void
 TimeInstantLayer::setProperty(const PropertyName &name, int value)
 {
-    if (name == "Colour") {
-	switch (value) {
-	default:
-	case 0:	setBaseColour(Qt::black); break;
-	case 1: setBaseColour(Qt::darkRed); break;
-	case 2: setBaseColour(Qt::darkBlue); break;
-	case 3: setBaseColour(Qt::darkGreen); break;
-	case 4: setBaseColour(QColor(200, 50, 255)); break;
-	case 5: setBaseColour(QColor(255, 150, 50)); break;
-	}
-    } else if (name == "Plot Type") {
+    if (name == "Plot Type") {
 	setPlotStyle(PlotStyle(value));
+    } else {
+        SingleColourLayer::setProperty(name, value);
     }
 }
 
 void
-TimeInstantLayer::setBaseColour(QColor colour)
-{
-    if (m_colour == colour) return;
-    m_colour = colour;
-    emit layerParametersChanged();
-}
-
-void
 TimeInstantLayer::setPlotStyle(PlotStyle style)
 {
     if (m_plotStyle == style) return;
@@ -360,21 +320,21 @@
 	odd = ((index % 2) == 1);
     }
 
-    paint.setPen(m_colour);
+    paint.setPen(getBaseQColor());
 
-    QColor brushColour(m_colour);
+    QColor brushColour(getBaseQColor());
     brushColour.setAlpha(100);
     paint.setBrush(brushColour);
 
     QColor oddBrushColour(brushColour);
     if (m_plotStyle == PlotSegmentation) {
-	if (m_colour == Qt::black) {
+	if (getBaseQColor() == Qt::black) {
 	    oddBrushColour = Qt::gray;
-	} else if (m_colour == Qt::darkRed) {
+	} else if (getBaseQColor() == Qt::darkRed) {
 	    oddBrushColour = Qt::red;
-	} else if (m_colour == Qt::darkBlue) {
+	} else if (getBaseQColor() == Qt::darkBlue) {
 	    oddBrushColour = Qt::blue;
-	} else if (m_colour == Qt::darkGreen) {
+	} else if (getBaseQColor() == Qt::darkGreen) {
 	    oddBrushColour = Qt::green;
 	} else {
 	    oddBrushColour = oddBrushColour.light(150);
@@ -421,7 +381,7 @@
 	}
 		
 	if (p.frame == illuminateFrame) {
-	    paint.setPen(Qt::black); //!!!
+	    paint.setPen(getForegroundQColor(v));
 	} else {
 	    paint.setPen(brushColour);
 	}
@@ -459,7 +419,7 @@
 	    odd = !odd;
 	}
 
-	paint.setPen(m_colour);
+	paint.setPen(getBaseQColor());
 	
 	if (p.label != "") {
 
@@ -756,24 +716,26 @@
     return true;
 }
 
+int
+TimeInstantLayer::getDefaultColourHint(bool darkbg, bool &impose)
+{
+    impose = false;
+    return ColourDatabase::getInstance()->getColourIndex
+        (QString(darkbg ? "Bright Purple" : "Purple"));
+}
+
 QString
 TimeInstantLayer::toXmlString(QString indent, QString extraAttributes) const
 {
-    return Layer::toXmlString(indent, extraAttributes +
-			      QString(" colour=\"%1\" plotStyle=\"%2\"")
-			      .arg(encodeColour(m_colour)).arg(m_plotStyle));
+    return SingleColourLayer::toXmlString(indent, extraAttributes +
+                                          QString(" plotStyle=\"%1\"")
+                                          .arg(m_plotStyle));
 }
 
 void
 TimeInstantLayer::setProperties(const QXmlAttributes &attributes)
 {
-    QString colourSpec = attributes.value("colour");
-    if (colourSpec != "") {
-	QColor colour(colourSpec);
-	if (colour.isValid()) {
-	    setBaseColour(QColor(colourSpec));
-	}
-    }
+    SingleColourLayer::setProperties(attributes);
 
     bool ok;
     PlotStyle style = (PlotStyle)
--- a/layer/TimeInstantLayer.h	Wed Jul 11 20:46:37 2007 +0000
+++ b/layer/TimeInstantLayer.h	Thu Jul 12 16:14:59 2007 +0000
@@ -16,7 +16,7 @@
 #ifndef _TIME_INSTANT_LAYER_H_
 #define _TIME_INSTANT_LAYER_H_
 
-#include "Layer.h"
+#include "SingleColourLayer.h"
 #include "data/model/SparseOneDimensionalModel.h"
 
 #include <QObject>
@@ -25,7 +25,7 @@
 class View;
 class QPainter;
 
-class TimeInstantLayer : public Layer
+class TimeInstantLayer : public SingleColourLayer
 {
     Q_OBJECT
 
@@ -70,9 +70,6 @@
 					  int value) const;
     virtual void setProperty(const PropertyName &, int value);
 
-    void setBaseColour(QColor);
-    QColor getBaseColour() const { return m_colour; }
-
     enum PlotStyle {
 	PlotInstants,
 	PlotSegmentation
@@ -101,11 +98,12 @@
 protected:
     SparseOneDimensionalModel::PointList getLocalPoints(View *v, int) const;
 
+    virtual int getDefaultColourHint(bool dark, bool &impose);
+
     SparseOneDimensionalModel *m_model;
     bool m_editing;
     SparseOneDimensionalModel::Point m_editingPoint;
     SparseOneDimensionalModel::EditCommand *m_editingCommand;
-    QColor m_colour;
     PlotStyle m_plotStyle;
 };
 
--- a/layer/TimeRulerLayer.cpp	Wed Jul 11 20:46:37 2007 +0000
+++ b/layer/TimeRulerLayer.cpp	Thu Jul 12 16:14:59 2007 +0000
@@ -17,6 +17,7 @@
 
 #include "data/model/Model.h"
 #include "base/RealTime.h"
+#include "base/ColourDatabase.h"
 #include "view/View.h"
 
 #include <QPainter>
@@ -28,9 +29,8 @@
 using std::endl;
 
 TimeRulerLayer::TimeRulerLayer() :
-    Layer(),
+    SingleColourLayer(),
     m_model(0),
-    m_colour(Qt::black),
     m_labelHeight(LabelTop)
 {
     
@@ -44,96 +44,6 @@
     emit modelReplaced();
 }
 
-void
-TimeRulerLayer::setBaseColour(QColor colour)
-{
-    if (m_colour == colour) return;
-    m_colour = colour;
-    emit layerParametersChanged();
-}
-
-Layer::PropertyList
-TimeRulerLayer::getProperties() const
-{
-    PropertyList list;
-    list.push_back("Colour");
-    return list;
-}
-
-QString
-TimeRulerLayer::getPropertyLabel(const PropertyName &name) const
-{
-    if (name == "Colour") return tr("Colour");
-    return "";
-}
-
-Layer::PropertyType
-TimeRulerLayer::getPropertyType(const PropertyName &) const
-{
-    return ValueProperty;
-}
-
-int
-TimeRulerLayer::getPropertyRangeAndValue(const PropertyName &name,
-					 int *min, int *max, int *deflt) const
-{
-    int val = 0;
-
-    if (name == "Colour") {
-
-	if (min) *min = 0;
-	if (max) *max = 5;
-        if (deflt) *deflt = 0;
-
-	if (m_colour == Qt::black) val = 0;
-	else if (m_colour == Qt::darkRed) val = 1;
-	else if (m_colour == Qt::darkBlue) val = 2;
-	else if (m_colour == Qt::darkGreen) val = 3;
-	else if (m_colour == QColor(200, 50, 255)) val = 4;
-	else if (m_colour == QColor(255, 150, 50)) val = 5;
-
-    } else {
-	
-	val = Layer::getPropertyRangeAndValue(name, min, max, deflt);
-    }
-
-    return val;
-}
-
-QString
-TimeRulerLayer::getPropertyValueLabel(const PropertyName &name,
-				    int value) const
-{
-    if (name == "Colour") {
-	switch (value) {
-	default:
-	case 0: return tr("Black");
-	case 1: return tr("Red");
-	case 2: return tr("Blue");
-	case 3: return tr("Green");
-	case 4: return tr("Purple");
-	case 5: return tr("Orange");
-	}
-    }
-    return tr("<unknown>");
-}
-
-void
-TimeRulerLayer::setProperty(const PropertyName &name, int value)
-{
-    if (name == "Colour") {
-	switch (value) {
-	default:
-	case 0:	setBaseColour(Qt::black); break;
-	case 1: setBaseColour(Qt::darkRed); break;
-	case 2: setBaseColour(Qt::darkBlue); break;
-	case 3: setBaseColour(Qt::darkGreen); break;
-	case 4: setBaseColour(QColor(200, 50, 255)); break;
-	case 5: setBaseColour(QColor(255, 150, 50)); break;
-	}
-    }
-}
-
 bool
 TimeRulerLayer::snapToFeatureFrame(View *v, int &frame,
                                    size_t &resolution, SnapType snap) const
@@ -323,12 +233,7 @@
     paint.setClipRect(newClipRect);
     paint.setClipRect(rect);
 
-    QColor greyColour(m_colour);
-    if (m_colour == Qt::black) {
-	greyColour = QColor(200,200,200);
-    } else {
-	greyColour = m_colour.light(150);
-    }
+    QColor greyColour = getPartialShades(v)[1];
 
     while (1) {
 
@@ -344,7 +249,7 @@
 	paint.setPen(greyColour);
 	paint.drawLine(x, 0, x, v->height());
 
-	paint.setPen(m_colour);
+	paint.setPen(getBaseQColor());
 	paint.drawLine(x, 0, x, 5);
 	paint.drawLine(x, v->height() - 6, x, v->height() - 1);
 
@@ -400,23 +305,24 @@
 
     paint.restore();
 }
-    
+
+int
+TimeRulerLayer::getDefaultColourHint(bool darkbg, bool &impose)
+{
+    impose = true;
+    return ColourDatabase::getInstance()->getColourIndex
+        (QString(darkbg ? "White" : "Black"));
+}
+
 QString
 TimeRulerLayer::toXmlString(QString indent, QString extraAttributes) const
 {
-    return Layer::toXmlString(indent, extraAttributes +
-			      QString(" colour=\"%1\"").arg(encodeColour(m_colour)));
+    return SingleColourLayer::toXmlString(indent, extraAttributes);
 }
 
 void
 TimeRulerLayer::setProperties(const QXmlAttributes &attributes)
 {
-    QString colourSpec = attributes.value("colour");
-    if (colourSpec != "") {
-	QColor colour(colourSpec);
-	if (colour.isValid()) {
-	    setBaseColour(QColor(colourSpec));
-	}
-    }
+    SingleColourLayer::setProperties(attributes);
 }
 
--- a/layer/TimeRulerLayer.h	Wed Jul 11 20:46:37 2007 +0000
+++ b/layer/TimeRulerLayer.h	Thu Jul 12 16:14:59 2007 +0000
@@ -16,7 +16,7 @@
 #ifndef _TIME_RULER_H_
 #define _TIME_RULER_H_
 
-#include "Layer.h"
+#include "SingleColourLayer.h"
 
 #include <QRect>
 #include <QColor>
@@ -25,7 +25,7 @@
 class Model;
 class QPainter;
 
-class TimeRulerLayer : public Layer
+class TimeRulerLayer : public SingleColourLayer
 {
     Q_OBJECT
 
@@ -37,23 +37,15 @@
     void setModel(Model *);
     virtual const Model *getModel() const { return m_model; }
 
-    void setBaseColour(QColor);
-    QColor getBaseColour() const { return m_colour; }
-
     enum LabelHeight { LabelTop, LabelMiddle, LabelBottom };
     void setLabelHeight(LabelHeight h) { m_labelHeight = h; }
     LabelHeight getLabelHeight() const { return m_labelHeight; }
 
     virtual bool snapToFeatureFrame(View *, int &, size_t &, SnapType) const;
 
-    virtual PropertyList getProperties() const;
-    virtual QString getPropertyLabel(const PropertyName &) const;
-    virtual PropertyType getPropertyType(const PropertyName &) const;
-    virtual int getPropertyRangeAndValue(const PropertyName &,
-                                         int *min, int *max, int *deflt) const;
-    virtual QString getPropertyValueLabel(const PropertyName &,
-					  int value) const;
-    virtual void setProperty(const PropertyName &, int value);
+    virtual ColourSignificance getLayerColourSignificance() const {
+        return ColourIrrelevant;
+    }
 
     virtual bool getValueExtents(float &, float &, bool &, QString &) const {
         return false;
@@ -66,9 +58,10 @@
 
 protected:
     Model *m_model;
-    QColor m_colour;
     LabelHeight m_labelHeight;
 
+    virtual int getDefaultColourHint(bool dark, bool &impose);
+
     int getMajorTickSpacing(View *, bool &quarterTicks) const;
 };
 
--- a/layer/TimeValueLayer.cpp	Wed Jul 11 20:46:37 2007 +0000
+++ b/layer/TimeValueLayer.cpp	Thu Jul 12 16:14:59 2007 +0000
@@ -19,6 +19,7 @@
 #include "base/RealTime.h"
 #include "base/Profiler.h"
 #include "base/LogRange.h"
+#include "base/ColourDatabase.h"
 #include "view/View.h"
 
 #include "data/model/SparseTimeValueModel.h"
@@ -38,13 +39,12 @@
 #include <cmath>
 
 TimeValueLayer::TimeValueLayer() :
-    Layer(),
+    SingleColourLayer(),
     m_model(0),
     m_editing(false),
     m_originalPoint(0, 0.0, tr("New Point")),
     m_editingPoint(0, 0.0, tr("New Point")),
     m_editingCommand(0),
-    m_colour(Qt::darkGreen),
     m_colourMap(0),
     m_plotStyle(PlotConnectedPoints),
     m_verticalScale(AutoAlignScale)
@@ -73,8 +73,7 @@
 Layer::PropertyList
 TimeValueLayer::getProperties() const
 {
-    PropertyList list;
-    list.push_back("Colour");
+    PropertyList list = SingleColourLayer::getProperties();
     list.push_back("Plot Type");
     list.push_back("Vertical Scale");
     list.push_back("Scale Units");
@@ -84,18 +83,20 @@
 QString
 TimeValueLayer::getPropertyLabel(const PropertyName &name) const
 {
-    if (name == "Colour") return tr("Colour");
     if (name == "Plot Type") return tr("Plot Type");
     if (name == "Vertical Scale") return tr("Vertical Scale");
     if (name == "Scale Units") return tr("Scale Units");
-    return "";
+    return SingleColourLayer::getPropertyLabel(name);
 }
 
 Layer::PropertyType
 TimeValueLayer::getPropertyType(const PropertyName &name) const
 {
+    if (name == "Plot Type") return ValueProperty;
+    if (name == "Vertical Scale") return ValueProperty;
     if (name == "Scale Units") return UnitsProperty;
-    else return ValueProperty;
+    if (name == "Colour" && m_plotStyle == PlotSegmentation) return ValueProperty;
+    return SingleColourLayer::getPropertyType(name);
 }
 
 QString
@@ -104,40 +105,22 @@
     if (name == "Vertical Scale" || name == "Scale Units") {
         return tr("Scale");
     }
-    return QString();
+    return SingleColourLayer::getPropertyGroupName(name);
 }
 
 int
 TimeValueLayer::getPropertyRangeAndValue(const PropertyName &name,
 					 int *min, int *max, int *deflt) const
 {
-    //!!! factor this colour handling stuff out into a colour manager class
-
     int val = 0;
 
-    if (name == "Colour") {
-
-        if (m_plotStyle == PlotSegmentation) {
+    if (name == "Colour" && m_plotStyle == PlotSegmentation) {
             
-            if (min) *min = 0;
-            if (max) *max = ColourMapper::getColourMapCount() - 1;
-            if (deflt) *deflt = 0;
-
-            val = m_colourMap;
+        if (min) *min = 0;
+        if (max) *max = ColourMapper::getColourMapCount() - 1;
+        if (deflt) *deflt = 0;
         
-        } else {
-
-            if (min) *min = 0;
-            if (max) *max = 5;
-            if (deflt) *deflt = 0;
-
-            if (m_colour == Qt::black) val = 0;
-            else if (m_colour == Qt::darkRed) val = 1;
-            else if (m_colour == Qt::darkBlue) val = 2;
-            else if (m_colour == Qt::darkGreen) val = 3;
-            else if (m_colour == QColor(200, 50, 255)) val = 4;
-            else if (m_colour == QColor(255, 150, 50)) val = 5;
-        }
+        val = m_colourMap;
 
     } else if (name == "Plot Type") {
 	
@@ -165,7 +148,7 @@
 
     } else {
 	
-	val = Layer::getPropertyRangeAndValue(name, min, max, deflt);
+	val = SingleColourLayer::getPropertyRangeAndValue(name, min, max, deflt);
     }
 
     return val;
@@ -175,20 +158,8 @@
 TimeValueLayer::getPropertyValueLabel(const PropertyName &name,
 				    int value) const
 {
-    if (name == "Colour") {
-        if (m_plotStyle == PlotSegmentation) {
-            return ColourMapper::getColourMapName(value);
-        } else {
-            switch (value) {
-            default:
-            case 0: return tr("Black");
-            case 1: return tr("Red");
-            case 2: return tr("Blue");
-            case 3: return tr("Green");
-            case 4: return tr("Purple");
-            case 5: return tr("Orange");
-            }
-        }
+    if (name == "Colour" && m_plotStyle == PlotSegmentation) {
+        return ColourMapper::getColourMapName(value);
     } else if (name == "Plot Type") {
 	switch (value) {
 	default:
@@ -208,26 +179,14 @@
 	case 3: return tr("+/-1");
 	}
     }
-    return tr("<unknown>");
+    return SingleColourLayer::getPropertyValueLabel(name, value);
 }
 
 void
 TimeValueLayer::setProperty(const PropertyName &name, int value)
 {
-    if (name == "Colour") {
-        if (m_plotStyle == PlotSegmentation) {
-            setFillColourMap(value);
-        } else {
-            switch (value) {
-            default:
-            case 0:	setBaseColour(Qt::black); break;
-            case 1: setBaseColour(Qt::darkRed); break;
-            case 2: setBaseColour(Qt::darkBlue); break;
-            case 3: setBaseColour(Qt::darkGreen); break;
-            case 4: setBaseColour(QColor(200, 50, 255)); break;
-            case 5: setBaseColour(QColor(255, 150, 50)); break;
-            }
-        }
+    if (name == "Colour" && m_plotStyle == PlotSegmentation) {
+        setFillColourMap(value);
     } else if (name == "Plot Type") {
 	setPlotStyle(PlotStyle(value));
     } else if (name == "Vertical Scale") {
@@ -238,18 +197,12 @@
                 (UnitDatabase::getInstance()->getUnitById(value));
             emit modelChanged();
         }
+    } else {
+        SingleColourLayer::setProperty(name, value);
     }
 }
 
 void
-TimeValueLayer::setBaseColour(QColor colour)
-{
-    if (m_colour == colour) return;
-    m_colour = colour;
-    emit layerParametersChanged();
-}
-
-void
 TimeValueLayer::setFillColourMap(int map)
 {
     if (m_colourMap == map) return;
@@ -565,6 +518,14 @@
     return QColor(solid.red(), solid.green(), solid.blue(), 120);
 }
 
+int
+TimeValueLayer::getDefaultColourHint(bool darkbg, bool &impose)
+{
+    impose = false;
+    return ColourDatabase::getInstance()->getColourIndex
+        (QString(darkbg ? "Bright Green" : "Green"));
+}
+
 void
 TimeValueLayer::paint(View *v, QPainter &paint, QRect rect) const
 {
@@ -583,9 +544,9 @@
 					   (frame0, frame1));
     if (points.empty()) return;
 
-    paint.setPen(m_colour);
+    paint.setPen(getBaseQColor());
 
-    QColor brushColour(m_colour);
+    QColor brushColour(getBaseQColor());
     brushColour.setAlpha(80);
     paint.setBrush(brushColour);
 
@@ -656,10 +617,10 @@
 	int labelY = y;
 
 	if (w < 1) w = 1;
-	paint.setPen(m_colour);
+	paint.setPen(getBaseQColor());
 
 	if (m_plotStyle == PlotSegmentation) {
-            paint.setPen(Qt::black);
+            paint.setPen(getForegroundQColor(v));
             paint.setBrush(getColourForValue(v, p.value));
 	    labelY = v->height();
 	} else if (m_plotStyle == PlotLines ||
@@ -676,7 +637,7 @@
 	    } else if (y > origin + 1) {
 		paint.drawRect(x + w/2, origin, 1, y - origin - 1);
 	    }
-	    paint.setPen(m_colour);
+	    paint.setPen(getBaseQColor());
 	}
 
 	if (illuminateFrame == p.frame) {
@@ -690,10 +651,7 @@
 
 	    if (m_plotStyle != PlotCurve &&
 		m_plotStyle != PlotLines) {
-		paint.setPen(Qt::black);//!!!
-		if (m_plotStyle != PlotSegmentation) {
-		    paint.setBrush(Qt::black);//!!!
-		}
+		paint.setPen(getForegroundQColor(v));
 	    }	    
 	}
 
@@ -1423,24 +1381,23 @@
 QString
 TimeValueLayer::toXmlString(QString indent, QString extraAttributes) const
 {
-    return Layer::toXmlString(indent, extraAttributes +
-			      QString(" colour=\"%1\" plotStyle=\"%2\" verticalScale=\"%3\"")
-			      .arg(encodeColour(m_colour)).arg(m_plotStyle)
-                              .arg(m_verticalScale));
+    return SingleColourLayer::toXmlString(indent, extraAttributes +
+                                          QString(" colourMap=\"%1\" plotStyle=\"%2\" verticalScale=\"%3\"")
+                                          .arg(m_colourMap)
+                                          .arg(m_plotStyle)
+                                          .arg(m_verticalScale));
 }
 
 void
 TimeValueLayer::setProperties(const QXmlAttributes &attributes)
 {
-    QString colourSpec = attributes.value("colour");
-    if (colourSpec != "") {
-	QColor colour(colourSpec);
-	if (colour.isValid()) {
-	    setBaseColour(QColor(colourSpec));
-	}
-    }
+    SingleColourLayer::setProperties(attributes);
 
     bool ok;
+
+    int cmap = attributes.value("colourMap").toInt(&ok);
+    if (ok) setFillColourMap(cmap);
+
     PlotStyle style = (PlotStyle)
 	attributes.value("plotStyle").toInt(&ok);
     if (ok) setPlotStyle(style);
--- a/layer/TimeValueLayer.h	Wed Jul 11 20:46:37 2007 +0000
+++ b/layer/TimeValueLayer.h	Thu Jul 12 16:14:59 2007 +0000
@@ -16,7 +16,7 @@
 #ifndef _TIME_VALUE_LAYER_H_
 #define _TIME_VALUE_LAYER_H_
 
-#include "Layer.h"
+#include "SingleColourLayer.h"
 #include "data/model/SparseTimeValueModel.h"
 
 #include <QObject>
@@ -25,7 +25,7 @@
 class View;
 class QPainter;
 
-class TimeValueLayer : public Layer
+class TimeValueLayer : public SingleColourLayer
 {
     Q_OBJECT
 
@@ -74,9 +74,6 @@
 					  int value) const;
     virtual void setProperty(const PropertyName &, int value);
 
-    void setBaseColour(QColor);
-    QColor getBaseColour() const { return m_colour; }
-
     void setFillColourMap(int);
     int getFillColourMap() const { return m_colourMap; }
 
@@ -130,12 +127,13 @@
 
     SparseTimeValueModel::PointList getLocalPoints(View *v, int) const;
 
+    virtual int getDefaultColourHint(bool dark, bool &impose);
+
     SparseTimeValueModel *m_model;
     bool m_editing;
     SparseTimeValueModel::Point m_originalPoint;
     SparseTimeValueModel::Point m_editingPoint;
     SparseTimeValueModel::EditCommand *m_editingCommand;
-    QColor m_colour;
     int m_colourMap;
     PlotStyle m_plotStyle;
     VerticalScale m_verticalScale;
--- a/layer/WaveformLayer.cpp	Wed Jul 11 20:46:37 2007 +0000
+++ b/layer/WaveformLayer.cpp	Thu Jul 12 16:14:59 2007 +0000
@@ -33,13 +33,10 @@
 using std::endl;
 
 WaveformLayer::WaveformLayer() :
-    Layer(),
+    SingleColourLayer(),
     m_model(0),
     m_gain(1.0f),
     m_autoNormalize(false),
-//!!!    m_colour(Qt::black),
-    m_colour(0),
-//    m_colour(QColor(84, 177, 248)),
     m_showMeans(true),
     m_greyscale(true),
     m_channelMode(SeparateChannels),
@@ -90,18 +87,10 @@
     if (channelsChanged) emit layerParametersChanged();
 }
 
-bool
-WaveformLayer::hasLightBackground() const
-{
-    bool dark = ColourDatabase::getInstance()->useDarkBackground(m_colour);
-    return !dark;
-}
-
 Layer::PropertyList
 WaveformLayer::getProperties() const
 {
-    PropertyList list;
-    list.push_back("Colour");
+    PropertyList list = SingleColourLayer::getProperties();
     list.push_back("Scale");
     list.push_back("Gain");
     list.push_back("Normalize Visible Area");
@@ -116,12 +105,11 @@
 QString
 WaveformLayer::getPropertyLabel(const PropertyName &name) const
 {
-    if (name == "Colour") return tr("Colour");
     if (name == "Scale") return tr("Scale");
     if (name == "Gain") return tr("Gain");
     if (name == "Normalize Visible Area") return tr("Normalize Visible Area");
     if (name == "Channels") return tr("Channels");
-    return "";
+    return SingleColourLayer::getPropertyLabel(name);
 }
 
 Layer::PropertyType
@@ -129,10 +117,9 @@
 {
     if (name == "Gain") return RangeProperty;
     if (name == "Normalize Visible Area") return ToggleProperty;
-    if (name == "Colour") return ColourProperty;
     if (name == "Channels") return ValueProperty;
     if (name == "Scale") return ValueProperty;
-    return InvalidProperty;
+    return SingleColourLayer::getPropertyType(name);
 }
 
 QString
@@ -170,24 +157,6 @@
         val = (m_autoNormalize ? 1 : 0);
         *deflt = 0;
 
-    } else if (name == "Colour") {
-
-        ColourDatabase::getInstance()->getColourPropertyRange(min, max);
-//!!!	*min = 0;
-//	*max = 5;
-        *deflt = 0;
-
-        val = m_colour;
-
-/*!!!
-	if (m_colour == Qt::black) val = 0;
-	else if (m_colour == Qt::darkRed) val = 1;
-	else if (m_colour == Qt::darkBlue ||
-                 m_colour == QColor(84, 177, 248)) val = 2;
-	else if (m_colour == Qt::darkGreen) val = 3;
-	else if (m_colour == QColor(200, 50, 255)) val = 4;
-	else if (m_colour == QColor(255, 150, 50)) val = 5;
-*/
     } else if (name == "Channels") {
 
         *min = 0;
@@ -206,7 +175,7 @@
 	val = (int)m_scale;
 
     } else {
-	val = Layer::getPropertyRangeAndValue(name, min, max, deflt);
+	val = SingleColourLayer::getPropertyRangeAndValue(name, min, max, deflt);
     }
 
     return val;
@@ -216,9 +185,6 @@
 WaveformLayer::getPropertyValueLabel(const PropertyName &name,
 				    int value) const
 {
-    if (name == "Colour") {
-        return Layer::getPropertyValueLabel(name, value);
-    }
     if (name == "Scale") {
 	switch (value) {
 	default:
@@ -235,7 +201,7 @@
         case 2: return tr("Butterfly");
         }
     }
-    return tr("<unknown>");
+    return SingleColourLayer::getPropertyValueLabel(name, value);
 }
 
 RangeMapper *
@@ -254,8 +220,6 @@
 	setGain(pow(10, float(value)/20.0));
     } else if (name == "Normalize Visible Area") {
         setAutoNormalize(value ? true : false);
-    } else if (name == "Colour") {
-        setBaseColour(value);
     } else if (name == "Channels") {
         if (value == 1) setChannelMode(MixChannels);
         else if (value == 2) setChannelMode(MergeChannels);
@@ -267,6 +231,8 @@
 	case 1: setScale(MeterScale); break;
 	case 2: setScale(dBScale); break;
 	}
+    } else {
+        SingleColourLayer::setProperty(name, value);
     }
 }
 
@@ -290,15 +256,6 @@
 }
 
 void
-WaveformLayer::setBaseColour(int colour)
-{
-    if (m_colour == colour) return;
-    m_colour = colour;
-    m_cacheValid = false;
-    emit layerParametersChanged();
-}
-
-void
 WaveformLayer::setShowMeans(bool showMeans)
 {
     if (m_showMeans == showMeans) return;
@@ -494,10 +451,10 @@
 	paint = new QPainter(m_cache);
 
 	paint->setPen(Qt::NoPen);
-	paint->setBrush(v->palette().background());
+	paint->setBrush(getBackgroundQColor(v));
 	paint->drawRect(rect);
 
-	paint->setPen(Qt::black);
+	paint->setPen(getForegroundQColor(v));
 	paint->setBrush(Qt::NoBrush);
 
     } else {
@@ -529,25 +486,9 @@
 
     RangeSummarisableTimeValueModel::RangeBlock *otherChannelRanges = 0;
     RangeSummarisableTimeValueModel::Range range;
-    
-    QColor greys[3];
-    QColor baseColour = ColourDatabase::getInstance()->getColour(m_colour);
-    if (baseColour == Qt::black) {
-	for (int i = 0; i < 3; ++i) { // 0 lightest, 2 darkest
-	    int level = 192 - 64 * i;
-	    greys[i] = QColor(level, level, level);
-	}
-    } else {
-	int hue, sat, val;
-	baseColour.getHsv(&hue, &sat, &val);
-	for (int i = 0; i < 3; ++i) { // 0 lightest, 2 darkest
-	    if (v->hasLightBackground()) {
-		greys[i] = QColor::fromHsv(hue, sat * (i + 1) / 4, val);
-	    } else {
-		greys[i] = QColor::fromHsv(hue, sat * (3 - i) / 4, val);
-	    }
-	}
-    }
+
+    QColor baseColour = getBaseQColor();
+    std::vector<QColor> greys = getPartialShades(v);
         
     QColor midColour = baseColour;
     if (midColour == Qt::black) {
@@ -1284,30 +1225,23 @@
         (m_colour, colourName, colourSpec, darkbg);
 
     s += QString("gain=\"%1\" "
-		 "colourName=\"%2\" "
-                 "colour=\"%3\" "
-                 "darkBackground=\"%4\" "
-		 "showMeans=\"%5\" "
-		 "greyscale=\"%6\" "
-		 "channelMode=\"%7\" "
-		 "channel=\"%8\" ")
+		 "showMeans=\"%2\" "
+		 "greyscale=\"%3\" "
+		 "channelMode=\"%4\" "
+		 "channel=\"%5\" "
+                 "scale=\"%6\" "
+		 "aggressive=\"%7\" "
+                 "autoNormalize=\"%8\"")
 	.arg(m_gain)
-	.arg(colourName)
-        .arg(colourSpec)
-        .arg(darkbg)
 	.arg(m_showMeans)
 	.arg(m_greyscale)
 	.arg(m_channelMode)
-	.arg(m_channel);
-
-    s += QString("scale=\"%1\" "
-		 "aggressive=\"%2\" "
-                 "autoNormalize=\"%3\"")
+	.arg(m_channel)
 	.arg(m_scale)
 	.arg(m_aggressive)
         .arg(m_autoNormalize);
 
-    return Layer::toXmlString(indent, extraAttributes + " " + s);
+    return SingleColourLayer::toXmlString(indent, extraAttributes + " " + s);
 }
 
 void
@@ -1315,15 +1249,11 @@
 {
     bool ok = false;
 
+    SingleColourLayer::setProperties(attributes);
+
     float gain = attributes.value("gain").toFloat(&ok);
     if (ok) setGain(gain);
 
-    QString colourName = attributes.value("colourName");
-    QString colourSpec = attributes.value("colour");
-    QString darkbg = attributes.value("darkBackground");
-    m_colour = ColourDatabase::getInstance()->putStringValues
-        (colourName, colourSpec, darkbg);
-
     bool showMeans = (attributes.value("showMeans") == "1" ||
 		      attributes.value("showMeans") == "true");
     setShowMeans(showMeans);
--- a/layer/WaveformLayer.h	Wed Jul 11 20:46:37 2007 +0000
+++ b/layer/WaveformLayer.h	Thu Jul 12 16:14:59 2007 +0000
@@ -17,9 +17,8 @@
 #define _WAVEFORM_LAYER_H_
 
 #include <QRect>
-#include <QColor>
 
-#include "Layer.h"
+#include "SingleColourLayer.h"
 
 #include "data/model/RangeSummarisableTimeValueModel.h"
 
@@ -27,7 +26,7 @@
 class QPainter;
 class QPixmap;
 
-class WaveformLayer : public Layer
+class WaveformLayer : public SingleColourLayer
 {
     Q_OBJECT
 
@@ -43,11 +42,13 @@
 
     virtual QString getFeatureDescription(View *v, QPoint &) const;
 
+    virtual ColourSignificance getLayerColourSignificance() const {
+        return ColourAndBackgroundSignificant;
+    }
+
     virtual int getVerticalScaleWidth(View *v, QPainter &) const;
     virtual void paintVerticalScale(View *v, QPainter &paint, QRect rect) const;
 
-    virtual bool hasLightBackground() const;
-
     void setModel(const RangeSummarisableTimeValueModel *model);
 
     virtual PropertyList getProperties() const;
@@ -76,16 +77,6 @@
     bool getAutoNormalize() const { return m_autoNormalize; }
 
     /**
-     * Set the basic display colour for waveforms.  The parameter is
-     * a ColourDatabase index.
-     *
-     * The default is the first colour in the database.
-     *!!! NB should default to white if the associated View !hasLightBackground()
-     */
-    void setBaseColour(int);
-    int getBaseColour() const;
-
-    /**
      * Set whether to display mean values as a lighter-coloured area
      * beneath the peaks.  Rendering will be slightly faster without
      * but arguably prettier with.
@@ -191,7 +182,7 @@
     virtual QString toXmlString(QString indent = "",
 				QString extraAttributes = "") const;
 
-    void setProperties(const QXmlAttributes &attributes);
+    virtual void setProperties(const QXmlAttributes &attributes);
 
     virtual int getVerticalZoomSteps(int &defaultStep) const;
     virtual int getCurrentVerticalZoomStep() const;
@@ -210,9 +201,10 @@
 
     float getValueForY(const View *v, int y, size_t &channel) const;
 
+    virtual void flagBaseColourChanged() { m_cacheValid = false; }
+
     float        m_gain;
     bool         m_autoNormalize;
-    int          m_colour;
     bool         m_showMeans;
     bool         m_greyscale;
     ChannelMode  m_channelMode;
--- a/layer/layer.pro	Wed Jul 11 20:46:37 2007 +0000
+++ b/layer/layer.pro	Thu Jul 12 16:14:59 2007 +0000
@@ -20,6 +20,7 @@
            LayerFactory.h \
            NoteLayer.h \
            PaintAssistant.h \
+           SingleColourLayer.h \
            SliceableLayer.h \
            SliceLayer.h \
            SpectrogramLayer.h \
@@ -35,6 +36,7 @@
            LayerFactory.cpp \
            NoteLayer.cpp \
            PaintAssistant.cpp \
+           SingleColourLayer.cpp \
            SliceLayer.cpp \
            SpectrogramLayer.cpp \
            SpectrumLayer.cpp \
--- a/view/Overview.cpp	Wed Jul 11 20:46:37 2007 +0000
+++ b/view/Overview.cpp	Thu Jul 12 16:14:59 2007 +0000
@@ -164,7 +164,7 @@
 	paint.setClipRect(r);
     }
 
-    paint.setPen(Qt::black);
+    paint.setPen(getForeground());
 
     int y = 0;
 
--- a/view/Pane.cpp	Wed Jul 11 20:46:37 2007 +0000
+++ b/view/Pane.cpp	Thu Jul 12 16:14:59 2007 +0000
@@ -568,8 +568,8 @@
 //	    std::cerr << "Pane::paintEvent: calling paint.save() in vertical scale block" << std::endl;
         paint.save();
             
-        paint.setPen(Qt::black);
-        paint.setBrush(Qt::white);
+        paint.setPen(getForeground());
+        paint.setBrush(getBackground());
         paint.drawRect(0, -1, m_scaleWidth, height()+1);
         
         paint.setBrush(Qt::NoBrush);
@@ -750,7 +750,7 @@
         for (size_t i = 0; i < texts.size(); ++i) {
             
             if (i + 1 == texts.size()) {
-                paint.setPen(Qt::black);
+                paint.setPen(getForeground());
             }
             
             drawVisibleText(paint, llx,
@@ -776,11 +776,7 @@
     }
     
     paint.save();
-    if (hasLightBackground()) {
-        paint.setPen(QPen(Qt::black, 2));
-    } else {
-        paint.setPen(QPen(Qt::white, 2));
-    }
+    paint.setPen(QPen(getForeground(), 2));
     
     //!!! duplicating display policy with View::drawSelections
     
@@ -856,8 +852,8 @@
             
             paint.save();
             
-            paint.setPen(Qt::black);
-            paint.setBrush(Qt::white);
+            paint.setPen(getForeground());
+            paint.setBrush(getBackground());
             paint.drawRect(xorigin, -1, m_scaleWidth, height()+1);
             
             paint.setBrush(Qt::NoBrush);
--- a/view/View.cpp	Wed Jul 11 20:46:37 2007 +0000
+++ b/view/View.cpp	Thu Jul 12 16:14:59 2007 +0000
@@ -21,6 +21,7 @@
 #include "base/Pitch.h"
 
 #include "layer/TimeRulerLayer.h" //!!! damn, shouldn't be including that here
+#include "layer/SingleColourLayer.h"
 #include "data/model/PowerOfSqrtTwoZoomConstraint.h" //!!! likewise
 
 #include <QPainter>
@@ -434,11 +435,63 @@
 bool
 View::hasLightBackground() const
 {
+    bool darkPalette = false;
+
+    QColor windowBg = palette().color(QPalette::Window);
+    if (windowBg.red() + windowBg.green() + windowBg.blue() < 384) {
+        darkPalette = true;
+    }
+
+    Layer::ColourSignificance maxSignificance = Layer::ColourAbsent;
+    bool mostSignificantHasDarkBackground = false;
+    
     for (LayerList::const_iterator i = m_layers.begin();
          i != m_layers.end(); ++i) {
-        if (!(*i)->hasLightBackground()) return false;
+
+        Layer::ColourSignificance s = (*i)->getLayerColourSignificance();
+        bool light = (*i)->hasLightBackground();
+
+        if (int(s) > int(maxSignificance)) {
+            maxSignificance = s;
+            mostSignificantHasDarkBackground = !light;
+        } else if (s == maxSignificance && !light) {
+            mostSignificantHasDarkBackground = true;
+        }
     }
-    return true;
+
+    if (int(maxSignificance) >= int(Layer::ColourAndBackgroundSignificant)) {
+        return !mostSignificantHasDarkBackground;
+    } else {
+        return !darkPalette;
+    }
+}
+
+QColor
+View::getBackground() const
+{
+    bool light = hasLightBackground();
+
+    QColor widgetbg = palette().window().color();
+    bool widgetLight =
+        (widgetbg.red() + widgetbg.green() + widgetbg.blue()) > 384;
+
+    if (widgetLight == light) return widgetbg;
+    else if (light) return Qt::white;
+    else return Qt::black;
+}
+
+QColor
+View::getForeground() const
+{
+    bool light = hasLightBackground();
+
+    QColor widgetfg = palette().text().color();
+    bool widgetLight =
+        (widgetfg.red() + widgetfg.green() + widgetfg.blue()) > 384;
+
+    if (widgetLight != light) return widgetfg;
+    else if (light) return Qt::black;
+    else return Qt::white;
 }
 
 View::LayerProgressBar::LayerProgressBar(QWidget *parent) :
@@ -455,6 +508,9 @@
     delete m_cache;
     m_cache = 0;
 
+    SingleColourLayer *scl = dynamic_cast<SingleColourLayer *>(layer);
+    if (scl) scl->setDefaultColourFor(this);
+
     m_layers.push_back(layer);
 
     m_progressBars[layer] = new LayerProgressBar(this);
@@ -624,26 +680,9 @@
 
         QColor penColour, surroundColour;
 
-        if (hasLightBackground()) {
-            penColour = Qt::black;
-            surroundColour = Qt::white;
-        } else {
-            penColour = Qt::white;
-            surroundColour = Qt::black;
-        }            
+        penColour = getForeground();
+        surroundColour = getBackground();
 
-/*
-	QColor origPenColour = paint.pen().color();
-	QColor penColour = origPenColour;
-	QColor surroundColour = Qt::white;  //palette().background().color();
-
-	if (!hasLightBackground()) {
-	    int h, s, v;
-	    penColour.getHsv(&h, &s, &v);
-	    penColour = QColor::fromHsv(h, s, 255 - v);
-	    surroundColour = Qt::black;
-	}
-*/
 	paint.setPen(surroundColour);
 
 	for (int dx = -1; dx <= 1; ++dx) {
@@ -656,9 +695,7 @@
 	paint.setPen(penColour);
 
 	paint.drawText(x, y, text);
-/*
-	paint.setPen(origPenColour);
-*/
+
         paint.restore();
 
     } else {
@@ -1124,7 +1161,8 @@
 View::areLayerColoursSignificant() const
 {
     for (LayerList::const_iterator i = m_layers.begin(); i != m_layers.end(); ++i) {
-	if ((*i)->isLayerColourSignificant()) return true;
+	if ((*i)->getLayerColourSignificance() ==
+            Layer::ColourHasMeaningfulValue) return true;
         if ((*i)->isLayerOpaque()) break;
     }
     return false;
@@ -1402,17 +1440,12 @@
 	else paint.begin(this);
 
 	paint.setClipRect(cacheRect);
-	
-	if (hasLightBackground()) {
-	    paint.setPen(Qt::white);
-	    paint.setBrush(Qt::white);
-	} else {
-	    paint.setPen(Qt::black);
-	    paint.setBrush(Qt::black);
-	}
+
+        paint.setPen(getBackground());
+        paint.setBrush(getBackground());
 	paint.drawRect(cacheRect);
 
-	paint.setPen(Qt::black);
+	paint.setPen(getForeground());
 	paint.setBrush(Qt::NoBrush);
 	
 	for (LayerList::iterator i = scrollables.begin(); i != scrollables.end(); ++i) {
@@ -1448,17 +1481,12 @@
     paint.setClipRect(nonCacheRect);
 
     if (scrollables.empty()) {
-	if (hasLightBackground()) {
-	    paint.setPen(Qt::white);
-	    paint.setBrush(Qt::white);
-	} else {
-	    paint.setPen(Qt::black);
-	    paint.setBrush(Qt::black);
-	}
+        paint.setPen(getBackground());
+        paint.setBrush(getBackground());
 	paint.drawRect(nonCacheRect);
     }
 	
-    paint.setPen(Qt::black);
+    paint.setPen(getForeground());
     paint.setBrush(Qt::NoBrush);
 	
     for (LayerList::iterator i = nonScrollables.begin(); i != nonScrollables.end(); ++i) {
@@ -1494,12 +1522,12 @@
 
         int playx = getXForFrame(m_playPointerFrame);
         
-        paint.setPen(Qt::black);
+        paint.setPen(getForeground());
         paint.drawLine(playx - 1, 0, playx - 1, height() - 1);
         paint.drawLine(playx + 1, 0, playx + 1, height() - 1);
         paint.drawPoint(playx, 0);
         paint.drawPoint(playx, height() - 1);
-        paint.setPen(Qt::white);
+        paint.setPen(getBackground());
         paint.drawLine(playx, 1, playx, height() - 2);
 
 	paint.end();
@@ -1577,11 +1605,7 @@
 
 	if (illuminateThis) {
 	    paint.save();
-	    if (hasLightBackground()) {
-		paint.setPen(QPen(Qt::black, 2));
-	    } else {
-		paint.setPen(QPen(Qt::white, 2));
-	    }
+            paint.setPen(QPen(getForeground(), 2));
 	    if (closeToLeft) {
 		paint.drawLine(p0, 1, p1, 1);
 		paint.drawLine(p0, 0, p0, height());
@@ -1941,17 +1965,12 @@
         
         QRect chunk(0, 0, width(), height());
 
-	if (hasLightBackground()) {
-	    paint.setPen(Qt::white);
-	    paint.setBrush(Qt::white);
-	} else {
-	    paint.setPen(Qt::black);
-	    paint.setBrush(Qt::black);
-	}
+        paint.setPen(getBackground());
+        paint.setBrush(getBackground());
 
 	paint.drawRect(QRect(xorigin + x, 0, width(), height()));
 
-	paint.setPen(Qt::black);
+	paint.setPen(getForeground());
 	paint.setBrush(Qt::NoBrush);
 
 	for (LayerList::iterator i = m_layers.begin();
--- a/view/View.h	Wed Jul 11 20:46:37 2007 +0000
+++ b/view/View.h	Thu Jul 12 16:14:59 2007 +0000
@@ -183,6 +183,8 @@
     virtual bool getFollowGlobalZoom() const { return m_followZoom; }
 
     virtual bool hasLightBackground() const;
+    virtual QColor getForeground() const;
+    virtual QColor getBackground() const;
 
     enum TextStyle {
 	BoxedText,
--- a/widgets/AudioDial.cpp	Wed Jul 11 20:46:37 2007 +0000
+++ b/widgets/AudioDial.cpp	Thu Jul 12 16:14:59 2007 +0000
@@ -127,7 +127,7 @@
 	
     QColor knobColor(m_knobColor);
     if (knobColor == Qt::black)
-	knobColor = palette().background().color();
+	knobColor = palette().window().color();
 
     QColor meterColor(m_meterColor);
     if (!isEnabled())
--- a/widgets/PropertyBox.cpp	Wed Jul 11 20:46:37 2007 +0000
+++ b/widgets/PropertyBox.cpp	Thu Jul 12 16:14:59 2007 +0000
@@ -48,7 +48,7 @@
 #include <iostream>
 #include <cmath>
 
-#define DEBUG_PROPERTY_BOX 1
+//#define DEBUG_PROPERTY_BOX 1
 
 PropertyBox::PropertyBox(PropertyContainer *container) :
     m_container(container),
@@ -447,10 +447,7 @@
                 ColourDatabase *db = ColourDatabase::getInstance();
                 for (size_t i = 0; i < db->getColourCount(); ++i) {
                     QString name = db->getColourName(i);
-                    QColor colour = db->getColour(i);
-                    QPixmap pmap(12, 12);
-                    pmap.fill(colour);
-                    cb->addItem(pmap, name);
+                    cb->addItem(db->getExamplePixmap(i, QSize(12, 12)), name);
                 }
                 cb->addItem(tr("Add New Colour..."));
             }