diff widgets/PropertyBox.cpp @ 1324:13d9b422f7fe zoom

Merge from default branch
author Chris Cannam
date Mon, 17 Sep 2018 13:51:31 +0100
parents 13e17e61f898
children 47f35694d603 d18ca558049e
line wrap: on
line diff
--- a/widgets/PropertyBox.cpp	Mon Dec 12 15:18:52 2016 +0000
+++ b/widgets/PropertyBox.cpp	Mon Sep 17 13:51:31 2018 +0100
@@ -20,23 +20,28 @@
 #include "base/PlayParameters.h"
 #include "base/PlayParameterRepository.h"
 #include "layer/Layer.h"
-#include "layer/ColourDatabase.h"
 #include "base/UnitDatabase.h"
 #include "base/RangeMapper.h"
 
 #include "AudioDial.h"
 #include "LEDButton.h"
 #include "IconLoader.h"
+#include "LevelPanWidget.h"
+#include "LevelPanToolButton.h"
+#include "WidgetScale.h"
 
 #include "NotifyingCheckBox.h"
 #include "NotifyingComboBox.h"
 #include "NotifyingPushButton.h"
-#include "ColourNameDialog.h"
+#include "NotifyingToolButton.h"
+#include "ColourComboBox.h"
+#include "ColourMapComboBox.h"
 
 #include <QGridLayout>
 #include <QHBoxLayout>
 #include <QVBoxLayout>
 #include <QPushButton>
+#include <QToolButton>
 #include <QLabel>
 #include <QFrame>
 #include <QApplication>
@@ -57,7 +62,7 @@
 {
 #ifdef DEBUG_PROPERTY_BOX
     cerr << "PropertyBox[" << this << "(\"" <<
-	container->getPropertyContainerName() << "\" at " << container << ")]::PropertyBox" << endl;
+        container->getPropertyContainerName() << "\" at " << container << ")]::PropertyBox" << endl;
 #endif
 
     m_mainBox = new QVBoxLayout;
@@ -93,7 +98,7 @@
     size_t i;
 
     for (i = 0; i < properties.size(); ++i) {
-	updatePropertyEditor(properties[i]);
+        updatePropertyEditor(properties[i]);
     }
 
     blockSignals(false);
@@ -103,9 +108,6 @@
     connect(UnitDatabase::getInstance(), SIGNAL(unitDatabaseChanged()),
             this, SLOT(unitDatabaseChanged()));
 
-    connect(ColourDatabase::getInstance(), SIGNAL(colourDatabaseChanged()),
-            this, SLOT(colourDatabaseChanged()));
-
 #ifdef DEBUG_PROPERTY_BOX
     cerr << "PropertyBox[" << this << "]::PropertyBox returning" << endl;
 #endif
@@ -126,18 +128,18 @@
 #endif
 
     if (m_viewPlayFrame) {
-	delete m_viewPlayFrame;
-	m_viewPlayFrame = 0;
+        delete m_viewPlayFrame;
+        m_viewPlayFrame = 0;
     }
 
     if (!m_container) return;
 
     Layer *layer = dynamic_cast<Layer *>(m_container);
     if (layer) {
-	disconnect(layer, SIGNAL(modelReplaced()),
+        disconnect(layer, SIGNAL(modelReplaced()),
                    this, SLOT(populateViewPlayFrame()));
-	connect(layer, SIGNAL(modelReplaced()),
-		this, SLOT(populateViewPlayFrame()));
+        connect(layer, SIGNAL(modelReplaced()),
+                this, SLOT(populateViewPlayFrame()));
     }
 
     PlayParameters *params = m_container->getPlayParameters();
@@ -147,7 +149,7 @@
     m_viewPlayFrame->setFrameStyle(QFrame::StyledPanel | QFrame::Sunken);
     m_mainBox->addWidget(m_viewPlayFrame);
 
-    QHBoxLayout *layout = new QHBoxLayout;
+    QGridLayout *layout = new QGridLayout;
     m_viewPlayFrame->setLayout(layout);
 
     layout->setMargin(layout->margin() / 2);
@@ -156,109 +158,75 @@
     SVDEBUG << "PropertyBox::populateViewPlayFrame: container " << m_container << " (name " << m_container->getPropertyContainerName() << ") params " << params << endl;
 #endif
 
+    QSize buttonSize = WidgetScale::scaleQSize(QSize(26, 26));
+    int col = 0;
+
+    if (params) {
+        
+        m_playButton = new NotifyingToolButton;
+        m_playButton->setCheckable(true);
+        m_playButton->setIcon(IconLoader().load("speaker"));
+        m_playButton->setToolTip(tr("Click to toggle playback"));
+        m_playButton->setChecked(!params->isPlayMuted());
+        m_playButton->setFixedSize(buttonSize);
+        connect(m_playButton, SIGNAL(toggled(bool)),
+                this, SLOT(playAudibleButtonChanged(bool)));
+        connect(m_playButton, SIGNAL(mouseEntered()),
+                this, SLOT(mouseEnteredWidget()));
+        connect(m_playButton, SIGNAL(mouseLeft()),
+                this, SLOT(mouseLeftWidget()));
+        connect(params, SIGNAL(playAudibleChanged(bool)),
+                this, SLOT(playAudibleChanged(bool)));
+
+        LevelPanToolButton *levelPan = new LevelPanToolButton;
+        levelPan->setFixedSize(buttonSize);
+        levelPan->setImageSize((buttonSize.height() * 3) / 4);
+        layout->addWidget(levelPan, 0, col++, Qt::AlignCenter);
+        connect(levelPan, SIGNAL(levelChanged(float)),
+                this, SLOT(playGainControlChanged(float)));
+        connect(levelPan, SIGNAL(panChanged(float)),
+                this, SLOT(playPanControlChanged(float)));
+        connect(params, SIGNAL(playGainChanged(float)),
+                levelPan, SLOT(setLevel(float)));
+        connect(params, SIGNAL(playPanChanged(float)),
+                levelPan, SLOT(setPan(float)));
+        connect(levelPan, SIGNAL(mouseEntered()),
+                this, SLOT(mouseEnteredWidget()));
+        connect(levelPan, SIGNAL(mouseLeft()),
+                this, SLOT(mouseLeftWidget()));
+
+        layout->addWidget(m_playButton, 0, col++, Qt::AlignCenter);
+
+        if (params->getPlayClipId() != "") {
+            NotifyingToolButton *playParamButton = new NotifyingToolButton;
+            playParamButton->setObjectName("playParamButton");
+            playParamButton->setIcon(IconLoader().load("faders"));
+            playParamButton->setFixedSize(buttonSize);
+            layout->addWidget(playParamButton, 0, col++, Qt::AlignCenter);
+            connect(playParamButton, SIGNAL(clicked()),
+                    this, SLOT(editPlayParameters()));
+            connect(playParamButton, SIGNAL(mouseEntered()),
+                    this, SLOT(mouseEnteredWidget()));
+            connect(playParamButton, SIGNAL(mouseLeft()),
+                    this, SLOT(mouseLeftWidget()));
+        }
+    }
+
+    layout->setColumnStretch(col++, 10);
+
     if (layer) {
-	QLabel *showLabel = new QLabel(tr("Show"));
-	layout->addWidget(showLabel);
-	layout->setAlignment(showLabel, Qt::AlignVCenter);
 
-	m_showButton = new LEDButton(Qt::blue);
-	layout->addWidget(m_showButton);
-	connect(m_showButton, SIGNAL(stateChanged(bool)),
-		this, SIGNAL(showLayer(bool)));
+        QLabel *showLabel = new QLabel(tr("Show"));
+        layout->addWidget(showLabel, 0, col++, Qt::AlignVCenter | Qt::AlignRight);
+
+        m_showButton = new LEDButton(palette().highlight().color());
+        layout->addWidget(m_showButton, 0, col++, Qt::AlignVCenter | Qt::AlignLeft);
+        connect(m_showButton, SIGNAL(stateChanged(bool)),
+                this, SIGNAL(showLayer(bool)));
         connect(m_showButton, SIGNAL(mouseEntered()),
                 this, SLOT(mouseEnteredWidget()));
         connect(m_showButton, SIGNAL(mouseLeft()),
                 this, SLOT(mouseLeftWidget()));
-	layout->setAlignment(m_showButton, Qt::AlignVCenter);
-    }
-    
-    if (params) {
-
-	QLabel *playLabel = new QLabel(tr("Play"));
-	layout->addWidget(playLabel);
-	layout->setAlignment(playLabel, Qt::AlignVCenter);
-
-	m_playButton = new LEDButton(Qt::darkGreen);
-        m_playButton->setState(!params->isPlayMuted());
-	layout->addWidget(m_playButton);
-	connect(m_playButton, SIGNAL(stateChanged(bool)),
-		this, SLOT(playAudibleButtonChanged(bool)));
-        connect(m_playButton, SIGNAL(mouseEntered()),
-                this, SLOT(mouseEnteredWidget()));
-        connect(m_playButton, SIGNAL(mouseLeft()),
-                this, SLOT(mouseLeftWidget()));
-	connect(params, SIGNAL(playAudibleChanged(bool)),
-		this, SLOT(playAudibleChanged(bool)));
-	layout->setAlignment(m_playButton, Qt::AlignVCenter);
-
-	layout->insertStretch(-1, 10);
-
-        if (params->getPlayClipId() != "") {
-            QPushButton *playParamButton =
-                new QPushButton(QIcon(":icons/faders.png"), "");
-            playParamButton->setFixedWidth(24);
-            playParamButton->setFixedHeight(24);
-            layout->addWidget(playParamButton);
-            connect(playParamButton, SIGNAL(clicked()),
-                    this, SLOT(editPlayParameters()));
-        }
-
-	AudioDial *gainDial = new AudioDial;
-	layout->addWidget(gainDial);
-	gainDial->setMeterColor(Qt::darkRed);
-	gainDial->setMinimum(-50);
-	gainDial->setMaximum(50);
-	gainDial->setPageStep(1);
-	gainDial->setFixedWidth(24);
-	gainDial->setFixedHeight(24);
-	gainDial->setNotchesVisible(false);
-        gainDial->setObjectName(tr("Playback Gain"));
-        gainDial->setRangeMapper(new LinearRangeMapper
-                                 (-50, 50, -25, 25, tr("dB")));
-	gainDial->setDefaultValue(0);
-        gainDial->setShowToolTip(true);
-	connect(gainDial, SIGNAL(valueChanged(int)),
-		this, SLOT(playGainDialChanged(int)));
-	connect(params, SIGNAL(playGainChanged(float)),
-		this, SLOT(playGainChanged(float)));
-	connect(this, SIGNAL(changePlayGainDial(int)),
-		gainDial, SLOT(setValue(int)));
-        connect(gainDial, SIGNAL(mouseEntered()),
-                this, SLOT(mouseEnteredWidget()));
-        connect(gainDial, SIGNAL(mouseLeft()),
-                this, SLOT(mouseLeftWidget()));
-        playGainChanged(params->getPlayGain());
-	layout->setAlignment(gainDial, Qt::AlignVCenter);
-
-	AudioDial *panDial = new AudioDial;
-	layout->addWidget(panDial);
-	panDial->setMeterColor(Qt::darkGreen);
-	panDial->setMinimum(-50);
-	panDial->setMaximum(50);
-	panDial->setPageStep(1);
-	panDial->setFixedWidth(24);
-	panDial->setFixedHeight(24);
-	panDial->setNotchesVisible(false);
-	panDial->setToolTip(tr("Playback Pan / Balance"));
-	panDial->setDefaultValue(0);
-        panDial->setObjectName(tr("Playback Pan / Balance"));
-        panDial->setShowToolTip(true);
-	connect(panDial, SIGNAL(valueChanged(int)),
-		this, SLOT(playPanDialChanged(int)));
-	connect(params, SIGNAL(playPanChanged(float)),
-		this, SLOT(playPanChanged(float)));
-	connect(this, SIGNAL(changePlayPanDial(int)),
-		panDial, SLOT(setValue(int)));
-        connect(panDial, SIGNAL(mouseEntered()),
-                this, SLOT(mouseEnteredWidget()));
-        connect(panDial, SIGNAL(mouseLeft()),
-                this, SLOT(mouseLeftWidget()));
-        playPanChanged(params->getPlayPan());
-	layout->setAlignment(panDial, Qt::AlignVCenter);
-
-    } else {
-
-	layout->insertStretch(-1, 10);
     }
 }
 
@@ -273,7 +241,7 @@
     value = m_container->getPropertyRangeAndValue(name, &min, &max, &deflt);
 
     bool have = (m_propertyControllers.find(name) !=
-		 m_propertyControllers.end());
+                 m_propertyControllers.end());
 
     QString groupName = m_container->getPropertyGroupName(name);
     QString propertyLabel = m_container->getPropertyLabel(name);
@@ -281,91 +249,108 @@
 
 #ifdef DEBUG_PROPERTY_BOX
     cerr << "PropertyBox[" << this
-	      << "(\"" << m_container->getPropertyContainerName()
-	      << "\")]";
-    cerr << "::updatePropertyEditor(\"" << name << "\"):";
-    cerr << " value " << value << ", have " << have << ", group \""
-	      << groupName << "\"" << endl;
+              << "(\"" << m_container->getPropertyContainerName()
+              << "\")]";
+    cerr << "::updatePropertyEditor(\"" << name << "\", "
+         << rangeChanged << "):";
+    cerr << " type " << type << ", value " << value
+         << ", have " << have << ", group \"" << groupName << "\"" << endl;
 #endif
 
-    bool inGroup = (groupName != QString());
-
+    QString groupLabel = groupName;
+    if (groupName == QString()) {
+        groupName = "ungrouped: " + name; // not tr(), this is internal id
+        groupLabel = propertyLabel;
+    }
+    
     if (!have) {
-	if (inGroup) {
-	    if (m_groupLayouts.find(groupName) == m_groupLayouts.end()) {
-#ifdef DEBUG_PROPERTY_BOX
-		cerr << "PropertyBox: adding label \"" << groupName << "\" and frame for group for \"" << name << "\"" << endl;
+        if (m_groupLayouts.find(groupName) == m_groupLayouts.end()) {
+            QWidget *labelWidget = new QLabel(groupLabel, m_mainWidget);
+            m_layout->addWidget(labelWidget, row, 0);
+            QWidget *frame = new QWidget(m_mainWidget);
+            frame->setMinimumSize(WidgetScale::scaleQSize(QSize(1, 24)));
+            m_groupLayouts[groupName] = new QGridLayout;
+#ifdef Q_OS_MAC
+            // Seems to be plenty of whitespace already
+            m_groupLayouts[groupName]->setContentsMargins(0, 0, 0, 0);
+#else
+            // Need a bit of padding on the left
+            m_groupLayouts[groupName]->setContentsMargins
+                (WidgetScale::scalePixelSize(10), 0, 0, 0);
 #endif
-		m_layout->addWidget(new QLabel(groupName, m_mainWidget), row, 0);
-		QFrame *frame = new QFrame(m_mainWidget);
-		m_layout->addWidget(frame, row, 1, 1, 2);
-		m_groupLayouts[groupName] = new QGridLayout;
-		m_groupLayouts[groupName]->setMargin(0);
-		frame->setLayout(m_groupLayouts[groupName]);
-	    }
-	} else {
-#ifdef DEBUG_PROPERTY_BOX 
-	    cerr << "PropertyBox: adding label \"" << propertyLabel << "\"" << endl;
-#endif
-	    m_layout->addWidget(new QLabel(propertyLabel, m_mainWidget), row, 0);
-	}
+            frame->setLayout(m_groupLayouts[groupName]);
+            m_layout->addWidget(frame, row, 1, 1, 2);
+            m_layout->setColumnStretch(1, 10);
+        }
     }
 
+    QGridLayout *groupLayout = m_groupLayouts[groupName];
+
+#ifdef DEBUG_PROPERTY_BOX
+    cerr << "groupName becomes \"" << groupName << "\", groupLabel = \""
+         << groupLabel << "\", groupLayout = " << groupLayout << endl;
+#endif
+    
+    assert(groupLayout);
+
+    QWidget *existing = m_propertyControllers[name];
+    
     switch (type) {
 
     case PropertyContainer::ToggleProperty:
     {
-        QAbstractButton *button = 0;
+        QAbstractButton *button;
 
-	if (have) {
-            button = dynamic_cast<QAbstractButton *>(m_propertyControllers[name]);
-            assert(button);
-	} else {
+        if (!(button = qobject_cast<QAbstractButton *>(existing))) {
 #ifdef DEBUG_PROPERTY_BOX 
-	    cerr << "PropertyBox: creating new checkbox" << endl;
+            cerr << "PropertyBox: creating new checkbox" << endl;
 #endif
             if (iconName != "") {
+#ifdef Q_OS_MAC
+                button = new NotifyingToolButton();
+#else
                 button = new NotifyingPushButton();
+#endif
                 button->setCheckable(true);
                 QIcon icon(IconLoader().load(iconName));
                 button->setIcon(icon);
                 button->setObjectName(name);
-                button->setFixedSize(QSize(18, 18));
+                button->setFixedSize(WidgetScale::scaleQSize(QSize(18, 18)));
             } else {
                 button = new NotifyingCheckBox();
                 button->setObjectName(name);
             }
-	    connect(button, SIGNAL(toggled(bool)),
-		    this, SLOT(propertyControllerChanged(bool)));
+            connect(button, SIGNAL(toggled(bool)),
+                    this, SLOT(propertyControllerChanged(bool)));
             connect(button, SIGNAL(mouseEntered()),
                     this, SLOT(mouseEnteredWidget()));
             connect(button, SIGNAL(mouseLeft()),
                     this, SLOT(mouseLeftWidget()));
-	    if (inGroup) {
-		button->setToolTip(propertyLabel);
-		m_groupLayouts[groupName]->addWidget
-                    (button, 0, m_groupLayouts[groupName]->columnCount());
-	    } else {
-		m_layout->addWidget(button, row, 1, 1, 2);
-	    }
-	    m_propertyControllers[name] = button;
-	}
+            button->setToolTip(propertyLabel);
+
+            if (existing) {
+                groupLayout->replaceWidget(existing, button);
+                delete existing;
+            } else {
+                groupLayout->addWidget(button, 0, groupLayout->columnCount());
+            }
+
+            m_propertyControllers[name] = button;
+        }
 
         if (button->isChecked() != (value > 0)) {
-	    button->blockSignals(true);
-	    button->setChecked(value > 0);
-	    button->blockSignals(false);
-	}
-	break;
+            button->blockSignals(true);
+            button->setChecked(value > 0);
+            button->blockSignals(false);
+        }
+        break;
     }
 
     case PropertyContainer::RangeProperty:
     {
-	AudioDial *dial;
+        AudioDial *dial;
 
-	if (have) {
-	    dial = dynamic_cast<AudioDial *>(m_propertyControllers[name]);
-	    assert(dial);
+        if ((dial = qobject_cast<AudioDial *>(existing))) {
             if (rangeChanged) {
                 dial->blockSignals(true);
                 dial->setMinimum(min);
@@ -373,72 +358,139 @@
                 dial->setRangeMapper(m_container->getNewPropertyRangeMapper(name));
                 dial->blockSignals(false);
             }
-                
-	} else {
+        } else {
 #ifdef DEBUG_PROPERTY_BOX 
-	    cerr << "PropertyBox: creating new dial" << endl;
+            cerr << "PropertyBox: creating new dial" << endl;
 #endif
-	    dial = new AudioDial();
-	    dial->setObjectName(name);
-	    dial->setMinimum(min);
-	    dial->setMaximum(max);
-	    dial->setPageStep(1);
-	    dial->setNotchesVisible((max - min) <= 12);
+            dial = new AudioDial();
+            dial->setObjectName(name);
+            dial->setMinimum(min);
+            dial->setMaximum(max);
+            dial->setPageStep(1);
+            dial->setNotchesVisible((max - min) <= 12);
             // important to set the range mapper before the default,
             // because the range mapper is used to map the default
             dial->setRangeMapper(m_container->getNewPropertyRangeMapper(name));
-	    dial->setDefaultValue(deflt);
+            dial->setDefaultValue(deflt);
             dial->setShowToolTip(true);
-	    connect(dial, SIGNAL(valueChanged(int)),
-		    this, SLOT(propertyControllerChanged(int)));
+            connect(dial, SIGNAL(valueChanged(int)),
+                    this, SLOT(propertyControllerChanged(int)));
             connect(dial, SIGNAL(mouseEntered()),
                     this, SLOT(mouseEnteredWidget()));
             connect(dial, SIGNAL(mouseLeft()),
                     this, SLOT(mouseLeftWidget()));
 
-	    if (inGroup) {
-		dial->setFixedWidth(24);
-		dial->setFixedHeight(24);
-		m_groupLayouts[groupName]->addWidget
-                    (dial, 0, m_groupLayouts[groupName]->columnCount());
-	    } else {
-		dial->setFixedWidth(32);
-		dial->setFixedHeight(32);
-		m_layout->addWidget(dial, row, 1);
-		QLabel *label = new QLabel(m_mainWidget);
-		connect(dial, SIGNAL(valueChanged(int)),
-			label, SLOT(setNum(int)));
-		label->setNum(value);
-		m_layout->addWidget(label, row, 2);
-	    }
+            dial->setFixedWidth(WidgetScale::scalePixelSize(24));
+            dial->setFixedHeight(WidgetScale::scalePixelSize(24));
 
-	    m_propertyControllers[name] = dial;
-	}
+            if (existing) {
+                groupLayout->replaceWidget(existing, dial);
+                delete existing;
+            } else {
+                groupLayout->addWidget(dial, 0, groupLayout->columnCount());
+            }
 
-	if (dial->value() != value) {
-	    dial->blockSignals(true);
-	    dial->setValue(value);
-	    dial->blockSignals(false);
-	}
-	break;
+            m_propertyControllers[name] = dial;
+        }
+
+        if (dial->value() != value) {
+            dial->blockSignals(true);
+            dial->setValue(value);
+            dial->blockSignals(false);
+        }
+        break;
     }
 
