Chris@287: /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
Chris@287: 
Chris@287: /*
Chris@287:     Sonic Visualiser
Chris@287:     An audio file viewer and annotation editor.
Chris@287:     Centre for Digital Music, Queen Mary, University of London.
Chris@287:     This file copyright 2007 QMUL.
Chris@287:     
Chris@287:     This program is free software; you can redistribute it and/or
Chris@287:     modify it under the terms of the GNU General Public License as
Chris@287:     published by the Free Software Foundation; either version 2 of the
Chris@287:     License, or (at your option) any later version.  See the file
Chris@287:     COPYING included with this distribution for more information.
Chris@287: */
Chris@287: 
Chris@287: #include "SingleColourLayer.h"
Chris@376: #include "ColourDatabase.h"
Chris@287: #include "view/View.h"
Chris@287: 
Chris@287: #include <iostream>
Chris@287: 
Chris@316: #include <QTextStream>
Chris@287: #include <QApplication>
Chris@287: 
Chris@367: //#define DEBUG_COLOUR_SELECTION 1
Chris@367: 
Chris@293: SingleColourLayer::ColourRefCount 
Chris@293: SingleColourLayer::m_colourRefCount;
Chris@287: 
Chris@287: SingleColourLayer::SingleColourLayer() :
Chris@294:     m_colour(0),
Chris@366:     m_colourExplicitlySet(false),
Chris@366:     m_defaultColourSet(false)
Chris@287: {
Chris@287:     setDefaultColourFor(0);
Chris@287: }
Chris@287: 
Chris@299: QPixmap
Chris@299: SingleColourLayer::getLayerPresentationPixmap(QSize size) const
Chris@299: {
Chris@299:     return ColourDatabase::getInstance()->getExamplePixmap(m_colour, size);
Chris@299: }
Chris@299: 
Chris@287: bool
Chris@287: SingleColourLayer::hasLightBackground() const
Chris@287: {
Chris@287:     bool dark = ColourDatabase::getInstance()->useDarkBackground(m_colour);
Chris@287:     return !dark;
Chris@287: }
Chris@287: 
Chris@287: Layer::PropertyList
Chris@287: SingleColourLayer::getProperties() const
Chris@287: {
Chris@287:     PropertyList list = Layer::getProperties();
Chris@287:     list.push_back("Colour");
Chris@287:     return list;
Chris@287: }
Chris@287: 
Chris@287: QString
Chris@287: SingleColourLayer::getPropertyLabel(const PropertyName &name) const
Chris@287: {
Chris@287:     if (name == "Colour") return tr("Colour");
Chris@287:     return "";
Chris@287: }
Chris@287: 
Chris@287: Layer::PropertyType
Chris@287: SingleColourLayer::getPropertyType(const PropertyName &name) const
Chris@287: {
Chris@287:     if (name == "Colour") return ColourProperty;
Chris@287:     return InvalidProperty;
Chris@287: }
Chris@287: 
Chris@287: QString
Chris@287: SingleColourLayer::getPropertyGroupName(const PropertyName &) const
Chris@287: {
Chris@287:     return QString();
Chris@287: }
Chris@287: 
Chris@287: int
Chris@287: SingleColourLayer::getPropertyRangeAndValue(const PropertyName &name,
Chris@287:                                         int *min, int *max, int *deflt) const
Chris@287: {
Chris@287:     int val = 0;
Chris@287: 
Chris@287:     int garbage0, garbage1, garbage2;
Chris@287:     if (!min) min = &garbage0;
Chris@287:     if (!max) max = &garbage1;
Chris@287:     if (!deflt) deflt = &garbage2;
Chris@287: 
Chris@287:     if (name == "Colour") {
Chris@287: 
Chris@287:         ColourDatabase::getInstance()->getColourPropertyRange(min, max);
Chris@287:         *deflt = 0; //!!!
Chris@287: 
Chris@287:         val = m_colour;
Chris@287: 
Chris@287:     } else {
Chris@287: 	val = Layer::getPropertyRangeAndValue(name, min, max, deflt);
Chris@287:     }
Chris@287: 
Chris@287:     return val;
Chris@287: }
Chris@287: 
Chris@287: QString
Chris@287: SingleColourLayer::getPropertyValueLabel(const PropertyName &name,
Chris@287: 				    int value) const
Chris@287: {
Chris@287:     if (name == "Colour") {
Chris@376:         ColourDatabase *db = ColourDatabase::getInstance();
Chris@376:         if (value >= 0 && size_t(value) < db->getColourCount()) {
Chris@376:             return db->getColourName(value);
Chris@376:         }
Chris@287:     }
Chris@287:     return tr("<unknown>");
Chris@287: }
Chris@287: 
Chris@287: RangeMapper *
Chris@287: SingleColourLayer::getNewPropertyRangeMapper(const PropertyName &) const
Chris@287: {
Chris@287:     return 0;
Chris@287: }
Chris@287: 
Chris@287: void
Chris@287: SingleColourLayer::setProperty(const PropertyName &name, int value)
Chris@287: {
Chris@287:     if (name == "Colour") {
Chris@287:         setBaseColour(value);
Chris@287:     }
Chris@287: }
Chris@287: 
Chris@287: void
Chris@287: SingleColourLayer::setDefaultColourFor(View *v)
Chris@287: {
Chris@367: #ifdef DEBUG_COLOUR_SELECTION
Chris@366:     std::cerr << "SingleColourLayer::setDefaultColourFor: m_colourExplicitlySet = " << m_colourExplicitlySet << ", m_defaultColourSet " << m_defaultColourSet << std::endl;
Chris@367: #endif
Chris@366: 
Chris@366:     if (m_colourExplicitlySet || m_defaultColourSet) return;
Chris@294: 
Chris@367:     if (v) m_defaultColourSet = true; // v==0 case doesn't really count
Chris@367: 
Chris@287:     bool dark = false;
Chris@287:     if (v) {
Chris@287:         dark = !v->hasLightBackground();
Chris@287:     } else {
Chris@287:         QColor bg = QApplication::palette().color(QPalette::Window);
Chris@287:         if (bg.red() + bg.green() + bg.blue() < 384) dark = true;
Chris@287:     }
Chris@287: 
Chris@287:     ColourDatabase *cdb = ColourDatabase::getInstance();
Chris@287: 
Chris@287:     int hint = -1;
Chris@287:     bool impose = false;
Chris@287:     if (v) {
Chris@293:         if (m_colourRefCount.find(m_colour) != m_colourRefCount.end() &&
Chris@293:             m_colourRefCount[m_colour] > 0) {
Chris@293:             m_colourRefCount[m_colour]--;
Chris@293:         }
Chris@287:         // We don't want to call this if !v because that probably
Chris@287:         // means we're being called from the constructor, and this is
Chris@287:         // a virtual function
Chris@287:         hint = getDefaultColourHint(dark, impose);
Chris@367: #ifdef DEBUG_COLOUR_SELECTION
Chris@367:         std::cerr << "hint = " << hint << ", impose = " << impose << std::endl;
Chris@367: #endif
Chris@293:     } else {
Chris@367: #ifdef DEBUG_COLOUR_SELECTION
Chris@367:         std::cerr << "(from ctor)" << std::endl;
Chris@367: #endif
Chris@287:     }
Chris@287: 
Chris@287:     if (hint >= 0 && impose) {
Chris@293:         setBaseColour(hint);
Chris@287:         return;
Chris@287:     }
Chris@287: 
Chris@293:     int bestCount = 0, bestColour = -1;
Chris@293:     
Chris@287:     for (int i = 0; i < cdb->getColourCount(); ++i) {
Chris@293: 
Chris@287:         int index = i;
Chris@287:         if (hint > 0) index = (index + hint) % cdb->getColourCount();
Chris@287:         if (cdb->useDarkBackground(index) != dark) continue;
Chris@293: 
Chris@293:         int count = 0;
Chris@293:         if (m_colourRefCount.find(index) != m_colourRefCount.end()) {
Chris@293:             count = m_colourRefCount[index];
Chris@287:         }
Chris@293: 
Chris@367: #ifdef DEBUG_COLOUR_SELECTION
Chris@367:         std::cerr << "index = " << index << ", count = " << count;
Chris@367: #endif
Chris@293: 
Chris@293:         if (bestColour < 0 || count < bestCount) {
Chris@293:             bestColour = index;
Chris@293:             bestCount = count;
Chris@367: #ifdef DEBUG_COLOUR_SELECTION
Chris@367:             std::cerr << " *";
Chris@367: #endif
Chris@293:         }
Chris@293: 
Chris@367: #ifdef DEBUG_COLOUR_SELECTION
Chris@367:         std::cerr << std::endl;
Chris@367: #endif
Chris@287:     }
Chris@293:     
Chris@293:     if (bestColour < 0) m_colour = 0;
Chris@293:     else m_colour = bestColour;
Chris@287: 
Chris@293:     if (m_colourRefCount.find(m_colour) == m_colourRefCount.end()) {
Chris@293:         m_colourRefCount[m_colour] = 1;
Chris@293:     } else {
Chris@293:         m_colourRefCount[m_colour]++;
Chris@293:     }
Chris@287: }
Chris@287: 
Chris@287: void
Chris@287: SingleColourLayer::setBaseColour(int colour)
Chris@287: {
Chris@294:     m_colourExplicitlySet = true;
Chris@294: 
Chris@287:     if (m_colour == colour) return;
Chris@293: 
Chris@293:     if (m_colourRefCount.find(m_colour) != m_colourRefCount.end() &&
Chris@293:         m_colourRefCount[m_colour] > 0) {
Chris@293:         m_colourRefCount[m_colour]--;
Chris@293:     }
Chris@293: 
Chris@287:     m_colour = colour;
Chris@293: 
Chris@293:     if (m_colourRefCount.find(m_colour) == m_colourRefCount.end()) {
Chris@293:         m_colourRefCount[m_colour] = 1;
Chris@293:     } else {
Chris@293:         m_colourRefCount[m_colour]++;
Chris@293:     }
Chris@293: 
Chris@287:     flagBaseColourChanged();
Chris@287:     emit layerParametersChanged();
Chris@287: }
Chris@287: 
Chris@287: int
Chris@287: SingleColourLayer::getBaseColour() const
Chris@287: {
Chris@287:     return m_colour;
Chris@287: }
Chris@287: 
Chris@287: QColor
Chris@287: SingleColourLayer::getBaseQColor() const
Chris@287: {
Chris@287:     return ColourDatabase::getInstance()->getColour(m_colour);
Chris@287: }
Chris@287: 
Chris@287: QColor
Chris@287: SingleColourLayer::getBackgroundQColor(View *v) const
Chris@287: {
Chris@287:     return v->getBackground();
Chris@287: }
Chris@287: 
Chris@287: QColor
Chris@287: SingleColourLayer::getForegroundQColor(View *v) const
Chris@287: {
Chris@287:     return v->getForeground();
Chris@287: }
Chris@287: 
Chris@287: std::vector<QColor>
Chris@287: SingleColourLayer::getPartialShades(View *v) const
Chris@287: {
Chris@287:     std::vector<QColor> s;
Chris@287:     QColor base = getBaseQColor();
Chris@287:     QColor bg = getBackgroundQColor(v);
Chris@287:     for (int i = 0; i < 3; ++i) {
Chris@287:         int red = base.red() + ((bg.red() - base.red()) * (i + 1)) / 4;
Chris@287:         int green = base.green() + ((bg.green() - base.green()) * (i + 1)) / 4;
Chris@287:         int blue = base.blue() + ((bg.blue() - base.blue()) * (i + 1)) / 4;
Chris@287:         s.push_back(QColor(red, green, blue));
Chris@287:     }
Chris@287:     return s;
Chris@287: }
Chris@287: 
Chris@316: void
Chris@316: SingleColourLayer::toXml(QTextStream &stream,
Chris@316:                          QString indent, QString extraAttributes) const
Chris@287: {
Chris@287:     QString s;
Chris@287:     
Chris@287:     QString colourName, colourSpec, darkbg;
Chris@287:     ColourDatabase::getInstance()->getStringValues
Chris@287:         (m_colour, colourName, colourSpec, darkbg);
Chris@287: 
Chris@287:     s += QString("colourName=\"%1\" "
Chris@287:                  "colour=\"%2\" "
Chris@287:                  "darkBackground=\"%3\" ")
Chris@287: 	.arg(colourName)
Chris@287:         .arg(colourSpec)
Chris@287:         .arg(darkbg);
Chris@287: 
Chris@316:     Layer::toXml(stream, indent, extraAttributes + " " + s);
Chris@287: }
Chris@287: 
Chris@287: void
Chris@287: SingleColourLayer::setProperties(const QXmlAttributes &attributes)
Chris@287: {
Chris@287:     QString colourName = attributes.value("colourName");
Chris@287:     QString colourSpec = attributes.value("colour");
Chris@287:     QString darkbg = attributes.value("darkBackground");
Chris@296: 
Chris@296:     int colour = ColourDatabase::getInstance()->putStringValues
Chris@287:         (colourName, colourSpec, darkbg);
Chris@296: 
Chris@296:     m_colourExplicitlySet = true;
Chris@296: 
Chris@296:     if (m_colour != colour) {
Chris@296: 
Chris@367: #ifdef DEBUG_COLOUR_SELECTION
Chris@296:         std::cerr << "SingleColourLayer::setProperties: changing colour from " << m_colour << " to " << colour << std::endl;
Chris@367: #endif
Chris@296: 
Chris@296:         if (m_colourRefCount.find(m_colour) != m_colourRefCount.end() &&
Chris@296:             m_colourRefCount[m_colour] > 0) {
Chris@296:             m_colourRefCount[m_colour]--;
Chris@296:         }
Chris@296: 
Chris@296:         m_colour = colour;
Chris@296: 
Chris@296:         if (m_colourRefCount.find(m_colour) == m_colourRefCount.end()) {
Chris@296:             m_colourRefCount[m_colour] = 1;
Chris@296:         } else {
Chris@296:             m_colourRefCount[m_colour]++;
Chris@296:         }
Chris@296: 
Chris@296:         flagBaseColourChanged();
Chris@294:     }
Chris@287: }
Chris@287: