changeset 189:5b7472db612b

* Add large chunks of context help in the optional status bar * Add an extra overlay mode in which even the centre frame is disabled * Fixes to FTP retrieval
author Chris Cannam
date Fri, 19 Jan 2007 13:13:14 +0000
parents dd573e090eed
children 53835534a9d3
files layer/WaveformLayer.cpp view/Overview.cpp view/Overview.h view/Pane.cpp view/Pane.h view/PaneStack.cpp view/PaneStack.h view/View.cpp view/View.h view/ViewManager.cpp view/ViewManager.h widgets/AudioDial.cpp widgets/AudioDial.h widgets/Fader.cpp widgets/Fader.h widgets/LEDButton.cpp widgets/LEDButton.h widgets/NotifyingCheckBox.cpp widgets/NotifyingCheckBox.h widgets/NotifyingComboBox.cpp widgets/NotifyingComboBox.h widgets/NotifyingPushButton.cpp widgets/NotifyingPushButton.h widgets/Panner.cpp widgets/Panner.h widgets/PropertyBox.cpp widgets/PropertyBox.h widgets/PropertyStack.cpp widgets/PropertyStack.h widgets/Thumbwheel.cpp widgets/Thumbwheel.h widgets/widgets.pro
diffstat 32 files changed, 645 insertions(+), 69 deletions(-) [+]
line wrap: on
line diff
--- a/layer/WaveformLayer.cpp	Fri Jan 12 21:52:56 2007 +0000
+++ b/layer/WaveformLayer.cpp	Fri Jan 19 13:13:14 2007 +0000
@@ -37,6 +37,7 @@
     m_gain(1.0f),
     m_autoNormalize(false),
     m_colour(Qt::black),
+//    m_colour(QColor(84, 177, 248)),
     m_showMeans(true),
     m_greyscale(true),
     m_channelMode(SeparateChannels),
@@ -164,7 +165,8 @@
 
 	if (m_colour == Qt::black) deft = 0;
 	else if (m_colour == Qt::darkRed) deft = 1;
-	else if (m_colour == Qt::darkBlue) deft = 2;
+	else if (m_colour == Qt::darkBlue ||
+                 m_colour == QColor(84, 177, 248)) deft = 2;
 	else if (m_colour == Qt::darkGreen) deft = 3;
 	else if (m_colour == QColor(200, 50, 255)) deft = 4;
 	else if (m_colour == QColor(255, 150, 50)) deft = 5;
@@ -246,6 +248,7 @@
 	default:
 	case 0:	setBaseColour(Qt::black); break;
 	case 1: setBaseColour(Qt::darkRed); break;
+//	case 2: setBaseColour(QColor(84, 177, 248)); break;
 	case 2: setBaseColour(Qt::darkBlue); break;
 	case 3: setBaseColour(Qt::darkGreen); break;
 	case 4: setBaseColour(QColor(200, 50, 255)); break;
--- a/view/Overview.cpp	Fri Jan 12 21:52:56 2007 +0000
+++ b/view/Overview.cpp	Fri Jan 19 13:13:14 2007 +0000
@@ -210,4 +210,23 @@
     }
 }
 
+void
+Overview::mouseDoubleClickEvent(QMouseEvent *e)
+{
+    long frame = getFrameForX(e->x());
+    emit centreFrameChanged(this, frame, true);
+}
 
+void
+Overview::enterEvent(QEvent *)
+{
+    emit contextHelpChanged(tr("Click and drag to navigate; double-click to jump"));
+}
+
+void
+Overview::leaveEvent(QEvent *)
+{
+    emit contextHelpChanged("");
+}
+
+
--- a/view/Overview.h	Fri Jan 12 21:52:56 2007 +0000
+++ b/view/Overview.h	Fri Jan 19 13:13:14 2007 +0000
@@ -52,6 +52,9 @@
     virtual void mousePressEvent(QMouseEvent *e);
     virtual void mouseReleaseEvent(QMouseEvent *e);
     virtual void mouseMoveEvent(QMouseEvent *e);
+    virtual void mouseDoubleClickEvent(QMouseEvent *e);
+    virtual void enterEvent(QEvent *);
+    virtual void leaveEvent(QEvent *);
     virtual bool shouldLabelSelections() const { return false; }
 
     QPoint m_clickPos;
--- a/view/Pane.cpp	Fri Jan 12 21:52:56 2007 +0000
+++ b/view/Pane.cpp	Fri Jan 19 13:13:14 2007 +0000
@@ -35,6 +35,7 @@
 #include "widgets/Thumbwheel.h"
 #include "widgets/Panner.h"
 #include "widgets/RangeInputDialog.h"
+#include "widgets/NotifyingPushButton.h"
 
 using std::cerr;
 using std::endl;
@@ -99,6 +100,8 @@
         m_hthumb->setSpeed(0.6);
         connect(m_hthumb, SIGNAL(valueChanged(int)), this, 
                 SLOT(horizontalThumbwheelMoved(int)));
+        connect(m_hthumb, SIGNAL(mouseEntered()), this, SLOT(mouseEnteredWidget()));
+        connect(m_hthumb, SIGNAL(mouseLeft()), this, SLOT(mouseLeftWidget()));
 
         m_vpan = new Panner;
         layout->addWidget(m_vpan, 0, 1);
@@ -109,6 +112,8 @@
                 this, SLOT(verticalPannerMoved(float, float, float, float)));
         connect(m_vpan, SIGNAL(doubleClicked()),
                 this, SLOT(editVerticalPannerExtents()));
+        connect(m_vpan, SIGNAL(mouseEntered()), this, SLOT(mouseEnteredWidget()));
+        connect(m_vpan, SIGNAL(mouseLeft()), this, SLOT(mouseLeftWidget()));
 
         m_vthumb = new Thumbwheel(Qt::Vertical);
         m_vthumb->setObjectName(tr("Vertical Zoom"));
@@ -117,19 +122,23 @@
         m_vthumb->setFixedHeight(70);
         connect(m_vthumb, SIGNAL(valueChanged(int)), this, 
                 SLOT(verticalThumbwheelMoved(int)));
+        connect(m_vthumb, SIGNAL(mouseEntered()), this, SLOT(mouseEnteredWidget()));
+        connect(m_vthumb, SIGNAL(mouseLeft()), this, SLOT(mouseLeftWidget()));
 
         if (layer) {
             RangeMapper *rm = layer->getNewVerticalZoomRangeMapper();
             if (rm) m_vthumb->setRangeMapper(rm);
         }
 
-        QPushButton *reset = new QPushButton;
-        reset->setFixedHeight(16);
-        reset->setFixedWidth(16);
-        layout->addWidget(reset, 1, 2);
-        connect(reset, SIGNAL(clicked()), m_hthumb, SLOT(resetToDefault()));
-        connect(reset, SIGNAL(clicked()), m_vthumb, SLOT(resetToDefault()));
-        connect(reset, SIGNAL(clicked()), m_vpan, SLOT(resetToDefault()));
+        m_reset = new NotifyingPushButton;
+        m_reset->setFixedHeight(16);
+        m_reset->setFixedWidth(16);
+        layout->addWidget(m_reset, 1, 2);
+        connect(m_reset, SIGNAL(clicked()), m_hthumb, SLOT(resetToDefault()));
+        connect(m_reset, SIGNAL(clicked()), m_vthumb, SLOT(resetToDefault()));
+        connect(m_reset, SIGNAL(clicked()), m_vpan, SLOT(resetToDefault()));
+        connect(m_reset, SIGNAL(mouseEntered()), this, SLOT(mouseEnteredWidget()));
+        connect(m_reset, SIGNAL(mouseLeft()), this, SLOT(mouseLeftWidget()));
     }
 
     int count = 0;
@@ -381,8 +390,7 @@
             waveformModel = (*vi)->getModel();
         }
 