+    case PropertyContainer::ColourProperty:
+    {
+        ColourComboBox *cb;
+        
+        if (!(cb = qobject_cast<ColourComboBox *>(existing))) {
+
+#ifdef DEBUG_PROPERTY_BOX 
+            cerr << "PropertyBox: creating new colour combobox" << endl;
+#endif
+            cb = new ColourComboBox(true);
+            cb->setObjectName(name);
+
+            connect(cb, SIGNAL(colourChanged(int)),
+                    this, SLOT(propertyControllerChanged(int)));
+            connect(cb, SIGNAL(mouseEntered()),
+                    this, SLOT(mouseEnteredWidget()));
+            connect(cb, SIGNAL(mouseLeft()),
+                    this, SLOT(mouseLeftWidget()));
+
+            cb->setToolTip(propertyLabel);
+
+            if (existing) {
+                groupLayout->replaceWidget(existing, cb);
+                delete existing;
+            } else {
+                groupLayout->addWidget(cb, 0, groupLayout->columnCount());
+            }
+            
+            m_propertyControllers[name] = cb;
+        }
+
+        if (cb->currentIndex() != value) {
+            cb->blockSignals(true);
+            cb->setCurrentIndex(value);
+            cb->blockSignals(false);
+        }
+
+        break;
+    }        
+
+    case PropertyContainer::ColourMapProperty:
+    {
+        ColourMapComboBox *cb;
+
+        if (!(cb = qobject_cast<ColourMapComboBox *>(existing))) {
+#ifdef DEBUG_PROPERTY_BOX 
+            cerr << "PropertyBox: creating new colourmap combobox" << endl;
+#endif
+            cb = new ColourMapComboBox(false);
+            cb->setObjectName(name);
+
+            connect(cb, SIGNAL(colourMapChanged(int)),
+                    this, SLOT(propertyControllerChanged(int)));
+            connect(cb, SIGNAL(mouseEntered()),
+                    this, SLOT(mouseEnteredWidget()));
+            connect(cb, SIGNAL(mouseLeft()),
+                    this, SLOT(mouseLeftWidget()));
+            
+            cb->setToolTip(propertyLabel);
+
+            if (existing) {
+                groupLayout->replaceWidget(existing, cb);
+                delete existing;
+            } else {
+                groupLayout->addWidget(cb, 0, groupLayout->columnCount());
+            }
+            
+            m_propertyControllers[name] = cb;
+        }
+
+        if (cb->currentIndex() != value) {
+            cb->blockSignals(true);
+            cb->setCurrentIndex(value);
+            cb->blockSignals(false);
+        }
+
+        break;
+    }        
+
     case PropertyContainer::ValueProperty:
     case PropertyContainer::UnitsProperty:
