diff options
| author | Harald Sitter <sitter@kde.org> | 2012-09-13 20:11:49 (GMT) |
|---|---|---|
| committer | Harald Sitter <sitter@kde.org> | 2012-09-13 20:11:49 (GMT) |
| commit | d1344b63ded3b53dff44794f8e17e67ca530de9e (patch) | |
| tree | 8d4a303f9211e772e1eae3b19acb6835b1435b2d | |
| parent | d6ff1b0cc3293a89b3f01a9b7606450e6404aaaf (diff) | |
implement runtime KSNI updates
previously kmix would throw away the KSNI and completely recreate it to
pick up mixer changes. incidentally enough doing that triggers repaints
and rescaling in plasma leading to utterly silly flickering. paritcularly
since pulseaudio stream changes (e.g. a track change in amarok) would
cause such an "update" you'd have a steady flow of flickering systray
the situation was resolved by introducing a MetaMixer in the DockWidget.
MetaMixer behaves like an abstraction wrapper around the actual Mixer. it
replicates the necessary interfaces/signals/slots and internally
controls the wiring to the actual mixer. that way the dockwidget can
connect() once to the metamixer signals while internally the mixer might
be constantly changing. then when an update is required the new update()
function is called, which calls reset() on the metamixer, which rewires
against the new master mixer and then automatically triggers UI updates
| -rw-r--r-- | apps/kmix.cpp | 21 | ||||
| -rw-r--r-- | gui/kmixdockwidget.cpp | 142 | ||||
| -rw-r--r-- | gui/kmixdockwidget.h | 35 |
3 files changed, 99 insertions, 99 deletions
diff --git a/apps/kmix.cpp b/apps/kmix.cpp index 6bc26b4..46707ee 100644 --- a/apps/kmix.cpp +++ b/apps/kmix.cpp @@ -313,20 +313,23 @@ void KMixWindow::recreateDockWidget() */ bool KMixWindow::updateDocking() { - // delete old dock widget - if (m_dockWidget) - { - // If this is called during a master control change, the dock widget is currently active, so we use deleteLater(). - m_dockWidget->deleteLater(); - m_dockWidget = 0L; - } + kDebug(); if ( m_showDockWidget == false || Mixer::mixers().count() == 0 ) { + if (m_dockWidget) { // Config update: we are not supposed to have one, but we have one. + m_dockWidget->deleteLater(); + m_dockWidget = 0; + } return false; } - m_dockWidget = new KMixDockWidget( this, m_volumeWidget ); // Could be optimized, by refreshing instead of recreating. - connect(m_dockWidget, SIGNAL(newMasterSelected()), SLOT(saveConfig()) ); + if (!m_dockWidget) { + m_dockWidget = new KMixDockWidget( this, m_volumeWidget ); // Could be optimized, by refreshing instead of recreating. + connect(m_dockWidget, SIGNAL(newMasterSelected()), SLOT(saveConfig()) ); + } else { + m_dockWidget->update(); + } + return true; } diff --git a/gui/kmixdockwidget.cpp b/gui/kmixdockwidget.cpp index d448a10..1d21e1f 100644 --- a/gui/kmixdockwidget.cpp +++ b/gui/kmixdockwidget.cpp @@ -25,24 +25,13 @@ #include <kaction.h> #include <klocale.h> -#include <kapplication.h> #include <kmenu.h> -#include <kurl.h> -#include <kglobalsettings.h> -#include <kdialog.h> -#include <kiconloader.h> #include <kdebug.h> #include <kwindowsystem.h> #include <kactioncollection.h> #include <ktoggleaction.h> -#include <qapplication.h> -#include <qcursor.h> #include <QDesktopWidget> -#include <QMouseEvent> - -#ifdef Q_WS_X11 -#include <fixx11h.h> -#endif +#include <QApplication> #include "gui/dialogselectmaster.h" #include "apps/kmix.h" @@ -51,9 +40,17 @@ #include "core/mixertoolbox.h" #include "gui/viewdockareapopup.h" +void MetaMixer::reset() +{ + disconnect(m_mixer, SIGNAL(controlChanged()), this, SIGNAL(controlChanged())); + m_mixer = Mixer::getGlobalMasterMixer(); + m_mixer->readSetFromHWforceUpdate(); + connect(m_mixer, SIGNAL(controlChanged()), this, SIGNAL(controlChanged())); + emit controlChanged(); // Triggers UI updates accordingly +} + KMixDockWidget::KMixDockWidget(KMixWindow* parent, bool volumePopup) : KStatusNotifierItem(parent) - // : KStatusNotifierItem(0) , _oldToolTipValue(-1) , _oldPixmapType('-') , _volumePopup(volumePopup) @@ -64,23 +61,15 @@ KMixDockWidget::KMixDockWidget(KMixWindow* parent, bool volumePopup) setTitle(i18n( "Volume Control")); setCategory(Hardware); setStatus(Active); - m_mixer = Mixer::getGlobalMasterMixer(); // ugly, but we'll live with that for now + + m_metaMixer.reset(); createMasterVolWidget(); createActions(); + connect(this, SIGNAL(scrollRequested(int,Qt::Orientation)), this, SLOT(trayWheelEvent(int,Qt::Orientation))); connect(this, SIGNAL(secondaryActivateRequested(QPoint)), this, SLOT(dockMute())); - bool NO_MENU_ANYMORE = true; - - if ( NO_MENU_ANYMORE ) - { - connect(contextMenu(), SIGNAL(aboutToShow()), this, SLOT(contextMenuAboutToShow())); - } - else - { - connect(this, SIGNAL(activateRequested(bool,QPoint)), this, SLOT(activateMenuOrWindow(bool,QPoint))); - connect(contextMenu(), SIGNAL(aboutToShow()), this, SLOT(contextMenuAboutToShow())); - } + connect(contextMenu(), SIGNAL(aboutToShow()), this, SLOT(contextMenuAboutToShow())); #ifdef __GNUC__ #warning minimizeRestore usage is currently slightly broken in KMIx. This should be fixed before doing a release. @@ -98,56 +87,50 @@ KMixDockWidget::KMixDockWidget(KMixWindow* parent, bool volumePopup) _volWA->setDefaultWidget(_referenceWidget2); _referenceWidget->addAction(_volWA); - connect( m_mixer, SIGNAL(controlChanged()), _referenceWidget2, SLOT(refreshVolumeLevels()) ); + connect( &m_metaMixer, SIGNAL(controlChanged()), _referenceWidget2, SLOT(refreshVolumeLevels()) ); //setAssociatedWidget(_referenceWidget); //setAssociatedWidget(_referenceWidget); // If you use the popup, associate that instead of the MainWindow - - //setContextMenu(_referenceWidget2); - } - else { + + //setContextMenu(_referenceWidget2); + } else { _volWA = 0; _referenceWidget = 0; } } - KMixDockWidget::~KMixDockWidget() { // Note: deleting _volWA also deletes its associated ViewDockAreaPopup (_referenceWidget) and prevents the // action to be left with a dangling pointer. // cesken: I adapted the patch from https://bugs.kde.org/show_bug.cgi?id=220621#c27 to branch /branches/work/kmix delete _volWA; - } void KMixDockWidget::createActions() { - QMenu *menu = contextMenu(); - - shared_ptr<MixDevice> md = Mixer::getGlobalMasterMD(); - if ( md.get() != 0 && md->playbackVolume().hasSwitch() ) - { - // Put "Mute" selector in context menu - KToggleAction *action = actionCollection()->add<KToggleAction>( "dock_mute" ); - action->setText( i18n( "M&ute" ) ); - connect(action, SIGNAL(triggered(bool)), SLOT(dockMute())); + QMenu *menu = contextMenu(); + + shared_ptr<MixDevice> md = Mixer::getGlobalMasterMD(); + if ( md.get() != 0 && md->playbackVolume().hasSwitch() ) { + // Put "Mute" selector in context menu + KToggleAction *action = actionCollection()->add<KToggleAction>( "dock_mute" ); + action->setText( i18n( "M&ute" ) ); + connect(action, SIGNAL(triggered(bool)), SLOT(dockMute())); + menu->addAction( action ); + } + + // Put "Select Master Channel" dialog in context menu + QAction *action = actionCollection()->addAction( "select_master" ); + action->setText( i18n("Select Master Channel...") ); + action->setEnabled(m_metaMixer.hasMixer()); + connect(action, SIGNAL(triggered(bool)), SLOT(selectMaster())); menu->addAction( action ); -} - // Put "Select Master Channel" dialog in context menu - if ( m_mixer != 0 ) { - QAction *action = actionCollection()->addAction( "select_master" ); - action->setText( i18n("Select Master Channel...") ); - connect(action, SIGNAL(triggered(bool)), SLOT(selectMaster())); - menu->addAction( action ); - } - //Context menu entry to access phonon settings - menu->addAction(_kmixMainWindow->actionCollection()->action("launch_kdesoundsetup")); + //Context menu entry to access phonon settings + menu->addAction(_kmixMainWindow->actionCollection()->action("launch_kdesoundsetup")); } - -void -KMixDockWidget::createMasterVolWidget() +void KMixDockWidget::createMasterVolWidget() { // Reset flags, so that the dock icon will be reconstructed _oldToolTipValue = -1; @@ -162,34 +145,22 @@ KMixDockWidget::createMasterVolWidget() } // create devices - m_mixer->readSetFromHWforceUpdate(); // after changing the master device, make sure to re-read (otherwise no "changed()" signals might get sent by the Mixer - /* With the recently introduced QSocketNotifier stuff, we can't rely on regular timer updates - any longer. Also the readSetFromHWforceUpdate() won't be enough. As a workaround, we trigger - all "repaints" manually here. - The call to m_mixer->readSetFromHWforceUpdate() is most likely superfluous, even if we don't use QSocketNotifier (e.g. in backends OSS, Solaris, ...) - */ setVolumeTip(); updatePixmap(); - /* We are setting up 3 connections: - * Refreshig the _dockAreaPopup (not anymore necessary, because ViewBase already does it) - * Refreshing the Tooltip - * Refreshing the Icon - */ - // connect( m_mixer, SIGNAL(controlChanged()), _dockAreaPopup, SLOT(refreshVolumeLevels()) ); - connect( m_mixer, SIGNAL(controlChanged()), this, SLOT(setVolumeTip()) ); - connect( m_mixer, SIGNAL(controlChanged()), this, SLOT(updatePixmap()) ); + + connect( &m_metaMixer, SIGNAL(controlChanged()), this, SLOT(setVolumeTip()) ); + connect( &m_metaMixer, SIGNAL(controlChanged()), this, SLOT(updatePixmap()) ); } void KMixDockWidget::selectMaster() { - DialogSelectMaster* dsm = new DialogSelectMaster(m_mixer); + DialogSelectMaster* dsm = new DialogSelectMaster(m_metaMixer.mixer()); dsm->setAttribute(Qt::WA_DeleteOnClose, true); connect ( dsm, SIGNAL(newMasterSelected(QString&,QString&)), SLOT(handleNewMaster(QString&,QString&)) ); connect ( dsm, SIGNAL(newMasterSelected(QString&,QString&)), SIGNAL(newMasterSelected()) ); dsm->show(); } - void KMixDockWidget::handleNewMaster(QString& /*mixerID*/, QString& /*control_id*/) { /* When a new master is selected, we will need to destroy *this instance of KMixDockWidget. @@ -201,11 +172,16 @@ void KMixDockWidget::handleNewMaster(QString& /*mixerID*/, QString& /*control_id _kmixMainWindow->updateDocking(); } +void KMixDockWidget::update() +{ + m_metaMixer.reset(); + actionCollection()->action(QLatin1String("select_master"))->setEnabled(m_metaMixer.hasMixer()); +} void KMixDockWidget::setVolumeTip() { - shared_ptr<MixDevice> md = Mixer::getGlobalMasterMD(); + shared_ptr<MixDevice> md = Mixer::getGlobalMasterMD(); QString tip = ""; int newToolTipValue = 0; @@ -283,14 +259,6 @@ KMixDockWidget::updatePixmap() _oldPixmapType = newPixmapType; } - - -void KMixDockWidget::activateMenuOrWindow(bool val, const QPoint &pos) -{ - kDebug() << "activateMenuOrWindow: " << val << "," << pos; -} - - void KMixDockWidget::activate(const QPoint &pos) { kDebug() << "Activate at " << pos; @@ -304,12 +272,12 @@ void KMixDockWidget::activate(const QPoint &pos) kDebug() << "Use default KStatusNotifierItem behavior"; setAssociatedWidget(_kmixMainWindow); // KStatusNotifierItem::activate(pos); - KStatusNotifierItem::activate(); + KStatusNotifierItem::activate(); return; } KMenu* dockAreaPopup =_referenceWidget; // TODO Refactor to use _referenceWidget directly - kDebug() << "Skip default KStatusNotifierItkdebem behavior"; + kDebug() << "Skip default KStatusNotifierItem behavior"; if ( dockAreaPopup->isVisible() ) { dockAreaPopup->hide(); kDebug() << "dap is visible => hide and return"; @@ -407,12 +375,12 @@ KMixDockWidget::trayWheelEvent(int delta,Qt::Orientation wheelOrientation) void KMixDockWidget::dockMute() { - shared_ptr<MixDevice> md = Mixer::getGlobalMasterMD(); - if ( md ) - { - md->toggleMute(); - md->mixer()->commitVolumeChange( md ); - } + shared_ptr<MixDevice> md = Mixer::getGlobalMasterMD(); + if ( md ) + { + md->toggleMute(); + md->mixer()->commitVolumeChange( md ); + } } void diff --git a/gui/kmixdockwidget.h b/gui/kmixdockwidget.h index 7160961..2767298 100644 --- a/gui/kmixdockwidget.h +++ b/gui/kmixdockwidget.h @@ -24,7 +24,6 @@ #define KMIXDOCKWIDGET_H class QString; -#include <QWidget> class QWidgetAction; #include <kstatusnotifieritem.h> @@ -33,6 +32,35 @@ class Mixer; class ViewDockAreaPopup; class Volume; +/** + * @brief The MetaMixer class provides a solid wrapper around a possible changing mixer. + * + * The primay use of this class is to provide one instance to connect slots to + * while the underlying object that triggers the signals can be changing. + * Additionally it nicely hides the rewiring logic that is going on in the back. + */ +class MetaMixer : public QObject +{ + Q_OBJECT +public: + MetaMixer() : m_mixer(0) {} + + /** + * @brief rewires against current master mixer + * @note this also triggers all signals to ensure UI updates are done accordingly + */ + void reset(); + + Mixer *mixer() const { return m_mixer; } + bool hasMixer() const { return m_mixer; } + +signals: + void controlChanged(); + +private: + Mixer *m_mixer; +}; + class KMixDockWidget : public KStatusNotifierItem { Q_OBJECT @@ -46,6 +74,8 @@ class KMixDockWidget : public KStatusNotifierItem void setErrorPixmap(); void ignoreNextEvent(); + void update(); + signals: void newMasterSelected(); @@ -69,7 +99,7 @@ class KMixDockWidget : public KStatusNotifierItem char _oldPixmapType; bool _volumePopup; KMixWindow* _kmixMainWindow; - Mixer *m_mixer; + MetaMixer m_metaMixer; bool _contextMenuWasOpen; @@ -81,7 +111,6 @@ class KMixDockWidget : public KStatusNotifierItem void selectMaster(); void handleNewMaster(QString& soundcard_id, QString& channel_id); void contextMenuAboutToShow(); - void activateMenuOrWindow(bool, const QPoint &); }; #endif |