-        if (!m_manager ||
-            m_manager->getOverlayMode() == ViewManager::NoOverlays) {
+        if (!m_manager || !m_manager->shouldShowVerticalScale()) {
             break;
         }
 
@@ -464,14 +472,21 @@
     int sampleRate = getModelsSampleRate();
     paint.setBrush(Qt::NoBrush);
 
-    if (m_centreLineVisible) {
+    if (m_centreLineVisible &&
+        m_manager &&
+        m_manager->shouldShowCentreLine()) {
 
-	if (hasLightBackground()) {
-	    paint.setPen(QColor(50, 50, 50));
-	} else {
-	    paint.setPen(QColor(200, 200, 200));
-	}	
-	paint.drawLine(width() / 2, 0, width() / 2, height() - 1);
+        QColor c = QColor(0, 0, 0);
+        if (!hasLightBackground()) {
+            c = QColor(240, 240, 240);
+        }
+        paint.setPen(c);
+        int x = width() / 2 + 1;
+	paint.drawLine(x, 0, x, height() - 1);
+        paint.drawLine(x-1, 1, x+1, 1);
+        paint.drawLine(x-2, 0, x+2, 0);
+        paint.drawLine(x-1, height() - 2, x+1, height() - 2);
+        paint.drawLine(x-2, height() - 1, x+2, height() - 1);
 
 	paint.setPen(QColor(50, 50, 50));
 
@@ -499,8 +514,7 @@
 	    }
 	}
 
-        if (m_manager &&
-            m_manager->getOverlayMode() != ViewManager::NoOverlays) {
+        if (m_manager && m_manager->shouldShowFrameCount()) {
 
             if (sampleRate) {
 
@@ -529,7 +543,7 @@
 
     if (waveformModel &&
         m_manager &&
-        m_manager->getOverlayMode() != ViewManager::NoOverlays &&
+        m_manager->shouldShowDuration() &&
 	r.y() + r.height() >= height() - fontHeight - 6) {
 
         size_t modelRate = waveformModel->getSampleRate();
@@ -565,7 +579,7 @@
     }
 
     if (m_manager &&
-        m_manager->getOverlayMode() == ViewManager::AllOverlays &&
+        m_manager->shouldShowLayerNames() &&
         r.y() + r.height() >= height() - m_layers.size() * fontHeight - 6) {
 
 	std::vector<QString> texts;
@@ -739,6 +753,7 @@
 Pane::mousePressEvent(QMouseEvent *e)
 {
     if (e->buttons() & Qt::RightButton) {
+        emit contextHelpChanged("");
         emit rightButtonMenuRequested(mapToGlobal(e->pos()));
         return;
     }
@@ -921,6 +936,8 @@
         return;
     }
 
+    updateContextHelp(&e->pos());
+
     ViewManager::ToolMode mode = ViewManager::NavigateMode;
     if (m_manager) mode = m_manager->getToolMode();
 
@@ -1271,6 +1288,7 @@
     bool previouslyIdentifying = m_identifyFeatures;
     m_identifyFeatures = false;
     if (previouslyIdentifying) update();
+    emit contextHelpChanged("");
 }
 
 void
@@ -1620,6 +1638,104 @@
     }
 }
 
+void
+Pane::updateContextHelp(const QPoint *pos)
+{
+    QString help = "";
+
+    if (m_clickedInRange) {
+        emit contextHelpChanged("");
+        return;
+    }
+
+    ViewManager::ToolMode mode = ViewManager::NavigateMode;
+    if (m_manager) mode = m_manager->getToolMode();
+
+    bool editable = false;
+    Layer *layer = getSelectedLayer();
+    if (layer && layer->isLayerEditable()) {
+        editable = true;
+    }
+        
+    if (mode == ViewManager::NavigateMode) {
+
+        help = tr("Click and drag to navigate");
+        
+    } else if (mode == ViewManager::SelectMode) {
+
+        bool haveSelection = (m_manager && !m_manager->getSelections().empty());
+
+        if (haveSelection) {
+            if (editable) {
+                help = tr("Click and drag to select a range; hold Shift to avoid snapping to items; hold Ctrl for multi-select; middle-click and drag to navigate");
+            } else {
+                help = tr("Click and drag to select a range; hold Ctrl for multi-select; middle-click and drag to navigate");
+            }                
+
+            if (pos) {
+                bool closeToLeft = false, closeToRight = false;
+                Selection selection = getSelectionAt(pos->x(), closeToLeft, closeToRight);
+                if ((closeToLeft || closeToRight) && !(closeToLeft && closeToRight)) {
+                    
+                    help = tr("Click and drag to move the selection boundary");
+                }
+            }
+        } else {
+            if (editable) {
+                help = tr("Click and drag to select a range; hold Shift to avoid snapping to items; middle-click to navigate");
+            } else {
+                help = tr("Click and drag to select a range; middle-click and drag to navigate");
+            }
+        }
+
+    } else if (mode == ViewManager::DrawMode) {
+        
+        //!!! could call through to a layer function to find out exact meaning
+	if (editable) {
+            help = tr("Click to add a new item in the active layer");
+        }
+        
+    } else if (mode == ViewManager::EditMode) {
+        
+        //!!! could call through to layer
+	if (editable) {
+            help = tr("Click and drag an item in the active layer to move it");
+            if (pos) {
+                bool closeToLeft = false, closeToRight = false;
+                Selection selection = getSelectionAt(pos->x(), closeToLeft, closeToRight);
+                if (!selection.isEmpty()) {
+                    help = tr("Click and drag to move all items in the selected range");
+                }
+            }
+        }
+    }
+
+    emit contextHelpChanged(help);
+}
+
+void
+Pane::mouseEnteredWidget()
+{
+    QWidget *w = dynamic_cast<QWidget *>(sender());
+    if (!w) return;
+
+    if (w == m_vpan) {
+        emit contextHelpChanged(tr("Click and drag to adjust the visible range of the vertical scale"));
+    } else if (w == m_vthumb) {
+        emit contextHelpChanged(tr("Click and drag to adjust the vertical zoom level"));
+    } else if (w == m_hthumb) {
+        emit contextHelpChanged(tr("Click and drag to adjust the horizontal zoom level"));
+    } else if (w == m_reset) {
+        emit contextHelpChanged(tr("Reset horizontal and vertical zoom levels to their defaults"));
+    }
+}
+
+void
+Pane::mouseLeftWidget()
+{
+    emit contextHelpChanged("");
+}
+
 QString
 Pane::toXmlString(QString indent, QString extraAttributes) const
 {
--- a/view/Pane.h	Fri Jan 12 21:52:56 2007 +0000
+++ b/view/Pane.h	Fri Jan 19 13:13:14 2007 +0000
@@ -29,6 +29,7 @@
 class Layer;
 class Thumbwheel;
 class Panner;
+class NotifyingPushButton;
 
 class Pane : public View
 {
@@ -67,6 +68,9 @@
 
     virtual void propertyContainerSelected(View *, PropertyContainer *pc);
 
+    void mouseEnteredWidget();
+    void mouseLeftWidget();
+
 protected:
     virtual void paintEvent(QPaintEvent *e);
     virtual void mousePressEvent(QMouseEvent *e);
@@ -96,6 +100,7 @@
     void dragTopLayer(QMouseEvent *e);
     void dragExtendSelection(QMouseEvent *e);
     void zoomToRegion(int x0, int y0, int x1, int y1);
+    void updateContextHelp(const QPoint *pos);
 
     bool m_identifyFeatures;
     QPoint m_identifyPoint;
@@ -125,6 +130,7 @@
     Panner *m_vpan;
     Thumbwheel *m_hthumb;
     Thumbwheel *m_vthumb;
+    NotifyingPushButton *m_reset;
 };
 
 #endif
--- a/view/PaneStack.cpp	Fri Jan 12 21:52:56 2007 +0000
+++ b/view/PaneStack.cpp	Fri Jan 19 13:13:14 2007 +0000
@@ -80,6 +80,8 @@
 	properties = new PropertyStack(frame, pane);
 	connect(properties, SIGNAL(propertyContainerSelected(View *, PropertyContainer *)),
 		this, SLOT(propertyContainerSelected(View *, PropertyContainer *)));
+        connect(properties, SIGNAL(contextHelpChanged(const QString &)),
+                this, SIGNAL(contextHelpChanged(const QString &)));
     }
     if (m_layoutStyle == PropertyStackPerPaneLayout) {
         layout->addWidget(properties);
--- a/view/PaneStack.h	Fri Jan 12 21:52:56 2007 +0000
+++ b/view/PaneStack.h	Fri Jan 19 13:13:14 2007 +0000
@@ -68,6 +68,7 @@
     void currentLayerChanged(Pane *pane, Layer *layer);
     void rightButtonMenuRequested(Pane *pane, QPoint position);
     void propertyStacksResized();
+    void contextHelpChanged(const QString &);
 
 public slots:
     void propertyContainerAdded(PropertyContainer *);
--- a/view/View.cpp	Fri Jan 12 21:52:56 2007 +0000
+++ b/view/View.cpp	Fri Jan 19 13:13:14 2007 +0000
@@ -1477,7 +1477,7 @@
 	}
 
 	if (sampleRate && shouldLabelSelections() && m_manager &&
-            m_manager->getOverlayMode() == ViewManager::AllOverlays) {
+            m_manager->shouldShowSelectionExtents()) {
 	    
 	    QString startText = QString("%1 / %2")
 		.arg(QString::fromStdString
--- a/view/View.h	Fri Jan 12 21:52:56 2007 +0000
+++ b/view/View.h	Fri Jan 19 13:13:14 2007 +0000
@@ -242,6 +242,8 @@
     void centreFrameChanged(void *, unsigned long, bool);
     void zoomLevelChanged(void *, unsigned long, bool);
 
+    void contextHelpChanged(const QString &);
+
 public slots:
     virtual void modelChanged();
     virtual void modelChanged(size_t startFrame, size_t endFrame);
--- a/view/ViewManager.cpp	Fri Jan 12 21:52:56 2007 +0000
+++ b/view/ViewManager.cpp	Fri Jan 19 13:13:14 2007 +0000
@@ -36,7 +36,7 @@
     m_toolMode(NavigateMode),
     m_playLoopMode(false),
     m_playSelectionMode(false),
-    m_overlayMode(BasicOverlays),
+    m_overlayMode(StandardOverlays),
     m_zoomWheelsEnabled(true)
 {
     QSettings settings;
--- a/view/ViewManager.h	Fri Jan 12 21:52:56 2007 +0000
+++ b/view/ViewManager.h	Fri Jan 19 13:13:14 2007 +0000
@@ -100,12 +100,32 @@
 
     enum OverlayMode {
         NoOverlays,
-        BasicOverlays,
+        MinimalOverlays,
+        StandardOverlays,
         AllOverlays
     };
     void setOverlayMode(OverlayMode mode);
     OverlayMode getOverlayMode() const { return m_overlayMode; }
 
+    bool shouldShowCentreLine() const {
+        return m_overlayMode != NoOverlays;
+    }
+    bool shouldShowFrameCount() const {
+        return m_overlayMode != NoOverlays;
+    }
+    bool shouldShowDuration() const {
+        return m_overlayMode > MinimalOverlays;
+    }
+    bool shouldShowVerticalScale() const {
+        return m_overlayMode > MinimalOverlays;
+    }
+    bool shouldShowSelectionExtents() const {
+        return m_overlayMode > MinimalOverlays;
+    }
+    bool shouldShowLayerNames() const {
+        return m_overlayMode == AllOverlays;
+    }
+
     void setZoomWheelsEnabled(bool enable);
     bool getZoomWheelsEnabled() const { return m_zoomWheelsEnabled; }
 
--- a/widgets/AudioDial.cpp	Fri Jan 12 21:52:56 2007 +0000
+++ b/widgets/AudioDial.cpp	Fri Jan 19 13:13:14 2007 +0000
@@ -66,6 +66,9 @@
 #define AUDIO_DIAL_RANGE (AUDIO_DIAL_MAX - AUDIO_DIAL_MIN)
 
 
+static int dialsExtant = 0;
+
+
 // Constructor.
 AudioDial::AudioDial(QWidget *parent) :
     QDial(parent),
@@ -79,6 +82,7 @@
 {
     m_mouseDial = false;
     m_mousePressed = false;
+    ++dialsExtant;
 }
 
 
@@ -86,11 +90,14 @@
 AudioDial::~AudioDial (void)
 {
     delete m_rangeMapper;
+    --dialsExtant;
 }
 
 
 void AudioDial::setRangeMapper(RangeMapper *mapper)
 {
+    std::cerr << "AudioDial[" << this << "][\"" << objectName().toStdString() << "\"::setRangeMapper(" << mapper << ") [current is " << m_rangeMapper << "] (have " << dialsExtant << " dials extant)" << std::endl;
+
     if (m_rangeMapper == mapper) return;
 
     if (!m_rangeMapper && mapper) {
@@ -515,6 +522,20 @@
     }
 }
 
+void
+AudioDial::enterEvent(QEvent *e)
+{
+    QDial::enterEvent(e);
+    emit mouseEntered();
+}
+
+void
+AudioDial::leaveEvent(QEvent *e)
+{
+    QDial::enterEvent(e);
+    emit mouseLeft();
+}
+
 #ifdef INCLUDE_MOCFILES
 #include "AudioDial.moc.cpp"
 #endif
--- a/widgets/AudioDial.h	Fri Jan 12 21:52:56 2007 +0000
+++ b/widgets/AudioDial.h	Fri Jan 19 13:13:14 2007 +0000
@@ -77,6 +77,10 @@
 
     void setShowToolTip(bool show);
 
+signals:
+    void mouseEntered();
+    void mouseLeft();
+
 public slots:
     /**
      * Set the colour of the knob.  The default is to inherit the
@@ -110,6 +114,8 @@
     virtual void mouseMoveEvent(QMouseEvent *pMouseEvent);
     virtual void mouseReleaseEvent(QMouseEvent *pMouseEvent);
     virtual void mouseDoubleClickEvent(QMouseEvent *pMouseEvent);
+    virtual void enterEvent(QEvent *);
+    virtual void leaveEvent(QEvent *);
 
 protected slots:
     void updateMappedValue(int value);
--- a/widgets/Fader.cpp	Fri Jan 12 21:52:56 2007 +0000
+++ b/widgets/Fader.cpp	Fri Jan 19 13:13:14 2007 +0000
@@ -201,6 +201,17 @@
     emit valueChanged(getValue());
 }
 
+void
+Fader::enterEvent(QEvent *)
+{
+    emit mouseEntered();
+}
+
+void
+Fader::leaveEvent(QEvent *)
+{
+    emit mouseLeft();
+}
 
 void
 Fader::setValue(float v)
--- a/widgets/Fader.h	Fri Jan 12 21:52:56 2007 +0000
+++ b/widgets/Fader.h	Fri Jan 19 13:13:14 2007 +0000
@@ -68,17 +68,22 @@
     void setPeakRight(float);
     float getPeakRight() { return m_peakRight; }
 
+signals:
+    void valueChanged(float); // 0.0 -> 1.0
+
+    void mouseEntered();
+    void mouseLeft();
+
+protected:
     virtual void mousePressEvent(QMouseEvent *ev);
     virtual void mouseDoubleClickEvent(QMouseEvent *ev);
     virtual void mouseMoveEvent(QMouseEvent *ev);
     virtual void mouseReleaseEvent(QMouseEvent *ev);
     virtual void wheelEvent( QWheelEvent *ev );
     virtual void paintEvent(QPaintEvent *ev);
+    virtual void enterEvent(QEvent *);
+    virtual void leaveEvent(QEvent *);
 
-signals:
-    void valueChanged(float); // 0.0 -> 1.0
-
-private:
     int getMaxX() const;
 
     bool m_withoutKnob;
--- a/widgets/LEDButton.cpp	Fri Jan 12 21:52:56 2007 +0000
+++ b/widgets/LEDButton.cpp	Fri Jan 19 13:13:14 2007 +0000
@@ -105,6 +105,18 @@
 }
 
 void
+LEDButton::enterEvent(QEvent *)
+{
+    emit mouseEntered();
+}
+
+void
+LEDButton::leaveEvent(QEvent *)
+{
+    emit mouseLeft();
+}
+
+void
 LEDButton::paintEvent(QPaintEvent *)
 {
     QPainter paint;
--- a/widgets/LEDButton.h	Fri Jan 12 21:52:56 2007 +0000
+++ b/widgets/LEDButton.h	Fri Jan 19 13:13:14 2007 +0000
@@ -54,6 +54,9 @@
 signals:
     void stateChanged(bool);
 
+    void mouseEntered();
+    void mouseLeft();
+
 public slots:
     void toggle();
     void on();
@@ -67,12 +70,12 @@
 protected:
     void paintEvent(QPaintEvent *);
     void mousePressEvent(QMouseEvent *);
+    void enterEvent(QEvent *);
+    void leaveEvent(QEvent *);
 
-private:
     bool led_state;
     QColor led_color;
 
-private:
     class LEDButtonPrivate;
     LEDButtonPrivate *d;
 };
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/widgets/NotifyingCheckBox.cpp	Fri Jan 19 13:13:14 2007 +0000
@@ -0,0 +1,35 @@
+/* -*- 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 "NotifyingCheckBox.h"
+
+NotifyingCheckBox::~NotifyingCheckBox()
+{
+}
+
+void
+NotifyingCheckBox::enterEvent(QEvent *e)
+{
+    QCheckBox::enterEvent(e);
+    emit mouseEntered();
+}
+
+void
+NotifyingCheckBox::leaveEvent(QEvent *e)
+{
+    QCheckBox::enterEvent(e);
+    emit mouseLeft();
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/widgets/NotifyingCheckBox.h	Fri Jan 19 13:13:14 2007 +0000
@@ -0,0 +1,46 @@
+/* -*- 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 _NOTIFYING_CHECK_BOX_H_
+#define _NOTIFYING_CHECK_BOX_H_
+
+#include <QCheckBox>
+
+/**
+ * Very trivial enhancement to QCheckBox to make it emit signals when
+ * the mouse enters and leaves (for context help).
+ */
+
+class NotifyingCheckBox : public QCheckBox
+{
+    Q_OBJECT
+public:
+
+    NotifyingCheckBox(QWidget *parent = 0) :
+        QCheckBox(parent) { }
+
+    virtual ~NotifyingCheckBox();
+
+signals:
+    void mouseEntered();
+    void mouseLeft();
+
+protected:
+    virtual void enterEvent(QEvent *);
+    virtual void leaveEvent(QEvent *);
+};
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/widgets/NotifyingComboBox.cpp	Fri Jan 19 13:13:14 2007 +0000
@@ -0,0 +1,35 @@
+/* -*- 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 "NotifyingComboBox.h"
+
+NotifyingComboBox::~NotifyingComboBox()
+{
+}
+
+void
+NotifyingComboBox::enterEvent(QEvent *e)
+{
+    QComboBox::enterEvent(e);
+    emit mouseEntered();
+}
+
+void
+NotifyingComboBox::leaveEvent(QEvent *e)
+{
+    QComboBox::enterEvent(e);
+    emit mouseLeft();
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/widgets/NotifyingComboBox.h	Fri Jan 19 13:13:14 2007 +0000
@@ -0,0 +1,46 @@
+/* -*- 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 _NOTIFYING_COMBO_BOX_H_
+#define _NOTIFYING_COMBO_BOX_H_
+
+#include <QComboBox>
+
+/**
+ * Very trivial enhancement to QComboBox to make it emit signals when
+ * the mouse enters and leaves (for context help).
+ */
+
+class NotifyingComboBox : public QComboBox
+{
+    Q_OBJECT
+public:
+
+    NotifyingComboBox(QWidget *parent = 0) :
+        QComboBox(parent) { }
+
+    virtual ~NotifyingComboBox();
+
+signals:
+    void mouseEntered();
+    void mouseLeft();
+
+protected:
+    virtual void enterEvent(QEvent *);
+    virtual void leaveEvent(QEvent *);
+};
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/widgets/NotifyingPushButton.cpp	Fri Jan 19 13:13:14 2007 +0000
@@ -0,0 +1,35 @@
+/* -*- 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 "NotifyingPushButton.h"
+
+NotifyingPushButton::~NotifyingPushButton()
+{
+}
+
+void
+NotifyingPushButton::enterEvent(QEvent *e)
+{
+    QPushButton::enterEvent(e);
+    emit mouseEntered();
+}
+
+void
+NotifyingPushButton::leaveEvent(QEvent *e)
+{
+    QPushButton::enterEvent(e);
+    emit mouseLeft();
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/widgets/NotifyingPushButton.h	Fri Jan 19 13:13:14 2007 +0000
@@ -0,0 +1,46 @@
+/* -*- 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 _NOTIFYING_PUSH_BUTTON_H_
+#define _NOTIFYING_PUSH_BUTTON_H_
+
+#include <QPushButton>
+
+/**
+ * Very trivial enhancement to QPushButton to make it emit signals when
+ * the mouse enters and leaves (for context help).
+ */
+
+class NotifyingPushButton : public QPushButton
+{
+    Q_OBJECT
+public:
+
+    NotifyingPushButton(QWidget *parent = 0) :
+        QPushButton(parent) { }
+
+    virtual ~NotifyingPushButton();
+
+signals:
+    void mouseEntered();
+    void mouseLeft();
+
+protected:
+    virtual void enterEvent(QEvent *);
+    virtual void leaveEvent(QEvent *);
+};
+
+#endif
+
--- a/widgets/Panner.cpp	Fri Jan 12 21:52:56 2007 +0000
+++ b/widgets/Panner.cpp	Fri Jan 19 13:13:14 2007 +0000
@@ -115,6 +115,18 @@
 }
 
 void
+Panner::enterEvent(QEvent *)
+{
+    emit mouseEntered();
+}
+
+void
+Panner::leaveEvent(QEvent *)
+{
+    emit mouseLeft();
+}
+
+void
 Panner::paintEvent(QPaintEvent *e)
 {
     QPainter paint(this);
--- a/widgets/Panner.h	Fri Jan 12 21:52:56 2007 +0000
+++ b/widgets/Panner.h	Fri Jan 19 13:13:14 2007 +0000
@@ -33,13 +33,6 @@
 
     void getRectExtents(float &x0, float &y0, float &width, float &height);
 
-    virtual void mousePressEvent(QMouseEvent *e);
-    virtual void mouseDoubleClickEvent(QMouseEvent *e);
-    virtual void mouseMoveEvent(QMouseEvent *e);
-    virtual void mouseReleaseEvent(QMouseEvent *e);
-    virtual void wheelEvent(QWheelEvent *e);
-    virtual void paintEvent(QPaintEvent *e);
-
     virtual QSize sizeHint() const;
 
 signals:
@@ -65,6 +58,9 @@
      */
     void doubleClicked();
 
+    void mouseEntered();
+    void mouseLeft();
+
 public slots:
     /** 
      * Set the extents of the panned rectangle within the overall
@@ -102,6 +98,15 @@
     void resetToDefault();
 
 protected:
+    virtual void mousePressEvent(QMouseEvent *e);
+    virtual void mouseDoubleClickEvent(QMouseEvent *e);
+    virtual void mouseMoveEvent(QMouseEvent *e);
+    virtual void mouseReleaseEvent(QMouseEvent *e);
+    virtual void wheelEvent(QWheelEvent *e);
+    virtual void paintEvent(QPaintEvent *e);
+    virtual void enterEvent(QEvent *);
+    virtual void leaveEvent(QEvent *);
+
     void normalise();
     void emitAndUpdate();
 
--- a/widgets/PropertyBox.cpp	Fri Jan 12 21:52:56 2007 +0000
+++ b/widgets/PropertyBox.cpp	Fri Jan 19 13:13:14 2007 +0000
@@ -29,11 +29,12 @@
 #include "AudioDial.h"
 #include "LEDButton.h"
 
+#include "NotifyingCheckBox.h"
+#include "NotifyingComboBox.h"
+
 #include <QGridLayout>
 #include <QHBoxLayout>
 #include <QVBoxLayout>
-#include <QCheckBox>
-#include <QComboBox>
 #include <QPushButton>
 #include <QLabel>
 #include <QFrame>
@@ -46,7 +47,8 @@
 
 PropertyBox::PropertyBox(PropertyContainer *container) :
     m_container(container),
-    m_showButton(0)
+    m_showButton(0),
+    m_playButton(0)
 {
 #ifdef DEBUG_PROPERTY_BOX
     std::cerr << "PropertyBox[" << this << "(\"" <<
@@ -145,6 +147,10 @@
 	layout->addWidget(m_showButton);
 	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);
     }
     
@@ -154,14 +160,18 @@
 	layout->addWidget(playLabel);
 	layout->setAlignment(playLabel, Qt::AlignVCenter);
 
-	LEDButton *playButton = new LEDButton(Qt::darkGreen);
-        playButton->setState(!params->isPlayMuted());
-	layout->addWidget(playButton);
-	connect(playButton, SIGNAL(stateChanged(bool)),
+	m_playButton = new LEDButton(Qt::darkGreen);
+        m_playButton->setState(!params->isPlayMuted());
+	layout->addWidget(m_playButton);
+	connect(m_playButton, SIGNAL(stateChanged(bool)),
 		params, SLOT(setPlayAudible(bool)));
+        connect(m_playButton, SIGNAL(mouseEntered()),
+                this, SLOT(mouseEnteredWidget()));
+        connect(m_playButton, SIGNAL(mouseLeft()),
+                this, SLOT(mouseLeftWidget()));
 	connect(params, SIGNAL(playAudibleChanged(bool)),
-		playButton, SLOT(setState(bool)));
-	layout->setAlignment(playButton, Qt::AlignVCenter);
+		m_playButton, SLOT(setState(bool)));
+	layout->setAlignment(m_playButton, Qt::AlignVCenter);
 
 	layout->insertStretch(-1, 10);
 
@@ -196,6 +206,10 @@
 		params, SLOT(setPlayGain(float)));
 	connect(this, SIGNAL(changePlayGainDial(int)),
 		gainDial, SLOT(setValue(int)));
+        connect(gainDial, SIGNAL(mouseEntered()),
+                this, SLOT(mouseEnteredWidget()));
+        connect(gainDial, SIGNAL(mouseLeft()),
+                this, SLOT(mouseLeftWidget()));
 	layout->setAlignment(gainDial, Qt::AlignVCenter);
 
 	AudioDial *panDial = new AudioDial;
@@ -219,6 +233,10 @@
 		params, SLOT(setPlayPan(float)));
 	connect(this, SIGNAL(changePlayPanDial(int)),
 		panDial, SLOT(setValue(int)));
+        connect(panDial, SIGNAL(mouseEntered()),
+                this, SLOT(mouseEnteredWidget()));
+        connect(panDial, SIGNAL(mouseLeft()),
+                this, SLOT(mouseLeftWidget()));
 	layout->setAlignment(panDial, Qt::AlignVCenter);
 
     } else {
@@ -278,19 +296,23 @@
 
     case PropertyContainer::ToggleProperty:
     {
-	QCheckBox *cb;
+        NotifyingCheckBox *cb;
 
 	if (have) {
-	    cb = dynamic_cast<QCheckBox *>(m_propertyControllers[name]);
+	    cb = dynamic_cast<NotifyingCheckBox *>(m_propertyControllers[name]);
 	    assert(cb);
 	} else {
 #ifdef DEBUG_PROPERTY_BOX 
 	    std::cerr << "PropertyBox: creating new checkbox" << std::endl;
 #endif
-	    cb = new QCheckBox();
+	    cb = new NotifyingCheckBox();
 	    cb->setObjectName(name);
 	    connect(cb, SIGNAL(stateChanged(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);
@@ -330,6 +352,10 @@
             dial->setShowToolTip(true);
 	    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);
@@ -360,17 +386,17 @@
     case PropertyContainer::ValueProperty:
     case PropertyContainer::UnitsProperty:
     {
-	QComboBox *cb;
+	NotifyingComboBox *cb;
 
 	if (have) {
-	    cb = dynamic_cast<QComboBox *>(m_propertyControllers[name]);
+	    cb = dynamic_cast<NotifyingComboBox *>(m_propertyControllers[name]);
 	    assert(cb);
 	} else {
 #ifdef DEBUG_PROPERTY_BOX 
 	    std::cerr << "PropertyBox: creating new combobox" << std::endl;
 #endif
 
-	    cb = new QComboBox();
+	    cb = new NotifyingComboBox();
 	    cb->setObjectName(name);
             cb->setDuplicatesEnabled(false);
 
@@ -389,6 +415,10 @@
 
 	    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);
@@ -478,7 +508,7 @@
     PropertyContainer::PropertyType type = m_container->getPropertyType(name);
 
     if (type == PropertyContainer::UnitsProperty) {
-        QComboBox *cb = dynamic_cast<QComboBox *>(obj);
+        NotifyingComboBox *cb = dynamic_cast<NotifyingComboBox *>(obj);
         if (cb) {
             QString unit = cb->currentText();
             m_container->setPropertyWithCommand
@@ -487,14 +517,6 @@
     } else if (type != PropertyContainer::InvalidProperty) {
 	m_container->setPropertyWithCommand(name, value);
     }
-
-//    if (type == PropertyContainer::RangeProperty) {
-//	AudioDial *dial = dynamic_cast<AudioDial *>(m_propertyControllers[name]);
-//!!!	if (dial) {
-//	    dial->setToolTip(QString("%1: %2").arg(name).arg(value));
-//	    //!!! unfortunately this doesn't update an already-visible tooltip
-//	}
-//    }
 }
     
 void
@@ -582,7 +604,40 @@
 {
     if (m_showButton) m_showButton->setState(visible);
 }
+
+void
+PropertyBox::mouseEnteredWidget()
+{
+    QWidget *w = dynamic_cast<QWidget *>(sender());
+    if (!w) return;
     
+    if (!m_container) return;
+    QString cname = m_container->objectName();
+    if (cname == "") return;
+
+    QString wname = w->objectName();
+
+    if (w == m_showButton) {
+        emit contextHelpChanged(tr("Toggle Visibility of %1 layer").arg(cname));
+    } else if (w == m_playButton) {
+        emit contextHelpChanged(tr("Toggle Playback of %1 layer").arg(cname));
+    } else if (wname == "") {
+        return;
+    } else if (dynamic_cast<NotifyingCheckBox *>(w)) {
+        emit contextHelpChanged(tr("Toggle %1 property of %2 layer")
+                                .arg(wname).arg(cname));
+    } else {
+        emit contextHelpChanged(tr("Adjust %1 property of %2 layer")
+                                .arg(wname).arg(cname));
+    }
+}
+
+void
+PropertyBox::mouseLeftWidget()
+{
+    emit contextHelpChanged("");
+}
+
 
 #ifdef INCLUDE_MOCFILES
 #include "PropertyBox.moc.cpp"
--- a/widgets/PropertyBox.h	Fri Jan 12 21:52:56 2007 +0000
+++ b/widgets/PropertyBox.h	Fri Jan 19 13:13:14 2007 +0000
@@ -44,6 +44,7 @@
     void changePlayPan(float);
     void changePlayPanDial(int);
     void showLayer(bool);
+    void contextHelpChanged(const QString &);
 
 public slots:
     void propertyContainerPropertyChanged(PropertyContainer *);
@@ -64,6 +65,9 @@
 
     void editPlugin();
 
+    void mouseEnteredWidget();
+    void mouseLeftWidget();
+
 protected:
     void updatePropertyEditor(PropertyContainer::PropertyName);
 
@@ -74,6 +78,7 @@
     QFrame *m_viewPlayFrame;
     QVBoxLayout *m_mainBox;
     LEDButton *m_showButton;
+    LEDButton *m_playButton;
     std::map<QString, QLayout *> m_groupLayouts;
     std::map<QString, QWidget *> m_propertyControllers;
 };
--- a/widgets/PropertyStack.cpp	Fri Jan 12 21:52:56 2007 +0000
+++ b/widgets/PropertyStack.cpp	Fri Jan 19 13:13:14 2007 +0000
@@ -78,6 +78,8 @@
 	PropertyBox *box = new PropertyBox(container);
 
 	connect(box, SIGNAL(showLayer(bool)), this, SLOT(showLayer(bool)));
+        connect(box, SIGNAL(contextHelpChanged(const QString &)),
+                this, SIGNAL(contextHelpChanged(const QString &)));
 
         Layer *layer = dynamic_cast<Layer *>(container);
         if (layer) {
--- a/widgets/PropertyStack.h	Fri Jan 12 21:52:56 2007 +0000
+++ b/widgets/PropertyStack.h	Fri Jan 19 13:13:14 2007 +0000
@@ -38,6 +38,7 @@
 
 signals:
     void propertyContainerSelected(View *client, PropertyContainer *container);
+    void contextHelpChanged(const QString &);
 
 public slots:
     void propertyContainerAdded(PropertyContainer *);
--- a/widgets/Thumbwheel.cpp	Fri Jan 12 21:52:56 2007 +0000
+++ b/widgets/Thumbwheel.cpp	Fri Jan 19 13:13:14 2007 +0000
@@ -262,6 +262,18 @@
 }
 
 void
+Thumbwheel::enterEvent(QEvent *)
+{
+    emit mouseEntered();
+}
+
+void
+Thumbwheel::leaveEvent(QEvent *)
+{
+    emit mouseLeft();
+}
+
+void
 Thumbwheel::mousePressEvent(QMouseEvent *e)
 {
     if (e->button() == Qt::MidButton ||
--- a/widgets/Thumbwheel.h	Fri Jan 12 21:52:56 2007 +0000
+++ b/widgets/Thumbwheel.h	Fri Jan 19 13:13:14 2007 +0000
@@ -36,13 +36,6 @@
     bool getShowScale() const;
     int getValue() const;
 
-    virtual void mousePressEvent(QMouseEvent *e);
-    virtual void mouseDoubleClickEvent(QMouseEvent *e);
-    virtual void mouseMoveEvent(QMouseEvent *e);
-    virtual void mouseReleaseEvent(QMouseEvent *e);
-    virtual void wheelEvent(QWheelEvent *e);
-    virtual void paintEvent(QPaintEvent *e);
-
     void setRangeMapper(RangeMapper *mapper); // I take ownership, will delete
     const RangeMapper *getRangeMapper() const { return m_rangeMapper; }
     float getMappedValue() const;
@@ -54,6 +47,9 @@
 signals:
     void valueChanged(int);
 
+    void mouseEntered();
+    void mouseLeft();
+
 public slots:
     void setMinimumValue(int min);
     void setMaximumValue(int max);
@@ -68,7 +64,16 @@
 protected slots:
     void updateMappedValue(int value);
 
-private:
+protected:
+    virtual void mousePressEvent(QMouseEvent *e);
+    virtual void mouseDoubleClickEvent(QMouseEvent *e);
+    virtual void mouseMoveEvent(QMouseEvent *e);
+    virtual void mouseReleaseEvent(QMouseEvent *e);
+    virtual void wheelEvent(QWheelEvent *e);
+    virtual void paintEvent(QPaintEvent *e);
+    virtual void enterEvent(QEvent *);
+    virtual void leaveEvent(QEvent *);
+
     int m_min;
     int m_max;
     int m_default;
--- a/widgets/widgets.pro	Fri Jan 12 21:52:56 2007 +0000
+++ b/widgets/widgets.pro	Fri Jan 19 13:13:14 2007 +0000
@@ -20,6 +20,9 @@
            LayerTree.h \
            LEDButton.h \
            ListInputDialog.h \
+           NotifyingCheckBox.h \
+           NotifyingComboBox.h \
+           NotifyingPushButton.h \
            Panner.h \
            PluginParameterBox.h \
            PluginParameterDialog.h \
@@ -36,6 +39,9 @@
            LayerTree.cpp \
            LEDButton.cpp \
            ListInputDialog.cpp \
+           NotifyingCheckBox.cpp \
+           NotifyingComboBox.cpp \
+           NotifyingPushButton.cpp \
            Panner.cpp \
            PluginParameterBox.cpp \
            PluginParameterDialog.cpp \