-    case PropertyContainer::ColourProperty:
     {
-	NotifyingComboBox *cb;
+        NotifyingComboBox *cb;
 
-	if (have) {
-	    cb = dynamic_cast<NotifyingComboBox *>(m_propertyControllers[name]);
-	    assert(cb);
-	} else {
+        if (!(cb = qobject_cast<NotifyingComboBox *>(existing))) {
 #ifdef DEBUG_PROPERTY_BOX 
-	    cerr << "PropertyBox: creating new combobox" << endl;
+            cerr << "PropertyBox: creating new combobox" << endl;
 #endif
-
-	    cb = new NotifyingComboBox();
-	    cb->setObjectName(name);
+            cb = new NotifyingComboBox();
+            cb->setObjectName(name);
             cb->setDuplicatesEnabled(false);
         }
 
@@ -463,7 +515,7 @@
                     }
                 }
 
-            } else if (type == PropertyContainer::UnitsProperty) {
+            } else { // PropertyContainer::UnitsProperty
 
                 QStringList units = UnitDatabase::getInstance()->getKnownUnits();
                 for (int i = 0; i < units.size(); ++i) {
@@ -471,50 +523,27 @@
                 }
 
                 cb->setEditable(true);
-
-            } else { // ColourProperty
-
-                //!!! should be a proper colour combobox class that
-                // manages its own Add New Colour entry...
-
-                int size = (QFontMetrics(QFont()).height() * 2) / 3;
-                if (size < 12) size = 12;
-                
-                ColourDatabase *db = ColourDatabase::getInstance();
-                for (int i = 0; i < db->getColourCount(); ++i) {
-                    QString name = db->getColourName(i);
-                    cb->addItem(db->getExamplePixmap(i, QSize(size, size)), name);
-                }
-                cb->addItem(tr("Add New Colour..."));
-            }                
-                
-            cb->blockSignals(false);
-            if (cb->count() < 20 && cb->count() > cb->maxVisibleItems()) {
-                cb->setMaxVisibleItems(cb->count());
             }
         }
 
         if (!have) {
-	    connect(cb, SIGNAL(activated(int)),
-		    this, SLOT(propertyControllerChanged(int)));
+            connect(cb, SIGNAL(activated(int)),
+                    this, SLOT(propertyControllerChanged(int)));
             connect(cb, SIGNAL(mouseEntered()),
                     this, SLOT(mouseEnteredWidget()));
             connect(cb, SIGNAL(mouseLeft()),
                     this, SLOT(mouseLeftWidget()));
 
-	    if (inGroup) {
-		cb->setToolTip(propertyLabel);
-		m_groupLayouts[groupName]->addWidget
-                    (cb, 0, m_groupLayouts[groupName]->columnCount());
-	    } else {
-		m_layout->addWidget(cb, row, 1, 1, 2);
-	    }
-	    m_propertyControllers[name] = cb;
-	}
+            cb->setToolTip(propertyLabel);
+            groupLayout->addWidget(cb, 0, groupLayout->columnCount());
+            m_propertyControllers[name] = cb;
+        } else if (existing != cb) {
+            groupLayout->replaceWidget(existing, cb);
+            delete existing;
+        }
 
         cb->blockSignals(true);
-        if (type == PropertyContainer::ValueProperty ||
-            type == PropertyContainer::ColourProperty) {
+        if (type == PropertyContainer::ValueProperty) {
             if (cb->currentIndex() != value) {
                 cb->setCurrentIndex(value);
             }
@@ -531,19 +560,12 @@
         }
         cb->blockSignals(false);
 
-#ifdef Q_OS_MAC
-	// Crashes on startup without this, for some reason; also
-	// prevents combo boxes from getting weirdly squished
-	// vertically
-	cb->setMinimumSize(QSize(10, cb->font().pixelSize() * 2));
-#endif
-
-	break;
+        break;
     }
 
     case PropertyContainer::InvalidProperty:
     default:
-	break;
+        break;
     }
 }
 
@@ -562,7 +584,7 @@
     blockSignals(true);
 
     for (i = 0; i < properties.size(); ++i) {
-	updatePropertyEditor(properties[i]);
+        updatePropertyEditor(properties[i]);
     }
 
     blockSignals(false);
@@ -575,7 +597,7 @@
 
     PropertyContainer::PropertyList properties = m_container->getProperties();
     for (size_t i = 0; i < properties.size(); ++i) {
-	updatePropertyEditor(properties[i], true);
+        updatePropertyEditor(properties[i], true);
     }
 
     blockSignals(false);
@@ -605,22 +627,6 @@
 }    
 
 void
-PropertyBox::colourDatabaseChanged()
-{
-    blockSignals(true);
-
-    PropertyContainer::PropertyList properties = m_container->getProperties();
-    for (size_t i = 0; i < properties.size(); ++i) {
-        if (m_container->getPropertyType(properties[i]) ==
-            PropertyContainer::ColourProperty) {
-            updatePropertyEditor(properties[i], true);
-        }
-    }
-
-    blockSignals(false);
-}    
-
-void
 PropertyBox::propertyControllerChanged(bool on)
 {
     propertyControllerChanged(on ? 1 : 0);
@@ -633,7 +639,7 @@
     QString name = obj->objectName();
 
 #ifdef DEBUG_PROPERTY_BOX
-    SVDEBUG << "PropertyBox::propertyControllerChanged(" << name	      << ", " << value << ")" << endl;
+    SVDEBUG << "PropertyBox::propertyControllerChanged(" << name              << ", " << value << ")" << endl;
 #endif
     
     PropertyContainer::PropertyType type = m_container->getPropertyType(name);
@@ -642,27 +648,16 @@
 
     if (type == PropertyContainer::UnitsProperty) {
 
-        NotifyingComboBox *cb = dynamic_cast<NotifyingComboBox *>(obj);
+        NotifyingComboBox *cb = qobject_cast<NotifyingComboBox *>(obj);
         if (cb) {
             QString unit = cb->currentText();
             c = m_container->getSetPropertyCommand
                 (name, UnitDatabase::getInstance()->getUnitId(unit));
         }
 
-    } else if (type == PropertyContainer::ColourProperty) {
-
-        if (value == int(ColourDatabase::getInstance()->getColourCount())) {
-            addNewColour();
-            if (value == int(ColourDatabase::getInstance()->getColourCount())) {
-                propertyContainerPropertyChanged(m_container);
-                return;
-            }
-        }
-        c = m_container->getSetPropertyCommand(name, value);
-
     } else if (type != PropertyContainer::InvalidProperty) {
 
-	c = m_container->getSetPropertyCommand(name, value);
+        c = m_container->getSetPropertyCommand(name, value);
     }
 
     if (c) CommandHistory::getInstance()->addCommand(c, true, true);
@@ -671,27 +666,9 @@
 }
 
 void
-PropertyBox::addNewColour()
-{
-    QColor newColour = QColorDialog::getColor();
-    if (!newColour.isValid()) return;
-
-    ColourNameDialog dialog(tr("Name New Colour"),
-                            tr("Enter a name for the new colour:"),
-                            newColour, newColour.name(), this);
-    dialog.showDarkBackgroundCheckbox(tr("Prefer black background for this colour"));
-    if (dialog.exec() == QDialog::Accepted) {
-        //!!! command
-        ColourDatabase *db = ColourDatabase::getInstance();
-        int index = db->addColour(newColour, dialog.getColourName());
-        db->setUseDarkBackground(index, dialog.isDarkBackgroundChecked());
-    }
-}
-
-void
 PropertyBox::playAudibleChanged(bool audible)
 {
-    m_playButton->setState(audible);
+    m_playButton->setChecked(audible);
 }
 
 void
@@ -707,26 +684,15 @@
         CommandHistory::getInstance()->addCommand(command, true, true);
     }
 }
-    
-void
-PropertyBox::playGainChanged(float gain)
-{
-    int dialValue = int(lrint(log10(gain) * 20.0));
-    if (dialValue < -50) dialValue = -50;
-    if (dialValue >  50) dialValue =  50;
-    emit changePlayGainDial(dialValue);
-}
 
 void
-PropertyBox::playGainDialChanged(int dialValue)
+PropertyBox::playGainControlChanged(float gain)
 {
     QObject *obj = sender();
 
     PlayParameters *params = m_container->getPlayParameters();
     if (!params) return;
 
-    float gain = float(pow(10, float(dialValue) / 20.0));
-
     if (params->getPlayGain() != gain) {
         PlayParameterRepository::EditCommand *command =
             new PlayParameterRepository::EditCommand(params);
@@ -736,28 +702,15 @@
 
     updateContextHelp(obj);
 }
-    
-void
-PropertyBox::playPanChanged(float pan)
-{
-    int dialValue = int(lrint(pan * 50.0));
-    if (dialValue < -50) dialValue = -50;
-    if (dialValue >  50) dialValue =  50;
-    emit changePlayPanDial(dialValue);
-}
 
 void
-PropertyBox::playPanDialChanged(int dialValue)
+PropertyBox::playPanControlChanged(float pan)
 {
     QObject *obj = sender();
 
     PlayParameters *params = m_container->getPlayParameters();
     if (!params) return;
 
-    float pan = float(dialValue) / 50.f;
-    if (pan < -1.f) pan = -1.f;
-    if (pan >  1.f) pan =  1.f;
-
     if (params->getPlayPan() != pan) {
         PlayParameterRepository::EditCommand *command =
             new PlayParameterRepository::EditCommand(params);
@@ -842,17 +795,34 @@
 void
 PropertyBox::updateContextHelp(QObject *o)
 {
-    QWidget *w = dynamic_cast<QWidget *>(o);
+    QWidget *w = qobject_cast<QWidget *>(o);
     if (!w) return;
 
     if (!m_container) return;
     QString cname = m_container->getPropertyContainerName();
     if (cname == "") return;
 
+    LevelPanToolButton *lp = qobject_cast<LevelPanToolButton *>(w);
+    if (lp) {
+        emit contextHelpChanged(tr("Adjust playback level and pan of %1").arg(cname));
+        return;
+    }
+
     QString wname = w->objectName();
 
+    if (wname == "playParamButton") {
+        PlayParameters *params = m_container->getPlayParameters();
+        if (params) {
+            emit contextHelpChanged
+                (tr("Change sound used for playback (currently \"%1\")")
+                 .arg(params->getPlayClipId()));
+            return;
+        }
+    }
+    
     QString extraText;
-    AudioDial *dial = dynamic_cast<AudioDial *>(w);
+    
+    AudioDial *dial = qobject_cast<AudioDial *>(w);
     if (dial) {
         double mv = dial->mappedValue();
         QString unit = "";
@@ -870,7 +840,7 @@
         emit contextHelpChanged(tr("Toggle Playback of %1").arg(cname));
     } else if (wname == "") {
         return;
-    } else if (dynamic_cast<QAbstractButton *>(w)) {
+    } else if (qobject_cast<QAbstractButton *>(w)) {
         emit contextHelpChanged(tr("Toggle %1 property of %2")
                                 .arg(wname).arg(cname));
     } else {